about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_abi/src/lib.rs3
-rw-r--r--compiler/rustc_ast/src/ast.rs178
-rw-r--r--compiler/rustc_ast/src/ast_traits.rs2
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs8
-rw-r--r--compiler/rustc_ast/src/expand/autodiff_attrs.rs13
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs86
-rw-r--r--compiler/rustc_ast/src/token.rs92
-rw-r--r--compiler/rustc_ast/src/tokenstream.rs62
-rw-r--r--compiler/rustc_ast/src/util/parser.rs24
-rw-r--r--compiler/rustc_ast/src/visit.rs87
-rw-r--r--compiler/rustc_ast_lowering/src/delegation.rs8
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs6
-rw-r--r--compiler/rustc_ast_lowering/src/format.rs28
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs350
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs382
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs33
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs15
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/item.rs83
-rw-r--r--compiler/rustc_borrowck/messages.ftl7
-rw-r--r--compiler/rustc_borrowck/src/consumers.rs7
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs15
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs4
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/var_name.rs2
-rw-r--r--compiler/rustc_borrowck/src/lib.rs53
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types.rs183
-rw-r--r--compiler/rustc_borrowck/src/session_diagnostics.rs11
-rw-r--r--compiler/rustc_builtin_macros/messages.ftl1
-rw-r--r--compiler/rustc_builtin_macros/src/alloc_error_handler.rs11
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/assert/context.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/autodiff.rs367
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/clone.rs4
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs6
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs17
-rw-r--r--compiler/rustc_builtin_macros/src/edition_panic.rs10
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs8
-rw-r--r--compiler/rustc_builtin_macros/src/global_allocator.rs20
-rw-r--r--compiler/rustc_builtin_macros/src/proc_macro_harness.rs40
-rw-r--r--compiler/rustc_builtin_macros/src/standard_library_imports.rs4
-rw-r--r--compiler/rustc_builtin_macros/src/test.rs62
-rw-r--r--compiler/rustc_builtin_macros/src/test_harness.rs46
-rw-r--r--compiler/rustc_codegen_cranelift/.cirrus.yml41
-rw-r--r--compiler/rustc_codegen_cranelift/.github/actions/github-release/README.md18
-rw-r--r--compiler/rustc_codegen_cranelift/.github/actions/github-release/action.yml13
-rw-r--r--compiler/rustc_codegen_cranelift/.github/actions/github-release/main.js162
-rw-r--r--compiler/rustc_codegen_cranelift/.github/actions/github-release/package-lock.json571
-rw-r--r--compiler/rustc_codegen_cranelift/.github/actions/github-release/package.json11
-rw-r--r--compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml12
-rw-r--r--compiler/rustc_codegen_cranelift/.github/workflows/main.yml43
-rw-r--r--compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml6
-rw-r--r--compiler/rustc_codegen_cranelift/.vscode/settings.json33
-rw-r--r--compiler/rustc_codegen_cranelift/.zed/settings.json25
-rw-r--r--compiler/rustc_codegen_cranelift/Cargo.lock87
-rw-r--r--compiler/rustc_codegen_cranelift/Cargo.toml24
-rw-r--r--compiler/rustc_codegen_cranelift/Readme.md2
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/prepare.rs7
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/tests.rs80
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/utils.rs6
-rw-r--r--compiler/rustc_codegen_cranelift/config.txt2
-rw-r--r--compiler/rustc_codegen_cranelift/docs/usage.md11
-rw-r--r--compiler/rustc_codegen_cranelift/example/gen_block_iterate.rs16
-rw-r--r--compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch (renamed from compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch)12
-rw-r--r--compiler/rustc_codegen_cranelift/patches/0028-coretests-Disable-long-running-tests.patch48
-rw-r--r--compiler/rustc_codegen_cranelift/patches/0028-sysroot_tests-Disable-long-running-tests.patch105
-rw-r--r--compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch26
-rw-r--r--compiler/rustc_codegen_cranelift/patches/coretests-lock.toml35
-rw-r--r--compiler/rustc_codegen_cranelift/rust-toolchain2
-rw-r--r--compiler/rustc_codegen_cranelift/rustfmt.toml4
-rw-r--r--compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs14
-rwxr-xr-xcompiler/rustc_codegen_cranelift/scripts/filter_profile.rs2
-rwxr-xr-xcompiler/rustc_codegen_cranelift/scripts/rustup.sh2
-rw-r--r--compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh23
-rwxr-xr-xcompiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh58
-rw-r--r--compiler/rustc_codegen_cranelift/src/abi/mod.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs6
-rw-r--r--compiler/rustc_codegen_cranelift/src/common.rs18
-rw-r--r--compiler/rustc_codegen_cranelift/src/config.rs34
-rw-r--r--compiler/rustc_codegen_cranelift/src/constant.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/debuginfo/types.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/driver/aot.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/driver/jit.rs225
-rw-r--r--compiler/rustc_codegen_cranelift/src/inline_asm.rs48
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs8
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs123
-rw-r--r--compiler/rustc_codegen_cranelift/src/lib.rs26
-rw-r--r--compiler/rustc_codegen_cranelift/src/num.rs8
-rw-r--r--compiler/rustc_codegen_cranelift/src/value_and_place.rs6
-rw-r--r--compiler/rustc_codegen_gcc/src/consts.rs2
-rw-r--r--compiler/rustc_codegen_llvm/messages.ftl2
-rw-r--r--compiler/rustc_codegen_llvm/src/abi.rs17
-rw-r--r--compiler/rustc_codegen_llvm/src/attributes.rs50
-rw-r--r--compiler/rustc_codegen_llvm/src/back/lto.rs12
-rw-r--r--compiler/rustc_codegen_llvm/src/back/write.rs6
-rw-r--r--compiler/rustc_codegen_llvm/src/builder.rs30
-rw-r--r--compiler/rustc_codegen_llvm/src/builder/autodiff.rs199
-rw-r--r--compiler/rustc_codegen_llvm/src/consts.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/context.rs62
-rw-r--r--compiler/rustc_codegen_llvm/src/errors.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs22
-rw-r--r--compiler/rustc_codegen_llvm/src/mono_item.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/va_arg.rs2
-rw-r--r--compiler/rustc_codegen_ssa/messages.ftl6
-rw-r--r--compiler/rustc_codegen_ssa/src/back/apple.rs131
-rw-r--r--compiler/rustc_codegen_ssa/src/back/apple/tests.rs12
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs25
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs20
-rw-r--r--compiler/rustc_codegen_ssa/src/back/metadata.rs10
-rw-r--r--compiler/rustc_codegen_ssa/src/back/mod.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/back/rpath.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/back/rpath/tests.rs6
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs6
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs36
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs9
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs6
-rw-r--r--compiler/rustc_const_eval/src/check_consts/check.rs2
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs2
-rw-r--r--compiler/rustc_const_eval/src/const_eval/valtrees.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs43
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs2
-rw-r--r--compiler/rustc_data_structures/src/fx.rs2
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs48
-rw-r--r--compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs4
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs21
-rw-r--r--compiler/rustc_errors/src/json.rs2
-rw-r--r--compiler/rustc_errors/src/lib.rs39
-rw-r--r--compiler/rustc_expand/src/base.rs7
-rw-r--r--compiler/rustc_expand/src/build.rs17
-rw-r--r--compiler/rustc_expand/src/expand.rs13
-rw-r--r--compiler/rustc_expand/src/mbe/transcribe.rs35
-rw-r--r--compiler/rustc_expand/src/placeholders.rs7
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs6
-rw-r--r--compiler/rustc_feature/src/removed.rs3
-rw-r--r--compiler/rustc_feature/src/unstable.rs5
-rw-r--r--compiler/rustc_hir/src/def.rs2
-rw-r--r--compiler/rustc_hir/src/definitions.rs15
-rw-r--r--compiler/rustc_hir/src/hir.rs10
-rw-r--r--compiler/rustc_hir/src/hir_id.rs22
-rw-r--r--compiler/rustc_hir/src/lang_items.rs6
-rw-r--r--compiler/rustc_hir/src/lib.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/check/always_applicable.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs12
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs47
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsicck.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs13
-rw-r--r--compiler/rustc_hir_analysis/src/check/region.rs298
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/mod.rs5
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/collect/item_bounds.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs63
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs59
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs331
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs245
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs12
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs44
-rw-r--r--compiler/rustc_hir_analysis/src/hir_wf_check.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/mod.rs2
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs29
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs68
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/expr_use_visitor.rs156
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs65
-rw-r--r--compiler/rustc_hir_typeck/src/intrinsicck.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs11
-rw-r--r--compiler/rustc_hir_typeck/src/op.rs343
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs11
-rw-r--r--compiler/rustc_hir_typeck/src/upvar.rs56
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs94
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs9
-rw-r--r--compiler/rustc_infer/src/infer/opaque_types/mod.rs42
-rw-r--r--compiler/rustc_interface/messages.ftl6
-rw-r--r--compiler/rustc_interface/src/errors.rs14
-rw-r--r--compiler/rustc_interface/src/interface.rs8
-rw-r--r--compiler/rustc_interface/src/passes.rs44
-rw-r--r--compiler/rustc_interface/src/tests.rs2
-rw-r--r--compiler/rustc_lint/messages.ftl22
-rw-r--r--compiler/rustc_lint/src/builtin.rs8
-rw-r--r--compiler/rustc_lint/src/context.rs15
-rw-r--r--compiler/rustc_lint/src/impl_trait_overcaptures.rs2
-rw-r--r--compiler/rustc_lint/src/levels.rs124
-rw-r--r--compiler/rustc_lint/src/lib.rs2
-rw-r--r--compiler/rustc_lint/src/lints.rs26
-rw-r--r--compiler/rustc_lint/src/non_ascii_idents.rs9
-rw-r--r--compiler/rustc_lint/src/nonstandard_style.rs22
-rw-r--r--compiler/rustc_lint/src/ptr_nulls.rs128
-rw-r--r--compiler/rustc_lint/src/reference_casting.rs44
-rw-r--r--compiler/rustc_lint/src/types.rs53
-rw-r--r--compiler/rustc_lint/src/utils.rs55
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs57
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp4
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp71
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp91
-rw-r--r--compiler/rustc_metadata/messages.ftl4
-rw-r--r--compiler/rustc_metadata/src/creader.rs60
-rw-r--r--compiler/rustc_metadata/src/errors.rs9
-rw-r--r--compiler/rustc_metadata/src/fs.rs26
-rw-r--r--compiler/rustc_metadata/src/locator.rs67
-rw-r--r--compiler/rustc_metadata/src/native_libs.rs6
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs10
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs103
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs14
-rw-r--r--compiler/rustc_middle/src/dep_graph/dep_node.rs1
-rw-r--r--compiler/rustc_middle/src/dep_graph/mod.rs1
-rw-r--r--compiler/rustc_middle/src/hir/map.rs54
-rw-r--r--compiler/rustc_middle/src/hir/mod.rs7
-rw-r--r--compiler/rustc_middle/src/hir/place.rs10
-rw-r--r--compiler/rustc_middle/src/lint.rs68
-rw-r--r--compiler/rustc_middle/src/middle/codegen_fn_attrs.rs2
-rw-r--r--compiler/rustc_middle/src/middle/region.rs10
-rw-r--r--compiler/rustc_middle/src/middle/stability.rs2
-rw-r--r--compiler/rustc_middle/src/mir/mono.rs1
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs148
-rw-r--r--compiler/rustc_middle/src/query/keys.rs4
-rw-r--r--compiler/rustc_middle/src/query/mod.rs22
-rw-r--r--compiler/rustc_middle/src/query/on_disk_cache.rs3
-rw-r--r--compiler/rustc_middle/src/thir.rs4
-rw-r--r--compiler/rustc_middle/src/traits/select.rs8
-rw-r--r--compiler/rustc_middle/src/ty/adt.rs2
-rw-r--r--compiler/rustc_middle/src/ty/closure.rs8
-rw-r--r--compiler/rustc_middle/src/ty/consts/valtree.rs2
-rw-r--r--compiler/rustc_middle/src/ty/context.rs54
-rw-r--r--compiler/rustc_middle/src/ty/erase_regions.rs24
-rw-r--r--compiler/rustc_middle/src/ty/instance.rs4
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs49
-rw-r--r--compiler/rustc_middle/src/ty/opaque_types.rs36
-rw-r--r--compiler/rustc_middle/src/ty/parameterized.rs4
-rw-r--r--compiler/rustc_middle/src/ty/print/mod.rs7
-rw-r--r--compiler/rustc_middle/src/ty/rvalue_scopes.rs2
-rw-r--r--compiler/rustc_middle/src/ty/significant_drop_order.rs2
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs8
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs47
-rw-r--r--compiler/rustc_middle/src/ty/trait_def.rs17
-rw-r--r--compiler/rustc_middle/src/ty/typeck_results.rs9
-rw-r--r--compiler/rustc_middle/src/ty/util.rs113
-rw-r--r--compiler/rustc_middle/src/util/bug.rs2
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_place.rs4
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs2
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/stmt.rs10
-rw-r--r--compiler/rustc_mir_build/src/builder/mod.rs14
-rw-r--r--compiler/rustc_mir_build/src/builder/scope.rs2
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs10
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs19
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs4
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs31
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs38
-rw-r--r--compiler/rustc_mir_transform/src/check_const_item_mutation.rs10
-rw-r--r--compiler/rustc_mir_transform/src/coroutine/by_move_body.rs2
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mappings.rs2
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans.rs107
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs18
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs202
-rw-r--r--compiler/rustc_mir_transform/src/instsimplify.rs6
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs3
-rw-r--r--compiler/rustc_mir_transform/src/ssa.rs38
-rw-r--r--compiler/rustc_monomorphize/messages.ftl2
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs1
-rw-r--r--compiler/rustc_monomorphize/src/errors.rs1
-rw-r--r--compiler/rustc_next_trait_solver/src/delegate.rs16
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs6
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs3
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs13
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/mod.rs1
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs40
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/search_graph.rs1
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/trait_goals.rs55
-rw-r--r--compiler/rustc_parse/messages.ftl4
-rw-r--r--compiler/rustc_parse/src/errors.rs21
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs309
-rw-r--r--compiler/rustc_parse/src/parser/item.rs315
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs97
-rw-r--r--compiler/rustc_parse/src/parser/nonterminal.rs16
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs11
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs15
-rw-r--r--compiler/rustc_parse/src/parser/tests.rs2
-rw-r--r--compiler/rustc_parse/src/parser/token_type.rs4
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs6
-rw-r--r--compiler/rustc_passes/messages.ftl4
-rw-r--r--compiler/rustc_passes/src/check_attr.rs37
-rw-r--r--compiler/rustc_passes/src/dead.rs22
-rw-r--r--compiler/rustc_passes/src/diagnostic_items.rs4
-rw-r--r--compiler/rustc_passes/src/entry.rs2
-rw-r--r--compiler/rustc_passes/src/errors.rs5
-rw-r--r--compiler/rustc_passes/src/lang_items.rs14
-rw-r--r--compiler/rustc_passes/src/stability.rs6
-rw-r--r--compiler/rustc_pattern_analysis/src/lints.rs11
-rw-r--r--compiler/rustc_pattern_analysis/src/rustc.rs5
-rw-r--r--compiler/rustc_query_impl/src/plumbing.rs11
-rw-r--r--compiler/rustc_query_system/src/dep_graph/graph.rs17
-rw-r--r--compiler/rustc_query_system/src/dep_graph/mod.rs3
-rw-r--r--compiler/rustc_resolve/Cargo.toml1
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs136
-rw-r--r--compiler/rustc_resolve/src/check_unused.rs4
-rw-r--r--compiler/rustc_resolve/src/def_collector.rs38
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs26
-rw-r--r--compiler/rustc_resolve/src/effective_visibilities.rs4
-rw-r--r--compiler/rustc_resolve/src/imports.rs2
-rw-r--r--compiler/rustc_resolve/src/late.rs74
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs52
-rw-r--r--compiler/rustc_resolve/src/lib.rs2
-rw-r--r--compiler/rustc_resolve/src/macros.rs15
-rw-r--r--compiler/rustc_resolve/src/rustdoc.rs8
-rw-r--r--compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs1
-rw-r--r--compiler/rustc_session/messages.ftl8
-rw-r--r--compiler/rustc_session/src/config.rs21
-rw-r--r--compiler/rustc_session/src/errors.rs14
-rw-r--r--compiler/rustc_session/src/options.rs13
-rw-r--r--compiler/rustc_session/src/session.rs50
-rw-r--r--compiler/rustc_session/src/utils.rs11
-rw-r--r--compiler/rustc_smir/Cargo.toml2
-rw-r--r--compiler/rustc_smir/src/lib.rs2
-rw-r--r--compiler/rustc_smir/src/rustc_internal/internal.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_internal/mod.rs9
-rw-r--r--compiler/rustc_smir/src/rustc_internal/pretty.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_smir/alloc.rs39
-rw-r--r--compiler/rustc_smir/src/rustc_smir/builder.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_smir/context.rs16
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/abi.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/error.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/mir.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/mod.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/ty.rs61
-rw-r--r--compiler/rustc_smir/src/rustc_smir/mod.rs1
-rw-r--r--compiler/rustc_smir/src/stable_mir/abi.rs (renamed from compiler/stable_mir/src/abi.rs)13
-rw-r--r--compiler/rustc_smir/src/stable_mir/compiler_interface.rs (renamed from compiler/stable_mir/src/compiler_interface.rs)25
-rw-r--r--compiler/rustc_smir/src/stable_mir/crate_def.rs (renamed from compiler/stable_mir/src/crate_def.rs)19
-rw-r--r--compiler/rustc_smir/src/stable_mir/error.rs (renamed from compiler/stable_mir/src/error.rs)0
-rw-r--r--compiler/rustc_smir/src/stable_mir/mir.rs (renamed from compiler/stable_mir/src/mir.rs)0
-rw-r--r--compiler/rustc_smir/src/stable_mir/mir/alloc.rs (renamed from compiler/stable_mir/src/mir/alloc.rs)9
-rw-r--r--compiler/rustc_smir/src/stable_mir/mir/body.rs (renamed from compiler/stable_mir/src/mir/body.rs)16
-rw-r--r--compiler/rustc_smir/src/stable_mir/mir/mono.rs (renamed from compiler/stable_mir/src/mir/mono.rs)43
-rw-r--r--compiler/rustc_smir/src/stable_mir/mir/pretty.rs (renamed from compiler/stable_mir/src/mir/pretty.rs)21
-rw-r--r--compiler/rustc_smir/src/stable_mir/mir/visit.rs (renamed from compiler/stable_mir/src/mir/visit.rs)10
-rw-r--r--compiler/rustc_smir/src/stable_mir/mod.rs239
-rw-r--r--compiler/rustc_smir/src/stable_mir/target.rs (renamed from compiler/stable_mir/src/target.rs)3
-rw-r--r--compiler/rustc_smir/src/stable_mir/ty.rs (renamed from compiler/stable_mir/src/ty.rs)82
-rw-r--r--compiler/rustc_smir/src/stable_mir/visitor.rs (renamed from compiler/stable_mir/src/visitor.rs)14
-rw-r--r--compiler/rustc_span/src/hygiene.rs281
-rw-r--r--compiler/rustc_span/src/lib.rs2
-rw-r--r--compiler/rustc_span/src/symbol.rs23
-rw-r--r--compiler/rustc_symbol_mangling/src/legacy.rs5
-rw-r--r--compiler/rustc_symbol_mangling/src/v0.rs1
-rw-r--r--compiler/rustc_target/src/asm/aarch64.rs2
-rw-r--r--compiler/rustc_target/src/asm/arm.rs2
-rw-r--r--compiler/rustc_target/src/callconv/mod.rs2
-rw-r--r--compiler/rustc_target/src/callconv/x86.rs2
-rw-r--r--compiler/rustc_target/src/lib.rs1
-rw-r--r--compiler/rustc_target/src/spec/base/apple/mod.rs111
-rw-r--r--compiler/rustc_target/src/spec/base/apple/tests.rs9
-rw-r--r--compiler/rustc_target/src/spec/base/mod.rs2
-rw-r--r--compiler/rustc_target/src/spec/json.rs4
-rw-r--r--compiler/rustc_target/src/spec/mod.rs19
-rw-r--r--compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/armv7_sony_vita_newlibeabihf.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs1
-rw-r--r--compiler/rustc_target/src/spec/targets/mips_unknown_linux_uclibc.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_uclibc.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv32_wrs_vxworks.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64_wrs_vxworks.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64gc_unknown_hermit.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64gc_unknown_netbsd.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64gc_unknown_none_elf.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64gc_unknown_openbsd.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/x86_64_unknown_l4re_uclibc.rs2
-rw-r--r--compiler/rustc_target/src/target_features.rs71
-rw-r--r--compiler/rustc_trait_selection/messages.ftl7
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs6
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs3
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs39
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs30
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs10
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs13
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs1
-rw-r--r--compiler/rustc_trait_selection/src/opaque_types.rs182
-rw-r--r--compiler/rustc_trait_selection/src/solve/delegate.rs28
-rw-r--r--compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs35
-rw-r--r--compiler/rustc_trait_selection/src/traits/effects.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs56
-rw-r--r--compiler/rustc_trait_selection/src/traits/normalize.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs64
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs17
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs37
-rw-r--r--compiler/rustc_ty_utils/src/instance.rs1
-rw-r--r--compiler/rustc_type_ir/src/fold.rs53
-rw-r--r--compiler/rustc_type_ir/src/infer_ctxt.rs12
-rw-r--r--compiler/rustc_type_ir/src/inherent.rs57
-rw-r--r--compiler/rustc_type_ir/src/interner.rs4
-rw-r--r--compiler/rustc_type_ir/src/relate/combine.rs1
-rw-r--r--compiler/rustc_type_ir/src/ty_kind.rs62
-rw-r--r--compiler/stable_mir/Cargo.toml3
-rw-r--r--compiler/stable_mir/src/lib.rs231
420 files changed, 7085 insertions, 6480 deletions
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index 3f28dc7a1ef..843d5ca61dd 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -1462,7 +1462,8 @@ impl BackendRepr {
         !self.is_unsized()
     }
 
-    /// Returns `true` if this is a single signed integer scalar
+    /// Returns `true` if this is a single signed integer scalar.
+    /// Sanity check: panics if this is not a scalar type (see PR #70189).
     #[inline]
     pub fn is_signed(&self) -> bool {
         match self {
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 064f05ef1f3..97e6879c33e 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -981,6 +981,75 @@ impl BinOpKind {
 
 pub type BinOp = Spanned<BinOpKind>;
 
+// Sometimes `BinOpKind` and `AssignOpKind` need the same treatment. The
+// operations covered by `AssignOpKind` are a subset of those covered by
+// `BinOpKind`, so it makes sense to convert `AssignOpKind` to `BinOpKind`.
+impl From<AssignOpKind> for BinOpKind {
+    fn from(op: AssignOpKind) -> BinOpKind {
+        match op {
+            AssignOpKind::AddAssign => BinOpKind::Add,
+            AssignOpKind::SubAssign => BinOpKind::Sub,
+            AssignOpKind::MulAssign => BinOpKind::Mul,
+            AssignOpKind::DivAssign => BinOpKind::Div,
+            AssignOpKind::RemAssign => BinOpKind::Rem,
+            AssignOpKind::BitXorAssign => BinOpKind::BitXor,
+            AssignOpKind::BitAndAssign => BinOpKind::BitAnd,
+            AssignOpKind::BitOrAssign => BinOpKind::BitOr,
+            AssignOpKind::ShlAssign => BinOpKind::Shl,
+            AssignOpKind::ShrAssign => BinOpKind::Shr,
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
+pub enum AssignOpKind {
+    /// The `+=` operator (addition)
+    AddAssign,
+    /// The `-=` operator (subtraction)
+    SubAssign,
+    /// The `*=` operator (multiplication)
+    MulAssign,
+    /// The `/=` operator (division)
+    DivAssign,
+    /// The `%=` operator (modulus)
+    RemAssign,
+    /// The `^=` operator (bitwise xor)
+    BitXorAssign,
+    /// The `&=` operator (bitwise and)
+    BitAndAssign,
+    /// The `|=` operator (bitwise or)
+    BitOrAssign,
+    /// The `<<=` operator (shift left)
+    ShlAssign,
+    /// The `>>=` operator (shift right)
+    ShrAssign,
+}
+
+impl AssignOpKind {
+    pub fn as_str(&self) -> &'static str {
+        use AssignOpKind::*;
+        match self {
+            AddAssign => "+=",
+            SubAssign => "-=",
+            MulAssign => "*=",
+            DivAssign => "/=",
+            RemAssign => "%=",
+            BitXorAssign => "^=",
+            BitAndAssign => "&=",
+            BitOrAssign => "|=",
+            ShlAssign => "<<=",
+            ShrAssign => ">>=",
+        }
+    }
+
+    /// AssignOps are always by value.
+    pub fn is_by_value(self) -> bool {
+        true
+    }
+}
+
+pub type AssignOp = Spanned<AssignOpKind>;
+
 /// Unary operator.
 ///
 /// Note that `&data` is not an operator, it's an `AddrOf` expression.
@@ -1593,7 +1662,7 @@ pub enum ExprKind {
     /// An assignment with an operator.
     ///
     /// E.g., `a += 1`.
-    AssignOp(BinOp, P<Expr>, P<Expr>),
+    AssignOp(AssignOp, P<Expr>, P<Expr>),
     /// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct field.
     Field(P<Expr>, Ident),
     /// An indexing operation (e.g., `foo[2]`).
@@ -3303,9 +3372,6 @@ pub struct Item<K = ItemKind> {
     pub id: NodeId,
     pub span: Span,
     pub vis: Visibility,
-    /// The name of the item.
-    /// It might be a dummy name in case of anonymous items.
-    pub ident: Ident,
 
     pub kind: K,
 
@@ -3327,23 +3393,23 @@ impl Item {
 
     pub fn opt_generics(&self) -> Option<&Generics> {
         match &self.kind {
-            ItemKind::ExternCrate(_)
+            ItemKind::ExternCrate(..)
             | ItemKind::Use(_)
-            | ItemKind::Mod(_, _)
+            | ItemKind::Mod(..)
             | ItemKind::ForeignMod(_)
             | ItemKind::GlobalAsm(_)
             | ItemKind::MacCall(_)
             | ItemKind::Delegation(_)
             | ItemKind::DelegationMac(_)
-            | ItemKind::MacroDef(_) => None,
+            | ItemKind::MacroDef(..) => None,
             ItemKind::Static(_) => None,
             ItemKind::Const(i) => Some(&i.generics),
             ItemKind::Fn(i) => Some(&i.generics),
             ItemKind::TyAlias(i) => Some(&i.generics),
-            ItemKind::TraitAlias(generics, _)
-            | ItemKind::Enum(_, generics)
-            | ItemKind::Struct(_, generics)
-            | ItemKind::Union(_, generics) => Some(&generics),
+            ItemKind::TraitAlias(_, generics, _)
+            | ItemKind::Enum(_, _, generics)
+            | ItemKind::Struct(_, _, generics)
+            | ItemKind::Union(_, _, generics) => Some(&generics),
             ItemKind::Trait(i) => Some(&i.generics),
             ItemKind::Impl(i) => Some(&i.generics),
         }
@@ -3420,6 +3486,7 @@ impl Default for FnHeader {
 pub struct Trait {
     pub safety: Safety,
     pub is_auto: IsAuto,
+    pub ident: Ident,
     pub generics: Generics,
     pub bounds: GenericBounds,
     pub items: ThinVec<P<AssocItem>>,
@@ -3465,6 +3532,7 @@ pub struct TyAliasWhereClauses {
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct TyAlias {
     pub defaultness: Defaultness,
+    pub ident: Ident,
     pub generics: Generics,
     pub where_clauses: TyAliasWhereClauses,
     pub bounds: GenericBounds,
@@ -3493,6 +3561,7 @@ pub struct FnContract {
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct Fn {
     pub defaultness: Defaultness,
+    pub ident: Ident,
     pub generics: Generics,
     pub sig: FnSig,
     pub contract: Option<P<FnContract>>,
@@ -3506,6 +3575,7 @@ pub struct Delegation {
     pub id: NodeId,
     pub qself: Option<P<QSelf>>,
     pub path: Path,
+    pub ident: Ident,
     pub rename: Option<Ident>,
     pub body: Option<P<Block>>,
     /// The item was expanded from a glob delegation item.
@@ -3523,6 +3593,7 @@ pub struct DelegationMac {
 
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct StaticItem {
+    pub ident: Ident,
     pub ty: P<Ty>,
     pub safety: Safety,
     pub mutability: Mutability,
@@ -3533,6 +3604,7 @@ pub struct StaticItem {
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct ConstItem {
     pub defaultness: Defaultness,
+    pub ident: Ident,
     pub generics: Generics,
     pub ty: P<Ty>,
     pub expr: Option<P<Expr>>,
@@ -3545,7 +3617,7 @@ pub enum ItemKind {
     /// An `extern crate` item, with the optional *original* crate name if the crate was renamed.
     ///
     /// E.g., `extern crate foo` or `extern crate foo_bar as foo`.
-    ExternCrate(Option<Symbol>),
+    ExternCrate(Option<Symbol>, Ident),
     /// A use declaration item (`use`).
     ///
     /// E.g., `use foo;`, `use foo::bar;` or `use foo::bar as FooBar;`.
@@ -3567,7 +3639,7 @@ pub enum ItemKind {
     /// E.g., `mod foo;` or `mod foo { .. }`.
     /// `unsafe` keyword on modules is accepted syntactically for macro DSLs, but not
     /// semantically by Rust.
-    Mod(Safety, ModKind),
+    Mod(Safety, Ident, ModKind),
     /// An external module (`extern`).
     ///
     /// E.g., `extern {}` or `extern "C" {}`.
@@ -3581,15 +3653,15 @@ pub enum ItemKind {
     /// An enum definition (`enum`).
     ///
     /// E.g., `enum Foo<A, B> { C<A>, D<B> }`.
-    Enum(EnumDef, Generics),
+    Enum(Ident, EnumDef, Generics),
     /// A struct definition (`struct`).
     ///
     /// E.g., `struct Foo<A> { x: A }`.
-    Struct(VariantData, Generics),
+    Struct(Ident, VariantData, Generics),
     /// A union definition (`union`).
     ///
     /// E.g., `union Foo<A, B> { x: A, y: B }`.
-    Union(VariantData, Generics),
+    Union(Ident, VariantData, Generics),
     /// A trait declaration (`trait`).
     ///
     /// E.g., `trait Foo { .. }`, `trait Foo<T> { .. }` or `auto trait Foo {}`.
@@ -3597,7 +3669,7 @@ pub enum ItemKind {
     /// Trait alias.
     ///
     /// E.g., `trait Foo = Bar + Quux;`.
-    TraitAlias(Generics, GenericBounds),
+    TraitAlias(Ident, Generics, GenericBounds),
     /// An implementation.
     ///
     /// E.g., `impl<A> Foo<A> { .. }` or `impl<A> Trait for Foo<A> { .. }`.
@@ -3608,7 +3680,7 @@ pub enum ItemKind {
     MacCall(P<MacCall>),
 
     /// A macro definition.
-    MacroDef(MacroDef),
+    MacroDef(Ident, MacroDef),
 
     /// A single delegation item (`reuse`).
     ///
@@ -3620,6 +3692,31 @@ pub enum ItemKind {
 }
 
 impl ItemKind {
+    pub fn ident(&self) -> Option<Ident> {
+        match *self {
+            ItemKind::ExternCrate(_, ident)
+            | ItemKind::Static(box StaticItem { ident, .. })
+            | ItemKind::Const(box ConstItem { ident, .. })
+            | ItemKind::Fn(box Fn { ident, .. })
+            | ItemKind::Mod(_, ident, _)
+            | ItemKind::TyAlias(box TyAlias { ident, .. })
+            | ItemKind::Enum(ident, ..)
+            | ItemKind::Struct(ident, ..)
+            | ItemKind::Union(ident, ..)
+            | ItemKind::Trait(box Trait { ident, .. })
+            | ItemKind::TraitAlias(ident, ..)
+            | ItemKind::MacroDef(ident, _)
+            | ItemKind::Delegation(box Delegation { ident, .. }) => Some(ident),
+
+            ItemKind::Use(_)
+            | ItemKind::ForeignMod(_)
+            | ItemKind::GlobalAsm(_)
+            | ItemKind::Impl(_)
+            | ItemKind::MacCall(_)
+            | ItemKind::DelegationMac(_) => None,
+        }
+    }
+
     /// "a" or "an"
     pub fn article(&self) -> &'static str {
         use ItemKind::*;
@@ -3660,11 +3757,11 @@ impl ItemKind {
             Self::Fn(box Fn { generics, .. })
             | Self::TyAlias(box TyAlias { generics, .. })
             | Self::Const(box ConstItem { generics, .. })
-            | Self::Enum(_, generics)
-            | Self::Struct(_, generics)
-            | Self::Union(_, generics)
+            | Self::Enum(_, _, generics)
+            | Self::Struct(_, _, generics)
+            | Self::Union(_, _, generics)
             | Self::Trait(box Trait { generics, .. })
-            | Self::TraitAlias(generics, _)
+            | Self::TraitAlias(_, generics, _)
             | Self::Impl(box Impl { generics, .. }) => Some(generics),
             _ => None,
         }
@@ -3700,6 +3797,17 @@ pub enum AssocItemKind {
 }
 
 impl AssocItemKind {
+    pub fn ident(&self) -> Option<Ident> {
+        match *self {
+            AssocItemKind::Const(box ConstItem { ident, .. })
+            | AssocItemKind::Fn(box Fn { ident, .. })
+            | AssocItemKind::Type(box TyAlias { ident, .. })
+            | AssocItemKind::Delegation(box Delegation { ident, .. }) => Some(ident),
+
+            AssocItemKind::MacCall(_) | AssocItemKind::DelegationMac(_) => None,
+        }
+    }
+
     pub fn defaultness(&self) -> Defaultness {
         match *self {
             Self::Const(box ConstItem { defaultness, .. })
@@ -3746,14 +3854,26 @@ impl TryFrom<ItemKind> for AssocItemKind {
 pub enum ForeignItemKind {
     /// A foreign static item (`static FOO: u8`).
     Static(Box<StaticItem>),
-    /// An foreign function.
+    /// A foreign function.
     Fn(Box<Fn>),
-    /// An foreign type.
+    /// A foreign type.
     TyAlias(Box<TyAlias>),
     /// A macro expanding to foreign items.
     MacCall(P<MacCall>),
 }
 
+impl ForeignItemKind {
+    pub fn ident(&self) -> Option<Ident> {
+        match *self {
+            ForeignItemKind::Static(box StaticItem { ident, .. })
+            | ForeignItemKind::Fn(box Fn { ident, .. })
+            | ForeignItemKind::TyAlias(box TyAlias { ident, .. }) => Some(ident),
+
+            ForeignItemKind::MacCall(_) => None,
+        }
+    }
+}
+
 impl From<ForeignItemKind> for ItemKind {
     fn from(foreign_item_kind: ForeignItemKind) -> ItemKind {
         match foreign_item_kind {
@@ -3790,21 +3910,21 @@ mod size_asserts {
 
     use super::*;
     // tidy-alphabetical-start
-    static_assert_size!(AssocItem, 88);
+    static_assert_size!(AssocItem, 80);
     static_assert_size!(AssocItemKind, 16);
     static_assert_size!(Attribute, 32);
     static_assert_size!(Block, 32);
     static_assert_size!(Expr, 72);
     static_assert_size!(ExprKind, 40);
-    static_assert_size!(Fn, 176);
-    static_assert_size!(ForeignItem, 88);
+    static_assert_size!(Fn, 184);
+    static_assert_size!(ForeignItem, 80);
     static_assert_size!(ForeignItemKind, 16);
     static_assert_size!(GenericArg, 24);
     static_assert_size!(GenericBound, 88);
     static_assert_size!(Generics, 40);
     static_assert_size!(Impl, 136);
-    static_assert_size!(Item, 136);
-    static_assert_size!(ItemKind, 64);
+    static_assert_size!(Item, 144);
+    static_assert_size!(ItemKind, 80);
     static_assert_size!(LitKind, 24);
     static_assert_size!(Local, 80);
     static_assert_size!(MetaItemLit, 40);
diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs
index 849cc650e9d..c9e2e9911ef 100644
--- a/compiler/rustc_ast/src/ast_traits.rs
+++ b/compiler/rustc_ast/src/ast_traits.rs
@@ -209,13 +209,11 @@ impl HasTokens for Attribute {
 impl HasTokens for Nonterminal {
     fn tokens(&self) -> Option<&LazyAttrTokenStream> {
         match self {
-            Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens(),
             Nonterminal::NtBlock(block) => block.tokens(),
         }
     }
     fn tokens_mut(&mut self) -> Option<&mut Option<LazyAttrTokenStream>> {
         match self {
-            Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens_mut(),
             Nonterminal::NtBlock(block) => block.tokens_mut(),
         }
     }
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index 4d613085d79..0b65246693d 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -570,6 +570,14 @@ impl MetaItemInner {
         }
     }
 
+    /// Returns the bool if `self` is a boolean `MetaItemInner::Literal`.
+    pub fn boolean_literal(&self) -> Option<bool> {
+        match self {
+            MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => Some(*b),
+            _ => None,
+        }
+    }
+
     /// Returns the `MetaItem` if `self` is a `MetaItemInner::MetaItem` or if it's
     /// `MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(_), .. })`.
     pub fn meta_item_or_bool(&self) -> Option<&MetaItemInner> {
diff --git a/compiler/rustc_ast/src/expand/autodiff_attrs.rs b/compiler/rustc_ast/src/expand/autodiff_attrs.rs
index c8ec185ee5e..f01c781f46c 100644
--- a/compiler/rustc_ast/src/expand/autodiff_attrs.rs
+++ b/compiler/rustc_ast/src/expand/autodiff_attrs.rs
@@ -77,6 +77,17 @@ pub struct AutoDiffAttrs {
     /// e.g. in the [JAX
     /// Documentation](https://jax.readthedocs.io/en/latest/_tutorials/advanced-autodiff.html#how-it-s-made-two-foundational-autodiff-functions).
     pub mode: DiffMode,
+    /// A user-provided, batching width. If not given, we will default to 1 (no batching).
+    /// Calling a differentiated, non-batched function through a loop 100 times is equivalent to:
+    /// - Calling the function 50 times with a batch size of 2
+    /// - Calling the function 25 times with a batch size of 4,
+    /// etc. A batched function takes more (or longer) arguments, and might be able to benefit from
+    /// cache locality, better re-usal of primal values, and other optimizations.
+    /// We will (before LLVM's vectorizer runs) just generate most LLVM-IR instructions `width`
+    /// times, so this massively increases code size. As such, values like 1024 are unlikely to
+    /// work. We should consider limiting this to u8 or u16, but will leave it at u32 for
+    /// experiments for now and focus on documenting the implications of a large width.
+    pub width: u32,
     pub ret_activity: DiffActivity,
     pub input_activity: Vec<DiffActivity>,
 }
@@ -222,6 +233,7 @@ impl AutoDiffAttrs {
     pub const fn error() -> Self {
         AutoDiffAttrs {
             mode: DiffMode::Error,
+            width: 0,
             ret_activity: DiffActivity::None,
             input_activity: Vec::new(),
         }
@@ -229,6 +241,7 @@ impl AutoDiffAttrs {
     pub fn source() -> Self {
         AutoDiffAttrs {
             mode: DiffMode::Source,
+            width: 0,
             ret_activity: DiffActivity::None,
             input_activity: Vec::new(),
         }
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 274fe312f7f..f7d13acdfc4 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -41,7 +41,6 @@ pub trait WalkItemKind {
         &mut self,
         span: Span,
         id: NodeId,
-        ident: &mut Ident,
         visibility: &mut Visibility,
         ctxt: Self::Ctxt,
         visitor: &mut impl MutVisitor,
@@ -900,8 +899,6 @@ pub fn visit_token<T: MutVisitor>(vis: &mut T, t: &mut Token) {
 fn visit_nonterminal<T: MutVisitor>(vis: &mut T, nt: &mut token::Nonterminal) {
     match nt {
         token::NtBlock(block) => vis.visit_block(block),
-        token::NtExpr(expr) => vis.visit_expr(expr),
-        token::NtLiteral(expr) => vis.visit_expr(expr),
     }
 }
 
@@ -963,10 +960,10 @@ fn walk_fn<T: MutVisitor>(vis: &mut T, kind: FnKind<'_>) {
     match kind {
         FnKind::Fn(
             _ctxt,
-            _ident,
             _vis,
             Fn {
                 defaultness,
+                ident,
                 generics,
                 contract,
                 body,
@@ -974,8 +971,9 @@ fn walk_fn<T: MutVisitor>(vis: &mut T, kind: FnKind<'_>) {
                 define_opaque,
             },
         ) => {
-            // Identifier and visibility are visited as a part of the item.
+            // Visibility is visited as a part of the item.
             visit_defaultness(vis, defaultness);
+            vis.visit_ident(ident);
             vis.visit_fn_header(header);
             vis.visit_generics(generics);
             vis.visit_fn_decl(decl);
@@ -1233,12 +1231,11 @@ pub fn walk_item_kind<K: WalkItemKind>(
     kind: &mut K,
     span: Span,
     id: NodeId,
-    ident: &mut Ident,
     visibility: &mut Visibility,
     ctxt: K::Ctxt,
     vis: &mut impl MutVisitor,
 ) {
-    kind.walk(span, id, ident, visibility, ctxt, vis)
+    kind.walk(span, id, visibility, ctxt, vis)
 }
 
 impl WalkItemKind for ItemKind {
@@ -1247,21 +1244,22 @@ impl WalkItemKind for ItemKind {
         &mut self,
         span: Span,
         id: NodeId,
-        ident: &mut Ident,
         visibility: &mut Visibility,
         _ctxt: Self::Ctxt,
         vis: &mut impl MutVisitor,
     ) {
         match self {
-            ItemKind::ExternCrate(_orig_name) => {}
+            ItemKind::ExternCrate(_orig_name, ident) => vis.visit_ident(ident),
             ItemKind::Use(use_tree) => vis.visit_use_tree(use_tree),
             ItemKind::Static(box StaticItem {
+                ident,
                 ty,
                 safety: _,
                 mutability: _,
                 expr,
                 define_opaque,
             }) => {
+                vis.visit_ident(ident);
                 vis.visit_ty(ty);
                 visit_opt(expr, |expr| vis.visit_expr(expr));
                 walk_define_opaques(vis, define_opaque);
@@ -1270,10 +1268,11 @@ impl WalkItemKind for ItemKind {
                 walk_const_item(vis, item);
             }
             ItemKind::Fn(func) => {
-                vis.visit_fn(FnKind::Fn(FnCtxt::Free, ident, visibility, &mut *func), span, id);
+                vis.visit_fn(FnKind::Fn(FnCtxt::Free, visibility, &mut *func), span, id);
             }
-            ItemKind::Mod(safety, mod_kind) => {
+            ItemKind::Mod(safety, ident, mod_kind) => {
                 visit_safety(vis, safety);
+                vis.visit_ident(ident);
                 match mod_kind {
                     ModKind::Loaded(
                         items,
@@ -1290,18 +1289,29 @@ impl WalkItemKind for ItemKind {
             }
             ItemKind::ForeignMod(nm) => vis.visit_foreign_mod(nm),
             ItemKind::GlobalAsm(asm) => vis.visit_inline_asm(asm),
-            ItemKind::TyAlias(box TyAlias { defaultness, generics, where_clauses, bounds, ty }) => {
+            ItemKind::TyAlias(box TyAlias {
+                defaultness,
+                ident,
+                generics,
+                where_clauses,
+                bounds,
+                ty,
+            }) => {
                 visit_defaultness(vis, defaultness);
+                vis.visit_ident(ident);
                 vis.visit_generics(generics);
                 visit_bounds(vis, bounds, BoundKind::Bound);
                 visit_opt(ty, |ty| vis.visit_ty(ty));
                 walk_ty_alias_where_clauses(vis, where_clauses);
             }
-            ItemKind::Enum(EnumDef { variants }, generics) => {
+            ItemKind::Enum(ident, EnumDef { variants }, generics) => {
+                vis.visit_ident(ident);
                 vis.visit_generics(generics);
                 variants.flat_map_in_place(|variant| vis.flat_map_variant(variant));
             }
-            ItemKind::Struct(variant_data, generics) | ItemKind::Union(variant_data, generics) => {
+            ItemKind::Struct(ident, variant_data, generics)
+            | ItemKind::Union(ident, variant_data, generics) => {
+                vis.visit_ident(ident);
                 vis.visit_generics(generics);
                 vis.visit_variant_data(variant_data);
             }
@@ -1326,22 +1336,28 @@ impl WalkItemKind for ItemKind {
                     vis.flat_map_assoc_item(item, AssocCtxt::Impl { of_trait: of_trait.is_some() })
                 });
             }
-            ItemKind::Trait(box Trait { safety, is_auto: _, generics, bounds, items }) => {
+            ItemKind::Trait(box Trait { safety, is_auto: _, ident, generics, bounds, items }) => {
                 visit_safety(vis, safety);
+                vis.visit_ident(ident);
                 vis.visit_generics(generics);
                 visit_bounds(vis, bounds, BoundKind::Bound);
                 items.flat_map_in_place(|item| vis.flat_map_assoc_item(item, AssocCtxt::Trait));
             }
-            ItemKind::TraitAlias(generics, bounds) => {
+            ItemKind::TraitAlias(ident, generics, bounds) => {
+                vis.visit_ident(ident);
                 vis.visit_generics(generics);
                 visit_bounds(vis, bounds, BoundKind::Bound);
             }
             ItemKind::MacCall(m) => vis.visit_mac_call(m),
-            ItemKind::MacroDef(def) => vis.visit_macro_def(def),
+            ItemKind::MacroDef(ident, def) => {
+                vis.visit_ident(ident);
+                vis.visit_macro_def(def)
+            }
             ItemKind::Delegation(box Delegation {
                 id,
                 qself,
                 path,
+                ident,
                 rename,
                 body,
                 from_glob: _,
@@ -1349,6 +1365,7 @@ impl WalkItemKind for ItemKind {
                 vis.visit_id(id);
                 vis.visit_qself(qself);
                 vis.visit_path(path);
+                vis.visit_ident(ident);
                 if let Some(rename) = rename {
                     vis.visit_ident(rename);
                 }
@@ -1381,7 +1398,6 @@ impl WalkItemKind for AssocItemKind {
         &mut self,
         span: Span,
         id: NodeId,
-        ident: &mut Ident,
         visibility: &mut Visibility,
         ctxt: Self::Ctxt,
         visitor: &mut impl MutVisitor,
@@ -1391,20 +1407,18 @@ impl WalkItemKind for AssocItemKind {
                 walk_const_item(visitor, item);
             }
             AssocItemKind::Fn(func) => {
-                visitor.visit_fn(
-                    FnKind::Fn(FnCtxt::Assoc(ctxt), ident, visibility, &mut *func),
-                    span,
-                    id,
-                );
+                visitor.visit_fn(FnKind::Fn(FnCtxt::Assoc(ctxt), visibility, &mut *func), span, id);
             }
             AssocItemKind::Type(box TyAlias {
                 defaultness,
+                ident,
                 generics,
                 where_clauses,
                 bounds,
                 ty,
             }) => {
                 visit_defaultness(visitor, defaultness);
+                visitor.visit_ident(ident);
                 visitor.visit_generics(generics);
                 visit_bounds(visitor, bounds, BoundKind::Bound);
                 visit_opt(ty, |ty| visitor.visit_ty(ty));
@@ -1415,6 +1429,7 @@ impl WalkItemKind for AssocItemKind {
                 id,
                 qself,
                 path,
+                ident,
                 rename,
                 body,
                 from_glob: _,
@@ -1422,6 +1437,7 @@ impl WalkItemKind for AssocItemKind {
                 visitor.visit_id(id);
                 visitor.visit_qself(qself);
                 visitor.visit_path(path);
+                visitor.visit_ident(ident);
                 if let Some(rename) = rename {
                     visitor.visit_ident(rename);
                 }
@@ -1449,8 +1465,9 @@ impl WalkItemKind for AssocItemKind {
 }
 
 fn walk_const_item<T: MutVisitor>(vis: &mut T, item: &mut ConstItem) {
-    let ConstItem { defaultness, generics, ty, expr, define_opaque } = item;
+    let ConstItem { defaultness, ident, generics, ty, expr, define_opaque } = item;
     visit_defaultness(vis, defaultness);
+    vis.visit_ident(ident);
     vis.visit_generics(generics);
     vis.visit_ty(ty);
     visit_opt(expr, |expr| vis.visit_expr(expr));
@@ -1487,12 +1504,11 @@ fn walk_item_ctxt<K: WalkItemKind>(
     item: &mut P<Item<K>>,
     ctxt: K::Ctxt,
 ) {
-    let Item { ident, attrs, id, kind, vis, span, tokens } = item.deref_mut();
+    let Item { attrs, id, kind, vis, span, tokens } = item.deref_mut();
     visitor.visit_id(id);
     visit_attrs(visitor, attrs);
     visitor.visit_vis(vis);
-    visitor.visit_ident(ident);
-    kind.walk(*span, *id, ident, vis, ctxt, visitor);
+    kind.walk(*span, *id, vis, ctxt, visitor);
     visit_lazy_tts(visitor, tokens);
     visitor.visit_span(span);
 }
@@ -1525,38 +1541,37 @@ impl WalkItemKind for ForeignItemKind {
         &mut self,
         span: Span,
         id: NodeId,
-        ident: &mut Ident,
         visibility: &mut Visibility,
         _ctxt: Self::Ctxt,
         visitor: &mut impl MutVisitor,
     ) {
         match self {
             ForeignItemKind::Static(box StaticItem {
+                ident,
                 ty,
                 mutability: _,
                 expr,
                 safety: _,
                 define_opaque,
             }) => {
+                visitor.visit_ident(ident);
                 visitor.visit_ty(ty);
                 visit_opt(expr, |expr| visitor.visit_expr(expr));
                 walk_define_opaques(visitor, define_opaque);
             }
             ForeignItemKind::Fn(func) => {
-                visitor.visit_fn(
-                    FnKind::Fn(FnCtxt::Foreign, ident, visibility, &mut *func),
-                    span,
-                    id,
-                );
+                visitor.visit_fn(FnKind::Fn(FnCtxt::Foreign, visibility, &mut *func), span, id);
             }
             ForeignItemKind::TyAlias(box TyAlias {
                 defaultness,
+                ident,
                 generics,
                 where_clauses,
                 bounds,
                 ty,
             }) => {
                 visit_defaultness(visitor, defaultness);
+                visitor.visit_ident(ident);
                 visitor.visit_generics(generics);
                 visit_bounds(visitor, bounds, BoundKind::Bound);
                 visit_opt(ty, |ty| visitor.visit_ty(ty));
@@ -1984,8 +1999,7 @@ impl DummyAstNode for Item {
                 span: Default::default(),
                 tokens: Default::default(),
             },
-            ident: Ident::dummy(),
-            kind: ItemKind::ExternCrate(None),
+            kind: ItemKind::ExternCrate(None, Ident::dummy()),
             tokens: Default::default(),
         }
     }
@@ -2052,7 +2066,7 @@ impl<N: DummyAstNode, T: DummyAstNode> DummyAstNode for crate::ast_traits::AstNo
 #[derive(Debug)]
 pub enum FnKind<'a> {
     /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`.
-    Fn(FnCtxt, &'a mut Ident, &'a mut Visibility, &'a mut Fn),
+    Fn(FnCtxt, &'a mut Visibility, &'a mut Fn),
 
     /// E.g., `|x, y| body`.
     Closure(
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index 6e6f0f1b266..d57a369eebf 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -198,16 +198,17 @@ impl Lit {
         }
     }
 
-    /// Keep this in sync with `Token::can_begin_literal_maybe_minus` excluding unary negation.
+    /// Keep this in sync with `Token::can_begin_literal_maybe_minus` and
+    /// `Parser::eat_token_lit` (excluding unary negation).
     pub fn from_token(token: &Token) -> Option<Lit> {
         match token.uninterpolate().kind {
             Ident(name, IdentIsRaw::No) if name.is_bool_lit() => Some(Lit::new(Bool, name, None)),
             Literal(token_lit) => Some(token_lit),
-            Interpolated(ref nt)
-                if let NtExpr(expr) | NtLiteral(expr) = &**nt
-                    && let ast::ExprKind::Lit(token_lit) = expr.kind =>
-            {
-                Some(token_lit)
+            OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
+                MetaVarKind::Literal | MetaVarKind::Expr { .. },
+            ))) => {
+                // Unreachable with the current test suite.
+                panic!("from_token metavar");
             }
             _ => None,
         }
@@ -447,8 +448,9 @@ pub enum TokenKind {
 
     /// Identifier token.
     /// Do not forget about `NtIdent` when you want to match on identifiers.
-    /// It's recommended to use `Token::(ident,uninterpolate,uninterpolated_span)` to
-    /// treat regular and interpolated identifiers in the same way.
+    /// It's recommended to use `Token::{ident,uninterpolate}` and
+    /// `Parser::token_uninterpolated_span` to treat regular and interpolated
+    /// identifiers in the same way.
     Ident(Symbol, IdentIsRaw),
     /// This identifier (and its span) is the identifier passed to the
     /// declarative macro. The span in the surrounding `Token` is the span of
@@ -457,8 +459,9 @@ pub enum TokenKind {
 
     /// Lifetime identifier token.
     /// Do not forget about `NtLifetime` when you want to match on lifetime identifiers.
-    /// It's recommended to use `Token::(lifetime,uninterpolate,uninterpolated_span)` to
-    /// treat regular and interpolated lifetime identifiers in the same way.
+    /// It's recommended to use `Token::{ident,uninterpolate}` and
+    /// `Parser::token_uninterpolated_span` to treat regular and interpolated
+    /// identifiers in the same way.
     Lifetime(Symbol, IdentIsRaw),
     /// This identifier (and its span) is the lifetime passed to the
     /// declarative macro. The span in the surrounding `Token` is the span of
@@ -584,20 +587,6 @@ impl Token {
         Token::new(Ident(ident.name, ident.is_raw_guess().into()), ident.span)
     }
 
-    /// For interpolated tokens, returns a span of the fragment to which the interpolated
-    /// token refers. For all other tokens this is just a regular span.
-    /// It is particularly important to use this for identifiers and lifetimes
-    /// for which spans affect name resolution and edition checks.
-    /// Note that keywords are also identifiers, so they should use this
-    /// if they keep spans or perform edition checks.
-    pub fn uninterpolated_span(&self) -> Span {
-        match self.kind {
-            NtIdent(ident, _) | NtLifetime(ident, _) => ident.span,
-            Interpolated(ref nt) => nt.use_span(),
-            _ => self.span,
-        }
-    }
-
     pub fn is_range_separator(&self) -> bool {
         [DotDot, DotDotDot, DotDotEq].contains(&self.kind)
     }
@@ -642,12 +631,7 @@ impl Token {
             PathSep                           | // global path
             Lifetime(..)                      | // labeled loop
             Pound                             => true, // expression attributes
-            Interpolated(ref nt) =>
-                matches!(&**nt,
-                    NtBlock(..)   |
-                    NtExpr(..)    |
-                    NtLiteral(..)
-                ),
+            Interpolated(ref nt) => matches!(&**nt, NtBlock(..)),
             OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
                 MetaVarKind::Block |
                 MetaVarKind::Expr { .. } |
@@ -677,11 +661,6 @@ impl Token {
             Lt |                                 // path (UFCS constant)
             Shl => true,                         // path (double UFCS)
             Or => matches!(pat_kind, PatWithOr), // leading vert `|` or-pattern
-            Interpolated(nt) =>
-                matches!(&**nt,
-                    | NtExpr(..)
-                    | NtLiteral(..)
-                ),
             OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
                 MetaVarKind::Expr { .. } |
                 MetaVarKind::Literal |
@@ -724,7 +703,7 @@ impl Token {
         match self.kind {
             OpenDelim(Delimiter::Brace) | Literal(..) | Minus => true,
             Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true,
-            Interpolated(ref nt) => matches!(&**nt, NtExpr(..) | NtBlock(..) | NtLiteral(..)),
+            Interpolated(ref nt) => matches!(&**nt, NtBlock(..)),
             OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
                 MetaVarKind::Expr { .. } | MetaVarKind::Block | MetaVarKind::Literal,
             ))) => true,
@@ -768,22 +747,12 @@ impl Token {
     ///
     /// In other words, would this token be a valid start of `parse_literal_maybe_minus`?
     ///
-    /// Keep this in sync with and `Lit::from_token`, excluding unary negation.
+    /// Keep this in sync with `Lit::from_token` and `Parser::eat_token_lit`
+    /// (excluding unary negation).
     pub fn can_begin_literal_maybe_minus(&self) -> bool {
         match self.uninterpolate().kind {
             Literal(..) | Minus => true,
             Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true,
-            Interpolated(ref nt) => match &**nt {
-                NtLiteral(_) => true,
-                NtExpr(e) => match &e.kind {
-                    ast::ExprKind::Lit(_) => true,
-                    ast::ExprKind::Unary(ast::UnOp::Neg, e) => {
-                        matches!(&e.kind, ast::ExprKind::Lit(_))
-                    }
-                    _ => false,
-                },
-                _ => false,
-            },
             OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind))) => match mv_kind {
                 MetaVarKind::Literal => true,
                 MetaVarKind::Expr { can_begin_literal_maybe_minus, .. } => {
@@ -798,14 +767,6 @@ impl Token {
     pub fn can_begin_string_literal(&self) -> bool {
         match self.uninterpolate().kind {
             Literal(..) => true,
-            Interpolated(ref nt) => match &**nt {
-                NtLiteral(_) => true,
-                NtExpr(e) => match &e.kind {
-                    ast::ExprKind::Lit(_) => true,
-                    _ => false,
-                },
-                _ => false,
-            },
             OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind))) => match mv_kind {
                 MetaVarKind::Literal => true,
                 MetaVarKind::Expr { can_begin_string_literal, .. } => can_begin_string_literal,
@@ -869,12 +830,17 @@ impl Token {
 
     /// Is this a pre-parsed expression dropped into the token stream
     /// (which happens while parsing the result of macro expansion)?
-    pub fn is_whole_expr(&self) -> bool {
+    pub fn is_metavar_expr(&self) -> bool {
         #[allow(irrefutable_let_patterns)] // FIXME: temporary
         if let Interpolated(nt) = &self.kind
-            && let NtExpr(_) | NtLiteral(_) | NtBlock(_) = &**nt
+            && let NtBlock(_) = &**nt
         {
             true
+        } else if matches!(
+            self.is_metavar_seq(),
+            Some(MetaVarKind::Expr { .. } | MetaVarKind::Literal | MetaVarKind::Path)
+        ) {
+            true
         } else {
             matches!(self.is_metavar_seq(), Some(MetaVarKind::Path))
         }
@@ -882,6 +848,7 @@ impl Token {
 
     /// Is the token an interpolated block (`$b:block`)?
     pub fn is_whole_block(&self) -> bool {
+        #[allow(irrefutable_let_patterns)] // FIXME: temporary
         if let Interpolated(nt) = &self.kind
             && let NtBlock(..) = &**nt
         {
@@ -1100,8 +1067,6 @@ pub enum NtExprKind {
 /// For interpolation during macro expansion.
 pub enum Nonterminal {
     NtBlock(P<ast::Block>),
-    NtExpr(P<ast::Expr>),
-    NtLiteral(P<ast::Expr>),
 }
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)]
@@ -1191,15 +1156,12 @@ impl Nonterminal {
     pub fn use_span(&self) -> Span {
         match self {
             NtBlock(block) => block.span,
-            NtExpr(expr) | NtLiteral(expr) => expr.span,
         }
     }
 
     pub fn descr(&self) -> &'static str {
         match self {
             NtBlock(..) => "block",
-            NtExpr(..) => "expression",
-            NtLiteral(..) => "literal",
         }
     }
 }
@@ -1218,8 +1180,6 @@ impl fmt::Debug for Nonterminal {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match *self {
             NtBlock(..) => f.pad("NtBlock(..)"),
-            NtExpr(..) => f.pad("NtExpr(..)"),
-            NtLiteral(..) => f.pad("NtLiteral(..)"),
         }
     }
 }
@@ -1242,7 +1202,7 @@ mod size_asserts {
     // tidy-alphabetical-start
     static_assert_size!(Lit, 12);
     static_assert_size!(LitKind, 2);
-    static_assert_size!(Nonterminal, 16);
+    static_assert_size!(Nonterminal, 8);
     static_assert_size!(Token, 24);
     static_assert_size!(TokenKind, 16);
     // tidy-alphabetical-end
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index bdd244be6d1..fb331e74aeb 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -233,35 +233,52 @@ fn attrs_and_tokens_to_token_trees(
 
     // Insert inner attribute tokens.
     if !inner_attrs.is_empty() {
-        let mut found = false;
-        // Check the last two trees (to account for a trailing semi)
-        for tree in res.iter_mut().rev().take(2) {
-            if let TokenTree::Delimited(span, spacing, delim, delim_tokens) = tree {
-                // Inner attributes are only supported on extern blocks, functions,
-                // impls, and modules. All of these have their inner attributes
-                // placed at the beginning of the rightmost outermost braced group:
-                // e.g. fn foo() { #![my_attr] }
-                //
-                // Therefore, we can insert them back into the right location
-                // without needing to do any extra position tracking.
-                //
-                // Note: Outline modules are an exception - they can
-                // have attributes like `#![my_attr]` at the start of a file.
-                // Support for custom attributes in this position is not
-                // properly implemented - we always synthesize fake tokens,
-                // so we never reach this code.
+        let found = insert_inner_attrs(inner_attrs, res);
+        assert!(found, "Failed to find trailing delimited group in: {res:?}");
+    }
+
+    // Inner attributes are only supported on blocks, functions, impls, and
+    // modules. All of these have their inner attributes placed at the
+    // beginning of the rightmost outermost braced group:
+    // e.g. `fn foo() { #![my_attr] }`. (Note: the braces may be within
+    // invisible delimiters.)
+    //
+    // Therefore, we can insert them back into the right location without
+    // needing to do any extra position tracking.
+    //
+    // Note: Outline modules are an exception - they can have attributes like
+    // `#![my_attr]` at the start of a file. Support for custom attributes in
+    // this position is not properly implemented - we always synthesize fake
+    // tokens, so we never reach this code.
+    fn insert_inner_attrs(inner_attrs: &[Attribute], tts: &mut Vec<TokenTree>) -> bool {
+        for tree in tts.iter_mut().rev() {
+            if let TokenTree::Delimited(span, spacing, Delimiter::Brace, stream) = tree {
+                // Found it: the rightmost, outermost braced group.
                 let mut tts = vec![];
                 for inner_attr in inner_attrs {
                     tts.extend(inner_attr.token_trees());
                 }
-                tts.extend(delim_tokens.0.iter().cloned());
+                tts.extend(stream.0.iter().cloned());
                 let stream = TokenStream::new(tts);
-                *tree = TokenTree::Delimited(*span, *spacing, *delim, stream);
-                found = true;
-                break;
+                *tree = TokenTree::Delimited(*span, *spacing, Delimiter::Brace, stream);
+                return true;
+            } else if let TokenTree::Delimited(span, spacing, Delimiter::Invisible(src), stream) =
+                tree
+            {
+                // Recurse inside invisible delimiters.
+                let mut vec: Vec<_> = stream.iter().cloned().collect();
+                if insert_inner_attrs(inner_attrs, &mut vec) {
+                    *tree = TokenTree::Delimited(
+                        *span,
+                        *spacing,
+                        Delimiter::Invisible(*src),
+                        TokenStream::new(vec),
+                    );
+                    return true;
+                }
             }
         }
-        assert!(found, "Failed to find trailing delimited group in: {res:?}");
+        false
     }
 }
 
@@ -462,7 +479,6 @@ impl TokenStream {
     pub fn from_nonterminal_ast(nt: &Nonterminal) -> TokenStream {
         match nt {
             Nonterminal::NtBlock(block) => TokenStream::from_ast(block),
-            Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => TokenStream::from_ast(expr),
         }
     }
 
diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs
index 98b1fc52ed7..1e5f414fae1 100644
--- a/compiler/rustc_ast/src/util/parser.rs
+++ b/compiler/rustc_ast/src/util/parser.rs
@@ -1,6 +1,6 @@
 use rustc_span::kw;
 
-use crate::ast::{self, BinOpKind, RangeLimits};
+use crate::ast::{self, AssignOpKind, BinOpKind, RangeLimits};
 use crate::token::{self, Token};
 
 /// Associative operator.
@@ -9,7 +9,7 @@ pub enum AssocOp {
     /// A binary op.
     Binary(BinOpKind),
     /// `?=` where ? is one of the assignable BinOps
-    AssignOp(BinOpKind),
+    AssignOp(AssignOpKind),
     /// `=`
     Assign,
     /// `as`
@@ -44,16 +44,16 @@ impl AssocOp {
             token::Or => Some(Binary(BinOpKind::BitOr)),
             token::Shl => Some(Binary(BinOpKind::Shl)),
             token::Shr => Some(Binary(BinOpKind::Shr)),
-            token::PlusEq => Some(AssignOp(BinOpKind::Add)),
-            token::MinusEq => Some(AssignOp(BinOpKind::Sub)),
-            token::StarEq => Some(AssignOp(BinOpKind::Mul)),
-            token::SlashEq => Some(AssignOp(BinOpKind::Div)),
-            token::PercentEq => Some(AssignOp(BinOpKind::Rem)),
-            token::CaretEq => Some(AssignOp(BinOpKind::BitXor)),
-            token::AndEq => Some(AssignOp(BinOpKind::BitAnd)),
-            token::OrEq => Some(AssignOp(BinOpKind::BitOr)),
-            token::ShlEq => Some(AssignOp(BinOpKind::Shl)),
-            token::ShrEq => Some(AssignOp(BinOpKind::Shr)),
+            token::PlusEq => Some(AssignOp(AssignOpKind::AddAssign)),
+            token::MinusEq => Some(AssignOp(AssignOpKind::SubAssign)),
+            token::StarEq => Some(AssignOp(AssignOpKind::MulAssign)),
+            token::SlashEq => Some(AssignOp(AssignOpKind::DivAssign)),
+            token::PercentEq => Some(AssignOp(AssignOpKind::RemAssign)),
+            token::CaretEq => Some(AssignOp(AssignOpKind::BitXorAssign)),
+            token::AndEq => Some(AssignOp(AssignOpKind::BitAndAssign)),
+            token::OrEq => Some(AssignOp(AssignOpKind::BitOrAssign)),
+            token::ShlEq => Some(AssignOp(AssignOpKind::ShlAssign)),
+            token::ShrEq => Some(AssignOp(AssignOpKind::ShrAssign)),
             token::Lt => Some(Binary(BinOpKind::Lt)),
             token::Le => Some(Binary(BinOpKind::Le)),
             token::Ge => Some(Binary(BinOpKind::Ge)),
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 2716601ca4f..1ef92ff8898 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -66,7 +66,7 @@ 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 Ident, &'a Visibility, &'a Fn),
+    Fn(FnCtxt, &'a Visibility, &'a Fn),
 
     /// E.g., `|x, y| body`.
     Closure(&'a ClosureBinder, &'a Option<CoroutineKind>, &'a FnDecl, &'a Expr),
@@ -75,21 +75,21 @@ pub enum FnKind<'a> {
 impl<'a> FnKind<'a> {
     pub fn header(&self) -> Option<&'a FnHeader> {
         match *self {
-            FnKind::Fn(_, _, _, Fn { sig, .. }) => Some(&sig.header),
+            FnKind::Fn(_, _, Fn { sig, .. }) => Some(&sig.header),
             FnKind::Closure(..) => None,
         }
     }
 
     pub fn ident(&self) -> Option<&Ident> {
         match self {
-            FnKind::Fn(_, ident, ..) => Some(ident),
+            FnKind::Fn(_, _, Fn { ident, .. }) => Some(ident),
             _ => None,
         }
     }
 
     pub fn decl(&self) -> &'a FnDecl {
         match self {
-            FnKind::Fn(_, _, _, Fn { sig, .. }) => &sig.decl,
+            FnKind::Fn(_, _, Fn { sig, .. }) => &sig.decl,
             FnKind::Closure(_, _, decl, _) => decl,
         }
     }
@@ -118,7 +118,6 @@ pub trait WalkItemKind {
         &'a self,
         span: Span,
         id: NodeId,
-        ident: &'a Ident,
         visibility: &'a Visibility,
         ctxt: Self::Ctxt,
         visitor: &mut V,
@@ -364,63 +363,72 @@ impl WalkItemKind for ItemKind {
         &'a self,
         span: Span,
         id: NodeId,
-        ident: &'a Ident,
         vis: &'a Visibility,
         _ctxt: Self::Ctxt,
         visitor: &mut V,
     ) -> V::Result {
         match self {
-            ItemKind::ExternCrate(_rename) => {}
+            ItemKind::ExternCrate(_rename, ident) => try_visit!(visitor.visit_ident(ident)),
             ItemKind::Use(use_tree) => try_visit!(visitor.visit_use_tree(use_tree, id, false)),
             ItemKind::Static(box StaticItem {
+                ident,
                 ty,
                 safety: _,
                 mutability: _,
                 expr,
                 define_opaque,
             }) => {
+                try_visit!(visitor.visit_ident(ident));
                 try_visit!(visitor.visit_ty(ty));
                 visit_opt!(visitor, visit_expr, expr);
                 try_visit!(walk_define_opaques(visitor, define_opaque));
             }
             ItemKind::Const(box ConstItem {
                 defaultness: _,
+                ident,
                 generics,
                 ty,
                 expr,
                 define_opaque,
             }) => {
+                try_visit!(visitor.visit_ident(ident));
                 try_visit!(visitor.visit_generics(generics));
                 try_visit!(visitor.visit_ty(ty));
                 visit_opt!(visitor, visit_expr, expr);
                 try_visit!(walk_define_opaques(visitor, define_opaque));
             }
             ItemKind::Fn(func) => {
-                let kind = FnKind::Fn(FnCtxt::Free, ident, vis, &*func);
+                let kind = FnKind::Fn(FnCtxt::Free, vis, &*func);
                 try_visit!(visitor.visit_fn(kind, span, id));
             }
-            ItemKind::Mod(_unsafety, mod_kind) => match mod_kind {
-                ModKind::Loaded(items, _inline, _inner_span, _) => {
-                    walk_list!(visitor, visit_item, items);
+            ItemKind::Mod(_unsafety, ident, mod_kind) => {
+                try_visit!(visitor.visit_ident(ident));
+                match mod_kind {
+                    ModKind::Loaded(items, _inline, _inner_span, _) => {
+                        walk_list!(visitor, visit_item, items);
+                    }
+                    ModKind::Unloaded => {}
                 }
-                ModKind::Unloaded => {}
-            },
+            }
             ItemKind::ForeignMod(ForeignMod { extern_span: _, safety: _, abi: _, items }) => {
                 walk_list!(visitor, visit_foreign_item, items);
             }
             ItemKind::GlobalAsm(asm) => try_visit!(visitor.visit_inline_asm(asm)),
             ItemKind::TyAlias(box TyAlias {
                 generics,
+                ident,
                 bounds,
                 ty,
                 defaultness: _,
                 where_clauses: _,
             }) => {
+                try_visit!(visitor.visit_ident(ident));
                 try_visit!(visitor.visit_generics(generics));
                 walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
                 visit_opt!(visitor, visit_ty, ty);
             }
-            ItemKind::Enum(enum_definition, generics) => {
+            ItemKind::Enum(ident, enum_definition, generics) => {
+                try_visit!(visitor.visit_ident(ident));
                 try_visit!(visitor.visit_generics(generics));
                 try_visit!(visitor.visit_enum_def(enum_definition));
             }
@@ -444,32 +452,47 @@ impl WalkItemKind for ItemKind {
                     AssocCtxt::Impl { of_trait: of_trait.is_some() }
                 );
             }
-            ItemKind::Struct(struct_definition, generics)
-            | ItemKind::Union(struct_definition, generics) => {
+            ItemKind::Struct(ident, struct_definition, generics)
+            | ItemKind::Union(ident, struct_definition, generics) => {
+                try_visit!(visitor.visit_ident(ident));
                 try_visit!(visitor.visit_generics(generics));
                 try_visit!(visitor.visit_variant_data(struct_definition));
             }
-            ItemKind::Trait(box Trait { safety: _, is_auto: _, generics, bounds, items }) => {
+            ItemKind::Trait(box Trait {
+                safety: _,
+                is_auto: _,
+                ident,
+                generics,
+                bounds,
+                items,
+            }) => {
+                try_visit!(visitor.visit_ident(ident));
                 try_visit!(visitor.visit_generics(generics));
                 walk_list!(visitor, visit_param_bound, bounds, BoundKind::SuperTraits);
                 walk_list!(visitor, visit_assoc_item, items, AssocCtxt::Trait);
             }
-            ItemKind::TraitAlias(generics, bounds) => {
+            ItemKind::TraitAlias(ident, generics, bounds) => {
+                try_visit!(visitor.visit_ident(ident));
                 try_visit!(visitor.visit_generics(generics));
                 walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
             }
             ItemKind::MacCall(mac) => try_visit!(visitor.visit_mac_call(mac)),
-            ItemKind::MacroDef(ts) => try_visit!(visitor.visit_mac_def(ts, id)),
+            ItemKind::MacroDef(ident, ts) => {
+                try_visit!(visitor.visit_ident(ident));
+                try_visit!(visitor.visit_mac_def(ts, id))
+            }
             ItemKind::Delegation(box Delegation {
                 id,
                 qself,
                 path,
+                ident,
                 rename,
                 body,
                 from_glob: _,
             }) => {
                 try_visit!(visitor.visit_qself(qself));
                 try_visit!(visitor.visit_path(path, *id));
+                try_visit!(visitor.visit_ident(ident));
                 visit_opt!(visitor, visit_ident, rename);
                 visit_opt!(visitor, visit_block, body);
             }
@@ -743,34 +766,37 @@ impl WalkItemKind for ForeignItemKind {
         &'a self,
         span: Span,
         id: NodeId,
-        ident: &'a Ident,
         vis: &'a Visibility,
         _ctxt: Self::Ctxt,
         visitor: &mut V,
     ) -> V::Result {
         match self {
             ForeignItemKind::Static(box StaticItem {
+                ident,
                 ty,
                 mutability: _,
                 expr,
                 safety: _,
                 define_opaque,
             }) => {
+                try_visit!(visitor.visit_ident(ident));
                 try_visit!(visitor.visit_ty(ty));
                 visit_opt!(visitor, visit_expr, expr);
                 try_visit!(walk_define_opaques(visitor, define_opaque));
             }
             ForeignItemKind::Fn(func) => {
-                let kind = FnKind::Fn(FnCtxt::Foreign, ident, vis, &*func);
+                let kind = FnKind::Fn(FnCtxt::Foreign, vis, &*func);
                 try_visit!(visitor.visit_fn(kind, span, id));
             }
             ForeignItemKind::TyAlias(box TyAlias {
                 generics,
+                ident,
                 bounds,
                 ty,
                 defaultness: _,
                 where_clauses: _,
             }) => {
+                try_visit!(visitor.visit_ident(ident));
                 try_visit!(visitor.visit_generics(generics));
                 walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
                 visit_opt!(visitor, visit_ty, ty);
@@ -917,10 +943,10 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu
     match kind {
         FnKind::Fn(
             _ctxt,
-            _ident,
             _vis,
             Fn {
                 defaultness: _,
+                ident,
                 sig: FnSig { header, decl, span: _ },
                 generics,
                 contract,
@@ -928,7 +954,8 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu
                 define_opaque,
             },
         ) => {
-            // Identifier and visibility are visited as a part of the item.
+            // Visibility is visited as a part of the item.
+            try_visit!(visitor.visit_ident(ident));
             try_visit!(visitor.visit_fn_header(header));
             try_visit!(visitor.visit_generics(generics));
             try_visit!(visitor.visit_fn_decl(decl));
@@ -952,7 +979,6 @@ impl WalkItemKind for AssocItemKind {
         &'a self,
         span: Span,
         id: NodeId,
-        ident: &'a Ident,
         vis: &'a Visibility,
         ctxt: Self::Ctxt,
         visitor: &mut V,
@@ -960,28 +986,32 @@ impl WalkItemKind for AssocItemKind {
         match self {
             AssocItemKind::Const(box ConstItem {
                 defaultness: _,
+                ident,
                 generics,
                 ty,
                 expr,
                 define_opaque,
             }) => {
+                try_visit!(visitor.visit_ident(ident));
                 try_visit!(visitor.visit_generics(generics));
                 try_visit!(visitor.visit_ty(ty));
                 visit_opt!(visitor, visit_expr, expr);
                 try_visit!(walk_define_opaques(visitor, define_opaque));
             }
             AssocItemKind::Fn(func) => {
-                let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), ident, vis, &*func);
+                let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), vis, &*func);
                 try_visit!(visitor.visit_fn(kind, span, id));
             }
             AssocItemKind::Type(box TyAlias {
                 generics,
+                ident,
                 bounds,
                 ty,
                 defaultness: _,
                 where_clauses: _,
             }) => {
                 try_visit!(visitor.visit_generics(generics));
+                try_visit!(visitor.visit_ident(ident));
                 walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
                 visit_opt!(visitor, visit_ty, ty);
             }
@@ -992,12 +1022,14 @@ impl WalkItemKind for AssocItemKind {
                 id,
                 qself,
                 path,
+                ident,
                 rename,
                 body,
                 from_glob: _,
             }) => {
                 try_visit!(visitor.visit_qself(qself));
                 try_visit!(visitor.visit_path(path, *id));
+                try_visit!(visitor.visit_ident(ident));
                 visit_opt!(visitor, visit_ident, rename);
                 visit_opt!(visitor, visit_block, body);
             }
@@ -1039,11 +1071,10 @@ fn walk_item_ctxt<'a, V: Visitor<'a>, K: WalkItemKind>(
     item: &'a Item<K>,
     ctxt: K::Ctxt,
 ) -> V::Result {
-    let Item { id, span, ident, vis, attrs, kind, tokens: _ } = item;
+    let Item { id, span, vis, attrs, kind, tokens: _ } = item;
     walk_list!(visitor, visit_attribute, attrs);
     try_visit!(visitor.visit_vis(vis));
-    try_visit!(visitor.visit_ident(ident));
-    try_visit!(kind.walk(*span, *id, ident, vis, ctxt, visitor));
+    try_visit!(kind.walk(*span, *id, vis, ctxt, visitor));
     V::Result::output()
 }
 
diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs
index f7640c602d6..9899ee03a51 100644
--- a/compiler/rustc_ast_lowering/src/delegation.rs
+++ b/compiler/rustc_ast_lowering/src/delegation.rs
@@ -56,6 +56,7 @@ use crate::{AllowReturnTypeNotation, ImplTraitPosition, ResolverAstLoweringExt};
 pub(crate) struct DelegationResults<'hir> {
     pub body_id: hir::BodyId,
     pub sig: hir::FnSig<'hir>,
+    pub ident: Ident,
     pub generics: &'hir hir::Generics<'hir>,
 }
 
@@ -104,9 +105,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let decl = self.lower_delegation_decl(sig_id, param_count, c_variadic, span);
                 let sig = self.lower_delegation_sig(sig_id, decl, span);
                 let body_id = self.lower_delegation_body(delegation, param_count, span);
-
+                let ident = self.lower_ident(delegation.ident);
                 let generics = self.lower_delegation_generics(span);
-                DelegationResults { body_id, sig, generics }
+                DelegationResults { body_id, sig, ident, generics }
             }
             Err(err) => self.generate_delegation_error(err, span),
         }
@@ -405,8 +406,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let header = self.generate_header_error();
         let sig = hir::FnSig { decl, header, span };
 
+        let ident = Ident::dummy();
         let body_id = self.lower_body(|this| (&[], this.mk_expr(hir::ExprKind::Err(err), span)));
-        DelegationResults { generics, body_id, sig }
+        DelegationResults { ident, generics, body_id, sig }
     }
 
     fn generate_header_error(&self) -> hir::FnHeader {
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 52291fdfb30..80bb1e8fc41 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -274,7 +274,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 }
                 ExprKind::Assign(el, er, span) => self.lower_expr_assign(el, er, *span, e.span),
                 ExprKind::AssignOp(op, el, er) => hir::ExprKind::AssignOp(
-                    self.lower_binop(*op),
+                    self.lower_assign_op(*op),
                     self.lower_expr(el),
                     self.lower_expr(er),
                 ),
@@ -443,6 +443,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
         Spanned { node: b.node, span: self.lower_span(b.span) }
     }
 
+    fn lower_assign_op(&mut self, a: AssignOp) -> AssignOp {
+        Spanned { node: a.node, span: self.lower_span(a.span) }
+    }
+
     fn lower_legacy_const_generics(
         &mut self,
         mut f: Expr,
diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs
index 343895984ca..0de0319c667 100644
--- a/compiler/rustc_ast_lowering/src/format.rs
+++ b/compiler/rustc_ast_lowering/src/format.rs
@@ -323,14 +323,12 @@ fn make_count<'hir>(
 /// Generates
 ///
 /// ```text
-///     <core::fmt::rt::Placeholder::new(
-///         …usize, // position
-///         '…', // fill
-///         <core::fmt::rt::Alignment>::…, // alignment
-///         …u32, // flags
-///         <core::fmt::rt::Count::…>, // width
-///         <core::fmt::rt::Count::…>, // precision
-///     )
+///     <core::fmt::rt::Placeholder {
+///         position: …usize,
+///         flags: …u32,
+///         precision: <core::fmt::rt::Count::…>,
+///         width: <core::fmt::rt::Count::…>,
+///     }
 /// ```
 fn make_format_spec<'hir>(
     ctx: &mut LoweringContext<'_, 'hir>,
@@ -384,13 +382,13 @@ fn make_format_spec<'hir>(
     let flags = ctx.expr_u32(sp, flags);
     let precision = make_count(ctx, sp, precision, argmap);
     let width = make_count(ctx, sp, width, argmap);
-    let format_placeholder_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
-        sp,
-        hir::LangItem::FormatPlaceholder,
-        sym::new,
-    ));
-    let args = ctx.arena.alloc_from_iter([position, flags, precision, width]);
-    ctx.expr_call_mut(sp, format_placeholder_new, args)
+    let position = ctx.expr_field(Ident::new(sym::position, sp), ctx.arena.alloc(position), sp);
+    let flags = ctx.expr_field(Ident::new(sym::flags, sp), ctx.arena.alloc(flags), sp);
+    let precision = ctx.expr_field(Ident::new(sym::precision, sp), ctx.arena.alloc(precision), sp);
+    let width = ctx.expr_field(Ident::new(sym::width, sp), ctx.arena.alloc(width), sp);
+    let placeholder = ctx.arena.alloc(hir::QPath::LangItem(hir::LangItem::FormatPlaceholder, sp));
+    let fields = ctx.arena.alloc_from_iter([position, flags, precision, width]);
+    ctx.expr(sp, hir::ExprKind::Struct(placeholder, fields, hir::StructTailExpr::None))
 }
 
 fn expand_format_args<'hir>(
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 43bf951eddc..958a6917dff 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -107,7 +107,6 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
     }
 
     fn lower_foreign_item(&mut self, item: &ForeignItem) {
-        debug_assert_ne!(item.ident.name, kw::Empty);
         self.with_lctx(item.id, |lctx| hir::OwnerNode::ForeignItem(lctx.lower_foreign_item(item)))
     }
 }
@@ -151,7 +150,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let vis_span = self.lower_span(i.vis.span);
         let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
         let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);
-        let kind = self.lower_item_kind(i.span, i.id, hir_id, i.ident, attrs, vis_span, &i.kind);
+        let kind = self.lower_item_kind(i.span, i.id, hir_id, attrs, vis_span, &i.kind);
         let item = hir::Item {
             owner_id: hir_id.expect_owner(),
             kind,
@@ -166,41 +165,44 @@ impl<'hir> LoweringContext<'_, 'hir> {
         span: Span,
         id: NodeId,
         hir_id: hir::HirId,
-        ident: Ident,
         attrs: &'hir [hir::Attribute],
         vis_span: Span,
         i: &ItemKind,
     ) -> hir::ItemKind<'hir> {
         match i {
-            ItemKind::ExternCrate(orig_name) => {
-                debug_assert_ne!(ident.name, kw::Empty);
-                let ident = self.lower_ident(ident);
+            ItemKind::ExternCrate(orig_name, ident) => {
+                let ident = self.lower_ident(*ident);
                 hir::ItemKind::ExternCrate(*orig_name, ident)
             }
             ItemKind::Use(use_tree) => {
-                debug_assert_eq!(ident.name, kw::Empty);
                 // Start with an empty prefix.
                 let prefix = Path { segments: ThinVec::new(), span: use_tree.span, tokens: None };
 
                 self.lower_use_tree(use_tree, &prefix, id, vis_span, attrs)
             }
             ItemKind::Static(box ast::StaticItem {
+                ident,
                 ty: t,
                 safety: _,
                 mutability: m,
                 expr: e,
                 define_opaque,
             }) => {
-                debug_assert_ne!(ident.name, kw::Empty);
-                let ident = self.lower_ident(ident);
+                let ident = self.lower_ident(*ident);
                 let (ty, body_id) =
                     self.lower_const_item(t, span, e.as_deref(), ImplTraitPosition::StaticTy);
                 self.lower_define_opaque(hir_id, define_opaque);
                 hir::ItemKind::Static(ident, ty, *m, body_id)
             }
-            ItemKind::Const(box ast::ConstItem { generics, ty, expr, define_opaque, .. }) => {
-                debug_assert_ne!(ident.name, kw::Empty);
-                let ident = self.lower_ident(ident);
+            ItemKind::Const(box ast::ConstItem {
+                ident,
+                generics,
+                ty,
+                expr,
+                define_opaque,
+                ..
+            }) => {
+                let ident = self.lower_ident(*ident);
                 let (generics, (ty, body_id)) = self.lower_generics(
                     generics,
                     id,
@@ -214,13 +216,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
             }
             ItemKind::Fn(box Fn {
                 sig: FnSig { decl, header, span: fn_sig_span },
+                ident,
                 generics,
                 body,
                 contract,
                 define_opaque,
                 ..
             }) => {
-                debug_assert_ne!(ident.name, kw::Empty);
                 self.with_new_scopes(*fn_sig_span, |this| {
                     // Note: we don't need to change the return type from `T` to
                     // `impl Future<Output = T>` here because lower_body
@@ -248,7 +250,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         span: this.lower_span(*fn_sig_span),
                     };
                     this.lower_define_opaque(hir_id, define_opaque);
-                    let ident = this.lower_ident(ident);
+                    let ident = this.lower_ident(*ident);
                     hir::ItemKind::Fn {
                         ident,
                         sig,
@@ -258,9 +260,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     }
                 })
             }
-            ItemKind::Mod(_, mod_kind) => {
-                debug_assert_ne!(ident.name, kw::Empty);
-                let ident = self.lower_ident(ident);
+            ItemKind::Mod(_, ident, mod_kind) => {
+                let ident = self.lower_ident(*ident);
                 match mod_kind {
                     ModKind::Loaded(items, _, spans, _) => {
                         hir::ItemKind::Mod(ident, self.lower_mod(items, spans))
@@ -268,24 +269,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     ModKind::Unloaded => panic!("`mod` items should have been loaded by now"),
                 }
             }
-            ItemKind::ForeignMod(fm) => {
-                debug_assert_eq!(ident.name, kw::Empty);
-                hir::ItemKind::ForeignMod {
-                    abi: fm.abi.map_or(ExternAbi::FALLBACK, |abi| self.lower_abi(abi)),
-                    items: self
-                        .arena
-                        .alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))),
-                }
-            }
+            ItemKind::ForeignMod(fm) => hir::ItemKind::ForeignMod {
+                abi: fm.abi.map_or(ExternAbi::FALLBACK, |abi| self.lower_abi(abi)),
+                items: self
+                    .arena
+                    .alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))),
+            },
             ItemKind::GlobalAsm(asm) => {
-                debug_assert_eq!(ident.name, kw::Empty);
                 let asm = self.lower_inline_asm(span, asm);
                 let fake_body =
                     self.lower_body(|this| (&[], this.expr(span, hir::ExprKind::InlineAsm(asm))));
                 hir::ItemKind::GlobalAsm { asm, fake_body }
             }
-            ItemKind::TyAlias(box TyAlias { generics, where_clauses, ty, .. }) => {
-                debug_assert_ne!(ident.name, kw::Empty);
+            ItemKind::TyAlias(box TyAlias { ident, generics, where_clauses, ty, .. }) => {
                 // We lower
                 //
                 // type Foo = impl Trait
@@ -294,7 +290,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 //
                 // type Foo = Foo1
                 // opaque type Foo1: Trait
-                let ident = self.lower_ident(ident);
+                let ident = self.lower_ident(*ident);
                 let mut generics = generics.clone();
                 add_ty_alias_where_clause(&mut generics, *where_clauses, true);
                 let (generics, ty) = self.lower_generics(
@@ -322,9 +318,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 );
                 hir::ItemKind::TyAlias(ident, ty, generics)
             }
-            ItemKind::Enum(enum_definition, generics) => {
-                debug_assert_ne!(ident.name, kw::Empty);
-                let ident = self.lower_ident(ident);
+            ItemKind::Enum(ident, enum_definition, generics) => {
+                let ident = self.lower_ident(*ident);
                 let (generics, variants) = self.lower_generics(
                     generics,
                     id,
@@ -337,9 +332,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 );
                 hir::ItemKind::Enum(ident, hir::EnumDef { variants }, generics)
             }
-            ItemKind::Struct(struct_def, generics) => {
-                debug_assert_ne!(ident.name, kw::Empty);
-                let ident = self.lower_ident(ident);
+            ItemKind::Struct(ident, struct_def, generics) => {
+                let ident = self.lower_ident(*ident);
                 let (generics, struct_def) = self.lower_generics(
                     generics,
                     id,
@@ -348,9 +342,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 );
                 hir::ItemKind::Struct(ident, struct_def, generics)
             }
-            ItemKind::Union(vdata, generics) => {
-                debug_assert_ne!(ident.name, kw::Empty);
-                let ident = self.lower_ident(ident);
+            ItemKind::Union(ident, vdata, generics) => {
+                let ident = self.lower_ident(*ident);
                 let (generics, vdata) = self.lower_generics(
                     generics,
                     id,
@@ -369,7 +362,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 self_ty: ty,
                 items: impl_items,
             }) => {
-                debug_assert_eq!(ident.name, kw::Empty);
                 // Lower the "impl header" first. This ordering is important
                 // for in-band lifetimes! Consider `'a` here:
                 //
@@ -435,9 +427,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     items: new_impl_items,
                 }))
             }
-            ItemKind::Trait(box Trait { is_auto, safety, generics, bounds, items }) => {
-                debug_assert_ne!(ident.name, kw::Empty);
-                let ident = self.lower_ident(ident);
+            ItemKind::Trait(box Trait { is_auto, safety, ident, generics, bounds, items }) => {
+                let ident = self.lower_ident(*ident);
                 let (generics, (safety, items, bounds)) = self.lower_generics(
                     generics,
                     id,
@@ -456,9 +447,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 );
                 hir::ItemKind::Trait(*is_auto, safety, ident, generics, bounds, items)
             }
-            ItemKind::TraitAlias(generics, bounds) => {
-                debug_assert_ne!(ident.name, kw::Empty);
-                let ident = self.lower_ident(ident);
+            ItemKind::TraitAlias(ident, generics, bounds) => {
+                let ident = self.lower_ident(*ident);
                 let (generics, bounds) = self.lower_generics(
                     generics,
                     id,
@@ -472,9 +462,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 );
                 hir::ItemKind::TraitAlias(ident, generics, bounds)
             }
-            ItemKind::MacroDef(MacroDef { body, macro_rules }) => {
-                debug_assert_ne!(ident.name, kw::Empty);
-                let ident = self.lower_ident(ident);
+            ItemKind::MacroDef(ident, MacroDef { body, macro_rules }) => {
+                let ident = self.lower_ident(*ident);
                 let body = P(self.lower_delim_args(body));
                 let def_id = self.local_def_id(id);
                 let def_kind = self.tcx.def_kind(def_id);
@@ -488,11 +477,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 hir::ItemKind::Macro(ident, macro_def, macro_kind)
             }
             ItemKind::Delegation(box delegation) => {
-                debug_assert_ne!(ident.name, kw::Empty);
-                let ident = self.lower_ident(ident);
                 let delegation_results = self.lower_delegation(delegation, id, false);
                 hir::ItemKind::Fn {
-                    ident,
+                    ident: delegation_results.ident,
                     sig: delegation_results.sig,
                     generics: delegation_results.generics,
                     body: delegation_results.body_id,
@@ -649,61 +636,61 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
         let owner_id = hir_id.expect_owner();
         let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);
-        let item = hir::ForeignItem {
-            owner_id,
-            ident: self.lower_ident(i.ident),
-            kind: match &i.kind {
-                ForeignItemKind::Fn(box Fn { sig, generics, define_opaque, .. }) => {
-                    let fdec = &sig.decl;
-                    let itctx = ImplTraitContext::Universal;
-                    let (generics, (decl, fn_args)) =
-                        self.lower_generics(generics, i.id, itctx, |this| {
-                            (
-                                // Disallow `impl Trait` in foreign items.
-                                this.lower_fn_decl(
-                                    fdec,
-                                    i.id,
-                                    sig.span,
-                                    FnDeclKind::ExternFn,
-                                    None,
-                                ),
-                                this.lower_fn_params_to_names(fdec),
-                            )
-                        });
+        let (ident, kind) = match &i.kind {
+            ForeignItemKind::Fn(box Fn { sig, ident, generics, define_opaque, .. }) => {
+                let fdec = &sig.decl;
+                let itctx = ImplTraitContext::Universal;
+                let (generics, (decl, fn_args)) =
+                    self.lower_generics(generics, i.id, itctx, |this| {
+                        (
+                            // Disallow `impl Trait` in foreign items.
+                            this.lower_fn_decl(fdec, i.id, sig.span, FnDeclKind::ExternFn, None),
+                            this.lower_fn_params_to_names(fdec),
+                        )
+                    });
 
-                    // Unmarked safety in unsafe block defaults to unsafe.
-                    let header = self.lower_fn_header(sig.header, hir::Safety::Unsafe, attrs);
+                // Unmarked safety in unsafe block defaults to unsafe.
+                let header = self.lower_fn_header(sig.header, hir::Safety::Unsafe, attrs);
 
-                    if define_opaque.is_some() {
-                        self.dcx().span_err(i.span, "foreign functions cannot define opaque types");
-                    }
+                if define_opaque.is_some() {
+                    self.dcx().span_err(i.span, "foreign functions cannot define opaque types");
+                }
 
+                (
+                    ident,
                     hir::ForeignItemKind::Fn(
                         hir::FnSig { header, decl, span: self.lower_span(sig.span) },
                         fn_args,
                         generics,
-                    )
+                    ),
+                )
+            }
+            ForeignItemKind::Static(box StaticItem {
+                ident,
+                ty,
+                mutability,
+                expr: _,
+                safety,
+                define_opaque,
+            }) => {
+                let ty =
+                    self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy));
+                let safety = self.lower_safety(*safety, hir::Safety::Unsafe);
+                if define_opaque.is_some() {
+                    self.dcx().span_err(i.span, "foreign statics cannot define opaque types");
                 }
-                ForeignItemKind::Static(box StaticItem {
-                    ty,
-                    mutability,
-                    expr: _,
-                    safety,
-                    define_opaque,
-                }) => {
-                    let ty = self
-                        .lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy));
-                    let safety = self.lower_safety(*safety, hir::Safety::Unsafe);
-
-                    if define_opaque.is_some() {
-                        self.dcx().span_err(i.span, "foreign statics cannot define opaque types");
-                    }
+                (ident, hir::ForeignItemKind::Static(ty, *mutability, safety))
+            }
+            ForeignItemKind::TyAlias(box TyAlias { ident, .. }) => {
+                (ident, hir::ForeignItemKind::Type)
+            }
+            ForeignItemKind::MacCall(_) => panic!("macro shouldn't exist here"),
+        };
 
-                    hir::ForeignItemKind::Static(ty, *mutability, safety)
-                }
-                ForeignItemKind::TyAlias(..) => hir::ForeignItemKind::Type,
-                ForeignItemKind::MacCall(_) => panic!("macro shouldn't exist here"),
-            },
+        let item = hir::ForeignItem {
+            owner_id,
+            ident: self.lower_ident(*ident),
+            kind,
             vis_span: self.lower_span(i.vis.span),
             span: self.lower_span(i.span),
         };
@@ -713,7 +700,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
     fn lower_foreign_item_ref(&mut self, i: &ForeignItem) -> hir::ForeignItemRef {
         hir::ForeignItemRef {
             id: hir::ForeignItemId { owner_id: self.owner_id(i.id) },
-            ident: self.lower_ident(i.ident),
+            // `unwrap` is safe because `ForeignItemKind::MacCall` is the only foreign item kind
+            // without an identifier and it cannot reach here.
+            ident: self.lower_ident(i.kind.ident().unwrap()),
             span: self.lower_span(i.span),
         }
     }
@@ -800,13 +789,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
     }
 
     fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> {
-        debug_assert_ne!(i.ident.name, kw::Empty);
         let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
         let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);
         let trait_item_def_id = hir_id.expect_owner();
 
-        let (generics, kind, has_default) = match &i.kind {
-            AssocItemKind::Const(box ConstItem { generics, ty, expr, define_opaque, .. }) => {
+        let (ident, generics, kind, has_default) = match &i.kind {
+            AssocItemKind::Const(box ConstItem {
+                ident,
+                generics,
+                ty,
+                expr,
+                define_opaque,
+                ..
+            }) => {
                 let (generics, kind) = self.lower_generics(
                     generics,
                     i.id,
@@ -831,9 +826,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     }
                 }
 
-                (generics, kind, expr.is_some())
+                (*ident, generics, kind, expr.is_some())
             }
-            AssocItemKind::Fn(box Fn { sig, generics, body: None, define_opaque, .. }) => {
+            AssocItemKind::Fn(box Fn {
+                sig, ident, generics, body: None, define_opaque, ..
+            }) => {
                 // FIXME(contracts): Deny contract here since it won't apply to
                 // any impl method or callees.
                 let names = self.lower_fn_params_to_names(&sig.decl);
@@ -851,10 +848,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         "only trait methods with default bodies can define opaque types",
                     );
                 }
-                (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
+                (
+                    *ident,
+                    generics,
+                    hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)),
+                    false,
+                )
             }
             AssocItemKind::Fn(box Fn {
                 sig,
+                ident,
                 generics,
                 body: Some(body),
                 contract,
@@ -880,9 +883,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     attrs,
                 );
                 self.lower_define_opaque(hir_id, &define_opaque);
-                (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true)
+                (
+                    *ident,
+                    generics,
+                    hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)),
+                    true,
+                )
             }
-            AssocItemKind::Type(box TyAlias { generics, where_clauses, bounds, ty, .. }) => {
+            AssocItemKind::Type(box TyAlias {
+                ident, generics, where_clauses, bounds, ty, ..
+            }) => {
                 let mut generics = generics.clone();
                 add_ty_alias_where_clause(&mut generics, *where_clauses, false);
                 let (generics, kind) = self.lower_generics(
@@ -905,7 +915,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         )
                     },
                 );
-                (generics, kind, ty.is_some())
+                (*ident, generics, kind, ty.is_some())
             }
             AssocItemKind::Delegation(box delegation) => {
                 let delegation_results = self.lower_delegation(delegation, i.id, false);
@@ -913,7 +923,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     delegation_results.sig,
                     hir::TraitFn::Provided(delegation_results.body_id),
                 );
-                (delegation_results.generics, item_kind, true)
+                (delegation.ident, delegation_results.generics, item_kind, true)
             }
             AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
                 panic!("macros should have been expanded by now")
@@ -922,7 +932,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
         let item = hir::TraitItem {
             owner_id: trait_item_def_id,
-            ident: self.lower_ident(i.ident),
+            ident: self.lower_ident(ident),
             generics,
             kind,
             span: self.lower_span(i.span),
@@ -932,15 +942,20 @@ impl<'hir> LoweringContext<'_, 'hir> {
     }
 
     fn lower_trait_item_ref(&mut self, i: &AssocItem) -> hir::TraitItemRef {
-        let kind = match &i.kind {
-            AssocItemKind::Const(..) => hir::AssocItemKind::Const,
-            AssocItemKind::Type(..) => hir::AssocItemKind::Type,
-            AssocItemKind::Fn(box Fn { sig, .. }) => {
-                hir::AssocItemKind::Fn { has_self: sig.decl.has_self() }
+        let (ident, kind) = match &i.kind {
+            AssocItemKind::Const(box ConstItem { ident, .. }) => {
+                (*ident, hir::AssocItemKind::Const)
             }
-            AssocItemKind::Delegation(box delegation) => hir::AssocItemKind::Fn {
-                has_self: self.delegatee_is_method(i.id, delegation.id, i.span, false),
-            },
+            AssocItemKind::Type(box TyAlias { ident, .. }) => (*ident, hir::AssocItemKind::Type),
+            AssocItemKind::Fn(box Fn { ident, sig, .. }) => {
+                (*ident, hir::AssocItemKind::Fn { has_self: sig.decl.has_self() })
+            }
+            AssocItemKind::Delegation(box delegation) => (
+                delegation.ident,
+                hir::AssocItemKind::Fn {
+                    has_self: self.delegatee_is_method(i.id, delegation.id, i.span, false),
+                },
+            ),
             AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
                 panic!("macros should have been expanded by now")
             }
@@ -948,7 +963,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let id = hir::TraitItemId { owner_id: self.owner_id(i.id) };
         hir::TraitItemRef {
             id,
-            ident: self.lower_ident(i.ident),
+            ident: self.lower_ident(ident),
             span: self.lower_span(i.span),
             kind,
         }
@@ -964,16 +979,23 @@ impl<'hir> LoweringContext<'_, 'hir> {
         i: &AssocItem,
         is_in_trait_impl: bool,
     ) -> &'hir hir::ImplItem<'hir> {
-        debug_assert_ne!(i.ident.name, kw::Empty);
         // Since `default impl` is not yet implemented, this is always true in impls.
         let has_value = true;
         let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value);
         let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
         let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);
 
-        let (generics, kind) = match &i.kind {
-            AssocItemKind::Const(box ConstItem { generics, ty, expr, define_opaque, .. }) => self
-                .lower_generics(
+        let (ident, (generics, kind)) = match &i.kind {
+            AssocItemKind::Const(box ConstItem {
+                ident,
+                generics,
+                ty,
+                expr,
+                define_opaque,
+                ..
+            }) => (
+                *ident,
+                self.lower_generics(
                     generics,
                     i.id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
@@ -982,11 +1004,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
                             .lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy));
                         let body = this.lower_const_body(i.span, expr.as_deref());
                         this.lower_define_opaque(hir_id, &define_opaque);
-
                         hir::ImplItemKind::Const(ty, body)
                     },
                 ),
-            AssocItemKind::Fn(box Fn { sig, generics, body, contract, define_opaque, .. }) => {
+            ),
+            AssocItemKind::Fn(box Fn {
+                sig,
+                ident,
+                generics,
+                body,
+                contract,
+                define_opaque,
+                ..
+            }) => {
                 let body_id = self.lower_maybe_coroutine_body(
                     sig.span,
                     i.span,
@@ -1007,44 +1037,50 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 );
                 self.lower_define_opaque(hir_id, &define_opaque);
 
-                (generics, hir::ImplItemKind::Fn(sig, body_id))
+                (*ident, (generics, hir::ImplItemKind::Fn(sig, body_id)))
             }
-            AssocItemKind::Type(box TyAlias { generics, where_clauses, ty, .. }) => {
+            AssocItemKind::Type(box TyAlias { ident, generics, where_clauses, ty, .. }) => {
                 let mut generics = generics.clone();
                 add_ty_alias_where_clause(&mut generics, *where_clauses, false);
-                self.lower_generics(
-                    &generics,
-                    i.id,
-                    ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
-                    |this| match ty {
-                        None => {
-                            let guar = this.dcx().span_delayed_bug(
-                                i.span,
-                                "expected to lower associated type, but it was missing",
-                            );
-                            let ty = this.arena.alloc(this.ty(i.span, hir::TyKind::Err(guar)));
-                            hir::ImplItemKind::Type(ty)
-                        }
-                        Some(ty) => {
-                            let ty = this.lower_ty(
-                                ty,
-                                ImplTraitContext::OpaqueTy {
-                                    origin: hir::OpaqueTyOrigin::TyAlias {
-                                        parent: this.local_def_id(i.id),
-                                        in_assoc_ty: true,
+                (
+                    *ident,
+                    self.lower_generics(
+                        &generics,
+                        i.id,
+                        ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
+                        |this| match ty {
+                            None => {
+                                let guar = this.dcx().span_delayed_bug(
+                                    i.span,
+                                    "expected to lower associated type, but it was missing",
+                                );
+                                let ty = this.arena.alloc(this.ty(i.span, hir::TyKind::Err(guar)));
+                                hir::ImplItemKind::Type(ty)
+                            }
+                            Some(ty) => {
+                                let ty = this.lower_ty(
+                                    ty,
+                                    ImplTraitContext::OpaqueTy {
+                                        origin: hir::OpaqueTyOrigin::TyAlias {
+                                            parent: this.local_def_id(i.id),
+                                            in_assoc_ty: true,
+                                        },
                                     },
-                                },
-                            );
-                            hir::ImplItemKind::Type(ty)
-                        }
-                    },
+                                );
+                                hir::ImplItemKind::Type(ty)
+                            }
+                        },
+                    ),
                 )
             }
             AssocItemKind::Delegation(box delegation) => {
                 let delegation_results = self.lower_delegation(delegation, i.id, is_in_trait_impl);
                 (
-                    delegation_results.generics,
-                    hir::ImplItemKind::Fn(delegation_results.sig, delegation_results.body_id),
+                    delegation.ident,
+                    (
+                        delegation_results.generics,
+                        hir::ImplItemKind::Fn(delegation_results.sig, delegation_results.body_id),
+                    ),
                 )
             }
             AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
@@ -1054,7 +1090,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
         let item = hir::ImplItem {
             owner_id: hir_id.expect_owner(),
-            ident: self.lower_ident(i.ident),
+            ident: self.lower_ident(ident),
             generics,
             kind,
             vis_span: self.lower_span(i.vis.span),
@@ -1067,7 +1103,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
     fn lower_impl_item_ref(&mut self, i: &AssocItem, is_in_trait_impl: bool) -> hir::ImplItemRef {
         hir::ImplItemRef {
             id: hir::ImplItemId { owner_id: self.owner_id(i.id) },
-            ident: self.lower_ident(i.ident),
+            // `unwrap` is safe because `AssocItemKind::{MacCall,DelegationMac}` are the only
+            // assoc item kinds without an identifier and they cannot reach here.
+            ident: self.lower_ident(i.kind.ident().unwrap()),
             span: self.lower_span(i.span),
             kind: match &i.kind {
                 AssocItemKind::Const(..) => hir::AssocItemKind::Const,
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index da739b0e453..9916de8b7b1 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -47,14 +47,14 @@ enum SelfSemantic {
 }
 
 enum TraitOrTraitImpl {
-    Trait { span: Span, constness: Option<Span> },
-    TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref: Span },
+    Trait { span: Span, constness_span: Option<Span> },
+    TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref_span: Span },
 }
 
 impl TraitOrTraitImpl {
     fn constness(&self) -> Option<Span> {
         match self {
-            Self::Trait { constness: Some(span), .. }
+            Self::Trait { constness_span: Some(span), .. }
             | Self::TraitImpl { constness: Const::Yes(span), .. } => Some(*span),
             _ => None,
         }
@@ -66,7 +66,7 @@ struct AstValidator<'a> {
     features: &'a Features,
 
     /// The span of the `extern` in an `extern { ... }` block, if any.
-    extern_mod: Option<Span>,
+    extern_mod_span: Option<Span>,
 
     outer_trait_or_trait_impl: Option<TraitOrTraitImpl>,
 
@@ -75,7 +75,7 @@ struct AstValidator<'a> {
     /// Used to ban nested `impl Trait`, e.g., `impl Into<impl Debug>`.
     /// Nested `impl Trait` _is_ allowed in associated type position,
     /// e.g., `impl Iterator<Item = impl Debug>`.
-    outer_impl_trait: Option<Span>,
+    outer_impl_trait_span: Option<Span>,
 
     disallow_tilde_const: Option<TildeConstReason>,
 
@@ -96,17 +96,22 @@ impl<'a> AstValidator<'a> {
             trait_.map(|(constness, polarity, trait_ref)| TraitOrTraitImpl::TraitImpl {
                 constness,
                 polarity,
-                trait_ref: trait_ref.path.span,
+                trait_ref_span: trait_ref.path.span,
             }),
         );
         f(self);
         self.outer_trait_or_trait_impl = old;
     }
 
-    fn with_in_trait(&mut self, span: Span, constness: Option<Span>, f: impl FnOnce(&mut Self)) {
+    fn with_in_trait(
+        &mut self,
+        span: Span,
+        constness_span: Option<Span>,
+        f: impl FnOnce(&mut Self),
+    ) {
         let old = mem::replace(
             &mut self.outer_trait_or_trait_impl,
-            Some(TraitOrTraitImpl::Trait { span, constness }),
+            Some(TraitOrTraitImpl::Trait { span, constness_span }),
         );
         f(self);
         self.outer_trait_or_trait_impl = old;
@@ -170,10 +175,10 @@ impl<'a> AstValidator<'a> {
         Err(errors::WhereClauseBeforeTypeAlias { span, sugg })
     }
 
-    fn with_impl_trait(&mut self, outer: Option<Span>, f: impl FnOnce(&mut Self)) {
-        let old = mem::replace(&mut self.outer_impl_trait, outer);
+    fn with_impl_trait(&mut self, outer_span: Option<Span>, f: impl FnOnce(&mut Self)) {
+        let old = mem::replace(&mut self.outer_impl_trait_span, outer_span);
         f(self);
-        self.outer_impl_trait = old;
+        self.outer_impl_trait_span = old;
     }
 
     // Mirrors `visit::walk_ty`, but tracks relevant state.
@@ -258,21 +263,22 @@ impl<'a> AstValidator<'a> {
             && let TraitOrTraitImpl::TraitImpl {
                 constness: Const::No,
                 polarity: ImplPolarity::Positive,
-                trait_ref,
+                trait_ref_span,
                 ..
             } = parent
         {
-            Some(trait_ref.shrink_to_lo())
+            Some(trait_ref_span.shrink_to_lo())
         } else {
             None
         };
 
-        let make_trait_const_sugg =
-            if const_trait_impl && let TraitOrTraitImpl::Trait { span, constness: None } = parent {
-                Some(span.shrink_to_lo())
-            } else {
-                None
-            };
+        let make_trait_const_sugg = if const_trait_impl
+            && let TraitOrTraitImpl::Trait { span, constness_span: None } = parent
+        {
+            Some(span.shrink_to_lo())
+        } else {
+            None
+        };
 
         let parent_constness = parent.constness();
         self.dcx().emit_err(errors::TraitFnConst {
@@ -448,13 +454,13 @@ impl<'a> AstValidator<'a> {
         check_where_clause(where_clauses.after);
     }
 
-    fn check_foreign_kind_bodyless(&self, ident: Ident, kind: &str, body: Option<Span>) {
-        let Some(body) = body else {
+    fn check_foreign_kind_bodyless(&self, ident: Ident, kind: &str, body_span: Option<Span>) {
+        let Some(body_span) = body_span else {
             return;
         };
         self.dcx().emit_err(errors::BodyInExtern {
             span: ident.span,
-            body,
+            body: body_span,
             block: self.current_extern_span(),
             kind,
         });
@@ -473,7 +479,7 @@ impl<'a> AstValidator<'a> {
     }
 
     fn current_extern_span(&self) -> Span {
-        self.sess.source_map().guess_head_span(self.extern_mod.unwrap())
+        self.sess.source_map().guess_head_span(self.extern_mod_span.unwrap())
     }
 
     /// An `fn` in `extern { ... }` cannot have qualifiers, e.g. `async fn`.
@@ -583,9 +589,10 @@ impl<'a> AstValidator<'a> {
         self.dcx().emit_err(errors::ModuleNonAscii { span: ident.span, name: ident.name });
     }
 
-    fn deny_generic_params(&self, generics: &Generics, ident: Span) {
+    fn deny_generic_params(&self, generics: &Generics, ident_span: Span) {
         if !generics.params.is_empty() {
-            self.dcx().emit_err(errors::AutoTraitGeneric { span: generics.span, ident });
+            self.dcx()
+                .emit_err(errors::AutoTraitGeneric { span: generics.span, ident: ident_span });
         }
     }
 
@@ -605,11 +612,11 @@ impl<'a> AstValidator<'a> {
         }
     }
 
-    fn deny_items(&self, trait_items: &[P<AssocItem>], ident: Span) {
+    fn deny_items(&self, trait_items: &[P<AssocItem>], ident_span: Span) {
         if !trait_items.is_empty() {
-            let spans: Vec<_> = trait_items.iter().map(|i| i.ident.span).collect();
+            let spans: Vec<_> = trait_items.iter().map(|i| i.kind.ident().unwrap().span).collect();
             let total = trait_items.first().unwrap().span.to(trait_items.last().unwrap().span);
-            self.dcx().emit_err(errors::AutoTraitItems { spans, total, ident });
+            self.dcx().emit_err(errors::AutoTraitItems { spans, total, ident: ident_span });
         }
     }
 
@@ -694,7 +701,7 @@ impl<'a> AstValidator<'a> {
                 }
             }
             TyKind::ImplTrait(_, bounds) => {
-                if let Some(outer_impl_trait_sp) = self.outer_impl_trait {
+                if let Some(outer_impl_trait_sp) = self.outer_impl_trait_span {
                     self.dcx().emit_err(errors::NestedImplTrait {
                         span: ty.span,
                         outer: outer_impl_trait_sp,
@@ -727,6 +734,19 @@ impl<'a> AstValidator<'a> {
             )
         }
     }
+
+    // Used within `visit_item` for item kinds where we don't call `visit::walk_item`.
+    fn visit_attrs_vis(&mut self, attrs: &'a AttrVec, vis: &'a Visibility) {
+        walk_list!(self, visit_attribute, attrs);
+        self.visit_vis(vis);
+    }
+
+    // Used within `visit_item` for item kinds where we don't call `visit::walk_item`.
+    fn visit_attrs_vis_ident(&mut self, attrs: &'a AttrVec, vis: &'a Visibility, ident: &'a Ident) {
+        walk_list!(self, visit_attribute, attrs);
+        self.visit_vis(vis);
+        self.visit_ident(ident);
+    }
 }
 
 /// Checks that generic parameters are in the correct order,
@@ -817,8 +837,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             self.has_proc_macro_decls = true;
         }
 
-        if attr::contains_name(&item.attrs, sym::no_mangle) {
-            self.check_nomangle_item_asciionly(item.ident, item.span);
+        if let Some(ident) = item.kind.ident()
+            && attr::contains_name(&item.attrs, sym::no_mangle)
+        {
+            self.check_nomangle_item_asciionly(ident, item.span);
         }
 
         match &item.kind {
@@ -832,37 +854,33 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 self_ty,
                 items,
             }) => {
-                self.with_in_trait_impl(Some((*constness, *polarity, t)), |this| {
-                    this.visibility_not_permitted(
-                        &item.vis,
-                        errors::VisibilityNotPermittedNote::TraitImpl,
-                    );
-                    if let TyKind::Dummy = self_ty.kind {
-                        // Abort immediately otherwise the `TyKind::Dummy` will reach HIR lowering,
-                        // which isn't allowed. Not a problem for this obscure, obsolete syntax.
-                        this.dcx().emit_fatal(errors::ObsoleteAuto { span: item.span });
-                    }
-                    if let (&Safety::Unsafe(span), &ImplPolarity::Negative(sp)) = (safety, polarity)
-                    {
-                        this.dcx().emit_err(errors::UnsafeNegativeImpl {
-                            span: sp.to(t.path.span),
-                            negative: sp,
-                            r#unsafe: span,
-                        });
-                    }
+                self.visit_attrs_vis(&item.attrs, &item.vis);
+                self.visibility_not_permitted(
+                    &item.vis,
+                    errors::VisibilityNotPermittedNote::TraitImpl,
+                );
+                if let TyKind::Dummy = self_ty.kind {
+                    // Abort immediately otherwise the `TyKind::Dummy` will reach HIR lowering,
+                    // which isn't allowed. Not a problem for this obscure, obsolete syntax.
+                    self.dcx().emit_fatal(errors::ObsoleteAuto { span: item.span });
+                }
+                if let (&Safety::Unsafe(span), &ImplPolarity::Negative(sp)) = (safety, polarity) {
+                    self.dcx().emit_err(errors::UnsafeNegativeImpl {
+                        span: sp.to(t.path.span),
+                        negative: sp,
+                        r#unsafe: span,
+                    });
+                }
 
-                    this.visit_vis(&item.vis);
-                    this.visit_ident(&item.ident);
-                    let disallowed = matches!(constness, Const::No)
-                        .then(|| TildeConstReason::TraitImpl { span: item.span });
-                    this.with_tilde_const(disallowed, |this| this.visit_generics(generics));
-                    this.visit_trait_ref(t);
-                    this.visit_ty(self_ty);
+                let disallowed = matches!(constness, Const::No)
+                    .then(|| TildeConstReason::TraitImpl { span: item.span });
+                self.with_tilde_const(disallowed, |this| this.visit_generics(generics));
+                self.visit_trait_ref(t);
+                self.visit_ty(self_ty);
 
+                self.with_in_trait_impl(Some((*constness, *polarity, t)), |this| {
                     walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl { of_trait: true });
                 });
-                walk_list!(self, visit_attribute, &item.attrs);
-                return; // Avoid visiting again.
             }
             ItemKind::Impl(box Impl {
                 safety,
@@ -882,45 +900,49 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     only_trait,
                 };
 
-                self.with_in_trait_impl(None, |this| {
-                    this.visibility_not_permitted(
-                        &item.vis,
-                        errors::VisibilityNotPermittedNote::IndividualImplItems,
-                    );
-                    if let &Safety::Unsafe(span) = safety {
-                        this.dcx().emit_err(errors::InherentImplCannotUnsafe {
-                            span: self_ty.span,
-                            annotation_span: span,
-                            annotation: "unsafe",
-                            self_ty: self_ty.span,
-                        });
-                    }
-                    if let &ImplPolarity::Negative(span) = polarity {
-                        this.dcx().emit_err(error(span, "negative", false));
-                    }
-                    if let &Defaultness::Default(def_span) = defaultness {
-                        this.dcx().emit_err(error(def_span, "`default`", true));
-                    }
-                    if let &Const::Yes(span) = constness {
-                        this.dcx().emit_err(error(span, "`const`", true));
-                    }
+                self.visit_attrs_vis(&item.attrs, &item.vis);
+                self.visibility_not_permitted(
+                    &item.vis,
+                    errors::VisibilityNotPermittedNote::IndividualImplItems,
+                );
+                if let &Safety::Unsafe(span) = safety {
+                    self.dcx().emit_err(errors::InherentImplCannotUnsafe {
+                        span: self_ty.span,
+                        annotation_span: span,
+                        annotation: "unsafe",
+                        self_ty: self_ty.span,
+                    });
+                }
+                if let &ImplPolarity::Negative(span) = polarity {
+                    self.dcx().emit_err(error(span, "negative", false));
+                }
+                if let &Defaultness::Default(def_span) = defaultness {
+                    self.dcx().emit_err(error(def_span, "`default`", true));
+                }
+                if let &Const::Yes(span) = constness {
+                    self.dcx().emit_err(error(span, "`const`", true));
+                }
 
-                    this.visit_vis(&item.vis);
-                    this.visit_ident(&item.ident);
-                    this.with_tilde_const(
-                        Some(TildeConstReason::Impl { span: item.span }),
-                        |this| this.visit_generics(generics),
-                    );
-                    this.visit_ty(self_ty);
+                self.with_tilde_const(Some(TildeConstReason::Impl { span: item.span }), |this| {
+                    this.visit_generics(generics)
+                });
+                self.visit_ty(self_ty);
+                self.with_in_trait_impl(None, |this| {
                     walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl { of_trait: false });
                 });
-                walk_list!(self, visit_attribute, &item.attrs);
-                return; // Avoid visiting again.
             }
             ItemKind::Fn(
-                func
-                @ box Fn { defaultness, generics: _, sig, contract: _, body, define_opaque: _ },
+                func @ box Fn {
+                    defaultness,
+                    ident,
+                    generics: _,
+                    sig,
+                    contract: _,
+                    body,
+                    define_opaque: _,
+                },
             ) => {
+                self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
                 self.check_defaultness(item.span, *defaultness);
 
                 let is_intrinsic =
@@ -948,45 +970,40 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     });
                 }
 
-                self.visit_vis(&item.vis);
-                self.visit_ident(&item.ident);
-                let kind = FnKind::Fn(FnCtxt::Free, &item.ident, &item.vis, &*func);
+                let kind = FnKind::Fn(FnCtxt::Free, &item.vis, &*func);
                 self.visit_fn(kind, item.span, item.id);
-                walk_list!(self, visit_attribute, &item.attrs);
-                return; // Avoid visiting again.
             }
             ItemKind::ForeignMod(ForeignMod { extern_span, abi, safety, .. }) => {
-                self.with_in_extern_mod(*safety, |this| {
-                    let old_item = mem::replace(&mut this.extern_mod, Some(item.span));
-                    this.visibility_not_permitted(
-                        &item.vis,
-                        errors::VisibilityNotPermittedNote::IndividualForeignItems,
-                    );
-
-                    if &Safety::Default == safety {
-                        if item.span.at_least_rust_2024() {
-                            this.dcx().emit_err(errors::MissingUnsafeOnExtern { span: item.span });
-                        } else {
-                            this.lint_buffer.buffer_lint(
-                                MISSING_UNSAFE_ON_EXTERN,
-                                item.id,
-                                item.span,
-                                BuiltinLintDiag::MissingUnsafeOnExtern {
-                                    suggestion: item.span.shrink_to_lo(),
-                                },
-                            );
-                        }
+                let old_item = mem::replace(&mut self.extern_mod_span, Some(item.span));
+                self.visibility_not_permitted(
+                    &item.vis,
+                    errors::VisibilityNotPermittedNote::IndividualForeignItems,
+                );
+
+                if &Safety::Default == safety {
+                    if item.span.at_least_rust_2024() {
+                        self.dcx().emit_err(errors::MissingUnsafeOnExtern { span: item.span });
+                    } else {
+                        self.lint_buffer.buffer_lint(
+                            MISSING_UNSAFE_ON_EXTERN,
+                            item.id,
+                            item.span,
+                            BuiltinLintDiag::MissingUnsafeOnExtern {
+                                suggestion: item.span.shrink_to_lo(),
+                            },
+                        );
                     }
+                }
 
-                    if abi.is_none() {
-                        this.maybe_lint_missing_abi(*extern_span, item.id);
-                    }
+                if abi.is_none() {
+                    self.maybe_lint_missing_abi(*extern_span, item.id);
+                }
+                self.with_in_extern_mod(*safety, |this| {
                     visit::walk_item(this, item);
-                    this.extern_mod = old_item;
                 });
-                return; // Avoid visiting again.
+                self.extern_mod_span = old_item;
             }
-            ItemKind::Enum(def, _) => {
+            ItemKind::Enum(_, def, _) => {
                 for variant in &def.variants {
                     self.visibility_not_permitted(
                         &variant.vis,
@@ -999,36 +1016,33 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         );
                     }
                 }
+                visit::walk_item(self, item)
             }
-            ItemKind::Trait(box Trait { is_auto, generics, bounds, items, .. }) => {
+            ItemKind::Trait(box Trait { is_auto, generics, ident, bounds, items, .. }) => {
+                self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
                 let is_const_trait =
                     attr::find_by_name(&item.attrs, sym::const_trait).map(|attr| attr.span);
-                self.with_in_trait(item.span, is_const_trait, |this| {
-                    if *is_auto == IsAuto::Yes {
-                        // Auto traits cannot have generics, super traits nor contain items.
-                        this.deny_generic_params(generics, item.ident.span);
-                        this.deny_super_traits(bounds, item.ident.span);
-                        this.deny_where_clause(&generics.where_clause, item.ident.span);
-                        this.deny_items(items, item.ident.span);
-                    }
+                if *is_auto == IsAuto::Yes {
+                    // Auto traits cannot have generics, super traits nor contain items.
+                    self.deny_generic_params(generics, ident.span);
+                    self.deny_super_traits(bounds, ident.span);
+                    self.deny_where_clause(&generics.where_clause, ident.span);
+                    self.deny_items(items, ident.span);
+                }
 
-                    // Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound
-                    // context for the supertraits.
-                    this.visit_vis(&item.vis);
-                    this.visit_ident(&item.ident);
-                    let disallowed = is_const_trait
-                        .is_none()
-                        .then(|| TildeConstReason::Trait { span: item.span });
-                    this.with_tilde_const(disallowed, |this| {
-                        this.visit_generics(generics);
-                        walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
-                    });
+                // Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound
+                // context for the supertraits.
+                let disallowed =
+                    is_const_trait.is_none().then(|| TildeConstReason::Trait { span: item.span });
+                self.with_tilde_const(disallowed, |this| {
+                    this.visit_generics(generics);
+                    walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
+                });
+                self.with_in_trait(item.span, is_const_trait, |this| {
                     walk_list!(this, visit_assoc_item, items, AssocCtxt::Trait);
                 });
-                walk_list!(self, visit_attribute, &item.attrs);
-                return; // Avoid visiting again
             }
-            ItemKind::Mod(safety, mod_kind) => {
+            ItemKind::Mod(safety, ident, mod_kind) => {
                 if let &Safety::Unsafe(span) = safety {
                     self.dcx().emit_err(errors::UnsafeItem { span, kind: "module" });
                 }
@@ -1036,36 +1050,31 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _, _))
                     && !attr::contains_name(&item.attrs, sym::path)
                 {
-                    self.check_mod_file_item_asciionly(item.ident);
+                    self.check_mod_file_item_asciionly(*ident);
                 }
+                visit::walk_item(self, item)
             }
-            ItemKind::Struct(vdata, generics) => match vdata {
+            ItemKind::Struct(ident, vdata, generics) => match vdata {
                 VariantData::Struct { fields, .. } => {
-                    self.visit_vis(&item.vis);
-                    self.visit_ident(&item.ident);
+                    self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
                     self.visit_generics(generics);
                     // Permit `Anon{Struct,Union}` as field type.
                     walk_list!(self, visit_struct_field_def, fields);
-                    walk_list!(self, visit_attribute, &item.attrs);
-                    return;
                 }
-                _ => {}
+                _ => visit::walk_item(self, item),
             },
-            ItemKind::Union(vdata, generics) => {
+            ItemKind::Union(ident, vdata, generics) => {
                 if vdata.fields().is_empty() {
                     self.dcx().emit_err(errors::FieldlessUnion { span: item.span });
                 }
                 match vdata {
                     VariantData::Struct { fields, .. } => {
-                        self.visit_vis(&item.vis);
-                        self.visit_ident(&item.ident);
+                        self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
                         self.visit_generics(generics);
                         // Permit `Anon{Struct,Union}` as field type.
                         walk_list!(self, visit_struct_field_def, fields);
-                        walk_list!(self, visit_attribute, &item.attrs);
-                        return;
                     }
-                    _ => {}
+                    _ => visit::walk_item(self, item),
                 }
             }
             ItemKind::Const(box ConstItem { defaultness, expr, .. }) => {
@@ -1076,6 +1085,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         replace_span: self.ending_semi_or_hi(item.span),
                     });
                 }
+                visit::walk_item(self, item);
             }
             ItemKind::Static(box StaticItem { expr, safety, .. }) => {
                 self.check_item_safety(item.span, *safety);
@@ -1089,6 +1099,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         replace_span: self.ending_semi_or_hi(item.span),
                     });
                 }
+                visit::walk_item(self, item);
             }
             ItemKind::TyAlias(
                 ty_alias @ box TyAlias { defaultness, bounds, where_clauses, ty, .. },
@@ -1112,23 +1123,23 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         help: self.sess.is_nightly_build(),
                     });
                 }
+                visit::walk_item(self, item);
             }
-            _ => {}
+            _ => visit::walk_item(self, item),
         }
-
-        visit::walk_item(self, item);
     }
 
     fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
         match &fi.kind {
-            ForeignItemKind::Fn(box Fn { defaultness, sig, body, .. }) => {
+            ForeignItemKind::Fn(box Fn { defaultness, ident, sig, body, .. }) => {
                 self.check_defaultness(fi.span, *defaultness);
-                self.check_foreign_fn_bodyless(fi.ident, body.as_deref());
+                self.check_foreign_fn_bodyless(*ident, body.as_deref());
                 self.check_foreign_fn_headerless(sig.header);
-                self.check_foreign_item_ascii_only(fi.ident);
+                self.check_foreign_item_ascii_only(*ident);
             }
             ForeignItemKind::TyAlias(box TyAlias {
                 defaultness,
+                ident,
                 generics,
                 where_clauses,
                 bounds,
@@ -1136,15 +1147,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 ..
             }) => {
                 self.check_defaultness(fi.span, *defaultness);
-                self.check_foreign_kind_bodyless(fi.ident, "type", ty.as_ref().map(|b| b.span));
+                self.check_foreign_kind_bodyless(*ident, "type", ty.as_ref().map(|b| b.span));
                 self.check_type_no_bounds(bounds, "`extern` blocks");
                 self.check_foreign_ty_genericless(generics, where_clauses);
-                self.check_foreign_item_ascii_only(fi.ident);
+                self.check_foreign_item_ascii_only(*ident);
             }
-            ForeignItemKind::Static(box StaticItem { expr, safety, .. }) => {
+            ForeignItemKind::Static(box StaticItem { ident, safety, expr, .. }) => {
                 self.check_item_safety(fi.span, *safety);
-                self.check_foreign_kind_bodyless(fi.ident, "static", expr.as_ref().map(|b| b.span));
-                self.check_foreign_item_ascii_only(fi.ident);
+                self.check_foreign_kind_bodyless(*ident, "static", expr.as_ref().map(|b| b.span));
+                self.check_foreign_item_ascii_only(*ident);
             }
             ForeignItemKind::MacCall(..) => {}
         }
@@ -1353,7 +1364,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
         if let FnKind::Fn(
             _,
             _,
-            _,
             Fn {
                 sig: FnSig { header: FnHeader { ext: Extern::Implicit(extern_span), .. }, .. },
                 ..
@@ -1364,7 +1374,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
         }
 
         // Functions without bodies cannot have patterns.
-        if let FnKind::Fn(ctxt, _, _, Fn { body: None, sig, .. }) = fk {
+        if let FnKind::Fn(ctxt, _, Fn { body: None, sig, .. }) = fk {
             Self::check_decl_no_pat(&sig.decl, |span, ident, mut_ident| {
                 if mut_ident && matches!(ctxt, FnCtxt::Assoc(_)) {
                     if let Some(ident) = ident {
@@ -1398,15 +1408,17 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         .is_some();
 
         let disallowed = (!tilde_const_allowed).then(|| match fk {
-            FnKind::Fn(_, ident, _, _) => TildeConstReason::Function { ident: ident.span },
+            FnKind::Fn(_, _, f) => TildeConstReason::Function { ident: f.ident.span },
             FnKind::Closure(..) => TildeConstReason::Closure,
         });
         self.with_tilde_const(disallowed, |this| visit::walk_fn(this, fk));
     }
 
     fn visit_assoc_item(&mut self, item: &'a AssocItem, ctxt: AssocCtxt) {
-        if attr::contains_name(&item.attrs, sym::no_mangle) {
-            self.check_nomangle_item_asciionly(item.ident, item.span);
+        if let Some(ident) = item.kind.ident()
+            && attr::contains_name(&item.attrs, sym::no_mangle)
+        {
+            self.check_nomangle_item_asciionly(ident, item.span);
         }
 
         if ctxt == AssocCtxt::Trait || self.outer_trait_or_trait_impl.is_none() {
@@ -1466,8 +1478,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             }
         }
 
-        if let AssocItemKind::Const(..) = item.kind {
-            self.check_item_named(item.ident, "const");
+        if let AssocItemKind::Const(ci) = &item.kind {
+            self.check_item_named(ci.ident, "const");
         }
 
         let parent_is_const =
@@ -1479,10 +1491,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     || ctxt == AssocCtxt::Trait
                     || matches!(func.sig.header.constness, Const::Yes(_)) =>
             {
-                self.visit_vis(&item.vis);
-                self.visit_ident(&item.ident);
-                let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), &item.ident, &item.vis, &*func);
-                walk_list!(self, visit_attribute, &item.attrs);
+                self.visit_attrs_vis_ident(&item.attrs, &item.vis, &func.ident);
+                let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), &item.vis, &*func);
                 self.visit_fn(kind, item.span, item.id);
             }
             AssocItemKind::Type(_) => {
@@ -1587,7 +1597,7 @@ fn deny_equality_constraints(
                     generics.where_clause.span
                 } else {
                     let mut span = predicate_span;
-                    let mut prev: Option<Span> = None;
+                    let mut prev_span: Option<Span> = None;
                     let mut preds = generics.where_clause.predicates.iter().peekable();
                     // Find the predicate that shouldn't have been in the where bound list.
                     while let Some(pred) = preds.next() {
@@ -1597,12 +1607,12 @@ fn deny_equality_constraints(
                             if let Some(next) = preds.peek() {
                                 // This is the first predicate, remove the trailing comma as well.
                                 span = span.with_hi(next.span.lo());
-                            } else if let Some(prev) = prev {
+                            } else if let Some(prev_span) = prev_span {
                                 // Remove the previous comma as well.
-                                span = span.with_lo(prev.hi());
+                                span = span.with_lo(prev_span.hi());
                             }
                         }
-                        prev = Some(pred.span);
+                        prev_span = Some(pred.span);
                     }
                     span
                 };
@@ -1677,10 +1687,10 @@ pub fn check_crate(
     let mut validator = AstValidator {
         sess,
         features,
-        extern_mod: None,
+        extern_mod_span: None,
         outer_trait_or_trait_impl: None,
         has_proc_macro_decls: false,
-        outer_impl_trait: None,
+        outer_impl_trait_span: None,
         disallow_tilde_const: Some(TildeConstReason::Item),
         extern_mod_safety: None,
         lint_buffer: lints,
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 31ff102c127..e312f15f05b 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -99,6 +99,13 @@ impl<'a> PostExpansionVisitor<'a> {
                 }
                 visit::walk_ty(self, ty);
             }
+
+            fn visit_anon_const(&mut self, _: &ast::AnonConst) -> Self::Result {
+                // We don't walk the anon const because it crosses a conceptual boundary: We're no
+                // longer "inside" the original type.
+                // Brittle: We assume that the callers of `check_impl_trait` will later recurse into
+                // the items found in the AnonConst to look for nested TyAliases.
+            }
         }
         ImplTraitVisitor { vis: self, in_associated_ty }.visit_ty(ty);
     }
@@ -229,7 +236,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                 gate!(&self, trait_alias, i.span, "trait aliases are experimental");
             }
 
-            ast::ItemKind::MacroDef(ast::MacroDef { macro_rules: false, .. }) => {
+            ast::ItemKind::MacroDef(_, ast::MacroDef { macro_rules: false, .. }) => {
                 let msg = "`macro` is experimental";
                 gate!(&self, decl_macro, i.span, msg);
             }
@@ -325,17 +332,19 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
             ast::ExprKind::TryBlock(_) => {
                 gate!(&self, try_blocks, e.span, "`try` expression is experimental");
             }
-            ast::ExprKind::Lit(token::Lit { kind: token::LitKind::Float, suffix, .. }) => {
-                match suffix {
-                    Some(sym::f16) => {
-                        gate!(&self, f16, e.span, "the type `f16` is unstable")
-                    }
-                    Some(sym::f128) => {
-                        gate!(&self, f128, e.span, "the type `f128` is unstable")
-                    }
-                    _ => (),
+            ast::ExprKind::Lit(token::Lit {
+                kind: token::LitKind::Float | token::LitKind::Integer,
+                suffix,
+                ..
+            }) => match suffix {
+                Some(sym::f16) => {
+                    gate!(&self, f16, e.span, "the type `f16` is unstable")
                 }
-            }
+                Some(sym::f128) => {
+                    gate!(&self, f128, e.span, "the type `f128` is unstable")
+                }
+                _ => (),
+            },
             _ => {}
         }
         visit::walk_expr(self, e)
@@ -483,7 +492,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
         half_open_range_patterns_in_slices,
         "half-open range patterns in slices are unstable"
     );
-    gate_all!(inline_const_pat, "inline-const in pattern position is experimental");
     gate_all!(associated_const_equality, "associated const equality is incomplete");
     gate_all!(yeet_expr, "`do yeet` expression is experimental");
     gate_all!(dyn_star, "`dyn*` trait objects are experimental");
@@ -505,6 +513,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
     gate_all!(contracts, "contracts are incomplete");
     gate_all!(contracts_internals, "contract internal machinery is for internal use only");
     gate_all!(where_clause_attrs, "attributes in `where` clause are unstable");
+    gate_all!(super_let, "`super let` is experimental");
 
     if !visitor.features.never_patterns() {
         if let Some(spans) = spans.get(&sym::never_patterns) {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index 7d9dc89bd75..df848a26d39 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -274,22 +274,22 @@ impl<'a> State<'a> {
 
     fn print_expr_binary(
         &mut self,
-        op: ast::BinOp,
+        op: ast::BinOpKind,
         lhs: &ast::Expr,
         rhs: &ast::Expr,
         fixup: FixupContext,
     ) {
-        let binop_prec = op.node.precedence();
+        let binop_prec = op.precedence();
         let left_prec = lhs.precedence();
         let right_prec = rhs.precedence();
 
-        let (mut left_needs_paren, right_needs_paren) = match op.node.fixity() {
+        let (mut left_needs_paren, right_needs_paren) = match op.fixity() {
             Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec),
             Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec),
             Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec),
         };
 
-        match (&lhs.kind, op.node) {
+        match (&lhs.kind, op) {
             // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is
             // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead
             // of `(x as i32) < ...`. We need to convince it _not_ to do that.
@@ -312,7 +312,7 @@ impl<'a> State<'a> {
 
         self.print_expr_cond_paren(lhs, left_needs_paren, fixup.leftmost_subexpression());
         self.space();
-        self.word_space(op.node.as_str());
+        self.word_space(op.as_str());
         self.print_expr_cond_paren(rhs, right_needs_paren, fixup.subsequent_subexpression());
     }
 
@@ -410,7 +410,7 @@ impl<'a> State<'a> {
                 self.print_expr_method_call(seg, receiver, args, fixup);
             }
             ast::ExprKind::Binary(op, lhs, rhs) => {
-                self.print_expr_binary(*op, lhs, rhs, fixup);
+                self.print_expr_binary(op.node, lhs, rhs, fixup);
             }
             ast::ExprKind::Unary(op, expr) => {
                 self.print_expr_unary(*op, expr, fixup);
@@ -605,8 +605,7 @@ impl<'a> State<'a> {
                     fixup.leftmost_subexpression(),
                 );
                 self.space();
-                self.word(op.node.as_str());
-                self.word_space("=");
+                self.word_space(op.node.as_str());
                 self.print_expr_cond_paren(
                     rhs,
                     rhs.precedence() < ExprPrecedence::Assign,
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
index d406a56c05d..653bd77cc4d 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
@@ -28,23 +28,24 @@ impl<'a> State<'a> {
     }
 
     fn print_foreign_item(&mut self, item: &ast::ForeignItem) {
-        let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item;
+        let ast::Item { id, span, ref attrs, ref kind, ref vis, tokens: _ } = *item;
         self.ann.pre(self, AnnNode::SubItem(id));
         self.hardbreak_if_not_bol();
         self.maybe_print_comment(span.lo());
         self.print_outer_attributes(attrs);
         match kind {
             ast::ForeignItemKind::Fn(func) => {
-                self.print_fn_full(ident, vis, attrs, &*func);
+                self.print_fn_full(vis, attrs, &*func);
             }
             ast::ForeignItemKind::Static(box ast::StaticItem {
+                ident,
                 ty,
                 mutability,
                 expr,
                 safety,
                 define_opaque,
             }) => self.print_item_const(
-                ident,
+                *ident,
                 Some(*mutability),
                 &ast::Generics::default(),
                 ty,
@@ -56,13 +57,14 @@ impl<'a> State<'a> {
             ),
             ast::ForeignItemKind::TyAlias(box ast::TyAlias {
                 defaultness,
+                ident,
                 generics,
                 where_clauses,
                 bounds,
                 ty,
             }) => {
                 self.print_associated_type(
-                    ident,
+                    *ident,
                     generics,
                     *where_clauses,
                     bounds,
@@ -162,7 +164,7 @@ impl<'a> State<'a> {
         self.print_outer_attributes(&item.attrs);
         self.ann.pre(self, AnnNode::Item(item));
         match &item.kind {
-            ast::ItemKind::ExternCrate(orig_name) => {
+            ast::ItemKind::ExternCrate(orig_name, ident) => {
                 self.head(visibility_qualified(&item.vis, "extern crate"));
                 if let &Some(orig_name) = orig_name {
                     self.print_name(orig_name);
@@ -170,7 +172,7 @@ impl<'a> State<'a> {
                     self.word("as");
                     self.space();
                 }
-                self.print_ident(item.ident);
+                self.print_ident(*ident);
                 self.word(";");
                 self.end(); // end inner head-block
                 self.end(); // end outer head-block
@@ -182,6 +184,7 @@ impl<'a> State<'a> {
                 self.word(";");
             }
             ast::ItemKind::Static(box StaticItem {
+                ident,
                 ty,
                 safety,
                 mutability: mutbl,
@@ -190,7 +193,7 @@ impl<'a> State<'a> {
             }) => {
                 self.print_safety(*safety);
                 self.print_item_const(
-                    item.ident,
+                    *ident,
                     Some(*mutbl),
                     &ast::Generics::default(),
                     ty,
@@ -203,13 +206,14 @@ impl<'a> State<'a> {
             }
             ast::ItemKind::Const(box ast::ConstItem {
                 defaultness,
+                ident,
                 generics,
                 ty,
                 expr,
                 define_opaque,
             }) => {
                 self.print_item_const(
-                    item.ident,
+                    *ident,
                     None,
                     generics,
                     ty,
@@ -221,15 +225,15 @@ impl<'a> State<'a> {
                 );
             }
             ast::ItemKind::Fn(func) => {
-                self.print_fn_full(item.ident, &item.vis, &item.attrs, &*func);
+                self.print_fn_full(&item.vis, &item.attrs, &*func);
             }
-            ast::ItemKind::Mod(safety, mod_kind) => {
+            ast::ItemKind::Mod(safety, ident, mod_kind) => {
                 self.head(Self::to_string(|s| {
                     s.print_visibility(&item.vis);
                     s.print_safety(*safety);
                     s.word("mod");
                 }));
-                self.print_ident(item.ident);
+                self.print_ident(*ident);
 
                 match mod_kind {
                     ModKind::Loaded(items, ..) => {
@@ -273,13 +277,14 @@ impl<'a> State<'a> {
             }
             ast::ItemKind::TyAlias(box ast::TyAlias {
                 defaultness,
+                ident,
                 generics,
                 where_clauses,
                 bounds,
                 ty,
             }) => {
                 self.print_associated_type(
-                    item.ident,
+                    *ident,
                     generics,
                     *where_clauses,
                     bounds,
@@ -288,16 +293,16 @@ impl<'a> State<'a> {
                     *defaultness,
                 );
             }
-            ast::ItemKind::Enum(enum_definition, params) => {
-                self.print_enum_def(enum_definition, params, item.ident, item.span, &item.vis);
+            ast::ItemKind::Enum(ident, enum_definition, params) => {
+                self.print_enum_def(enum_definition, params, *ident, item.span, &item.vis);
             }
-            ast::ItemKind::Struct(struct_def, generics) => {
+            ast::ItemKind::Struct(ident, struct_def, generics) => {
                 self.head(visibility_qualified(&item.vis, "struct"));
-                self.print_struct(struct_def, generics, item.ident, item.span, true);
+                self.print_struct(struct_def, generics, *ident, item.span, true);
             }
-            ast::ItemKind::Union(struct_def, generics) => {
+            ast::ItemKind::Union(ident, struct_def, generics) => {
                 self.head(visibility_qualified(&item.vis, "union"));
-                self.print_struct(struct_def, generics, item.ident, item.span, true);
+                self.print_struct(struct_def, generics, *ident, item.span, true);
             }
             ast::ItemKind::Impl(box ast::Impl {
                 safety,
@@ -347,19 +352,19 @@ impl<'a> State<'a> {
                 self.bclose(item.span, empty);
             }
             ast::ItemKind::Trait(box ast::Trait {
-                is_auto,
                 safety,
+                is_auto,
+                ident,
                 generics,
                 bounds,
                 items,
-                ..
             }) => {
                 self.head("");
                 self.print_visibility(&item.vis);
                 self.print_safety(*safety);
                 self.print_is_auto(*is_auto);
                 self.word_nbsp("trait");
-                self.print_ident(item.ident);
+                self.print_ident(*ident);
                 self.print_generic_params(&generics.params);
                 if !bounds.is_empty() {
                     self.word_nbsp(":");
@@ -375,9 +380,9 @@ impl<'a> State<'a> {
                 let empty = item.attrs.is_empty() && items.is_empty();
                 self.bclose(item.span, empty);
             }
-            ast::ItemKind::TraitAlias(generics, bounds) => {
+            ast::ItemKind::TraitAlias(ident, generics, bounds) => {
                 self.head(visibility_qualified(&item.vis, "trait"));
-                self.print_ident(item.ident);
+                self.print_ident(*ident);
                 self.print_generic_params(&generics.params);
                 self.nbsp();
                 if !bounds.is_empty() {
@@ -395,8 +400,8 @@ impl<'a> State<'a> {
                     self.word(";");
                 }
             }
-            ast::ItemKind::MacroDef(macro_def) => {
-                self.print_mac_def(macro_def, &item.ident, item.span, |state| {
+            ast::ItemKind::MacroDef(ident, macro_def) => {
+                self.print_mac_def(macro_def, &ident, item.span, |state| {
                     state.print_visibility(&item.vis)
                 });
             }
@@ -549,24 +554,25 @@ impl<'a> State<'a> {
     }
 
     fn print_assoc_item(&mut self, item: &ast::AssocItem) {
-        let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item;
+        let ast::Item { id, span, ref attrs, ref kind, ref vis, tokens: _ } = *item;
         self.ann.pre(self, AnnNode::SubItem(id));
         self.hardbreak_if_not_bol();
         self.maybe_print_comment(span.lo());
         self.print_outer_attributes(attrs);
         match kind {
             ast::AssocItemKind::Fn(func) => {
-                self.print_fn_full(ident, vis, attrs, &*func);
+                self.print_fn_full(vis, attrs, &*func);
             }
             ast::AssocItemKind::Const(box ast::ConstItem {
                 defaultness,
+                ident,
                 generics,
                 ty,
                 expr,
                 define_opaque,
             }) => {
                 self.print_item_const(
-                    ident,
+                    *ident,
                     None,
                     generics,
                     ty,
@@ -579,13 +585,14 @@ impl<'a> State<'a> {
             }
             ast::AssocItemKind::Type(box ast::TyAlias {
                 defaultness,
+                ident,
                 generics,
                 where_clauses,
                 bounds,
                 ty,
             }) => {
                 self.print_associated_type(
-                    ident,
+                    *ident,
                     generics,
                     *where_clauses,
                     bounds,
@@ -671,14 +678,8 @@ impl<'a> State<'a> {
         }
     }
 
-    fn print_fn_full(
-        &mut self,
-        name: Ident,
-        vis: &ast::Visibility,
-        attrs: &[ast::Attribute],
-        func: &ast::Fn,
-    ) {
-        let ast::Fn { defaultness, generics, sig, contract, body, define_opaque } = func;
+    fn print_fn_full(&mut self, vis: &ast::Visibility, attrs: &[ast::Attribute], func: &ast::Fn) {
+        let ast::Fn { defaultness, ident, generics, sig, contract, body, define_opaque } = func;
 
         self.print_define_opaques(define_opaque.as_deref());
 
@@ -687,7 +688,7 @@ impl<'a> State<'a> {
         }
         self.print_visibility(vis);
         self.print_defaultness(*defaultness);
-        self.print_fn(&sig.decl, sig.header, Some(name), generics);
+        self.print_fn(&sig.decl, sig.header, Some(*ident), generics);
         if let Some(contract) = &contract {
             self.nbsp();
             self.print_contract(contract);
@@ -734,13 +735,13 @@ impl<'a> State<'a> {
         &mut self,
         decl: &ast::FnDecl,
         header: ast::FnHeader,
-        name: Option<Ident>,
+        ident: Option<Ident>,
         generics: &ast::Generics,
     ) {
         self.print_fn_header_info(header);
-        if let Some(name) = name {
+        if let Some(ident) = ident {
             self.nbsp();
-            self.print_ident(name);
+            self.print_ident(ident);
         }
         self.print_generic_params(&generics.params);
         self.print_fn_params_and_ret(decl, false);
diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl
index ada20e5c614..33b80c4b03d 100644
--- a/compiler/rustc_borrowck/messages.ftl
+++ b/compiler/rustc_borrowck/messages.ftl
@@ -162,13 +162,6 @@ borrowck_opaque_type_lifetime_mismatch =
     .prev_lifetime_label = lifetime `{$prev}` previously used here
     .note = if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
 
-borrowck_opaque_type_non_generic_param =
-    expected generic {$kind} parameter, found `{$ty}`
-    .label = {STREQ($ty, "'static") ->
-        [true] cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
-        *[other] this generic parameter must be used with a generic {$kind} parameter
-    }
-
 borrowck_partial_var_move_by_use_in_closure =
     variable {$is_partial ->
         [true] partially moved
diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs
index 5a89f7c351c..45cdd232564 100644
--- a/compiler/rustc_borrowck/src/consumers.rs
+++ b/compiler/rustc_borrowck/src/consumers.rs
@@ -1,7 +1,7 @@
 //! This file provides API for compiler consumers.
 
 use rustc_hir::def_id::LocalDefId;
-use rustc_index::{IndexSlice, IndexVec};
+use rustc_index::IndexVec;
 use rustc_middle::mir::{Body, Promoted};
 use rustc_middle::ty::TyCtxt;
 
@@ -100,8 +100,5 @@ pub fn get_body_with_borrowck_facts(
     def: LocalDefId,
     options: ConsumerOptions,
 ) -> BodyWithBorrowckFacts<'_> {
-    let (input_body, promoted) = tcx.mir_promoted(def);
-    let input_body: &Body<'_> = &input_body.borrow();
-    let promoted: &IndexSlice<_, _> = &promoted.borrow();
-    *super::do_mir_borrowck(tcx, input_body, promoted, Some(options)).1.unwrap()
+    *super::do_mir_borrowck(tcx, def, Some(options)).1.unwrap()
 }
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 978186f76a1..1f4eb0c449f 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -181,7 +181,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             let closure = self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
 
             let mut is_loop_move = false;
-            let mut in_pattern = false;
             let mut seen_spans = FxIndexSet::default();
 
             for move_site in &move_site_vec {
@@ -204,7 +203,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     self.suggest_ref_or_clone(
                         mpi,
                         &mut err,
-                        &mut in_pattern,
                         move_spans,
                         moved_place.as_ref(),
                         &mut has_suggest_reborrow,
@@ -256,15 +254,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             let place = &self.move_data.move_paths[mpi].place;
             let ty = place.ty(self.body, self.infcx.tcx).ty;
 
-            // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
-            // Same for if we're in a loop, see #101119.
-            if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) {
-                if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
-                    // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
-                    self.suggest_reborrow(&mut err, span, moved_place);
-                }
-            }
-
             if self.infcx.param_env.caller_bounds().iter().any(|c| {
                 c.as_trait_clause().is_some_and(|pred| {
                     pred.skip_binder().self_ty() == ty && self.infcx.tcx.is_fn_trait(pred.def_id())
@@ -330,7 +319,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         &self,
         mpi: MovePathIndex,
         err: &mut Diag<'infcx>,
-        in_pattern: &mut bool,
         move_spans: UseSpans<'tcx>,
         moved_place: PlaceRef<'tcx>,
         has_suggest_reborrow: &mut bool,
@@ -545,7 +533,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 && !move_span.is_dummy()
                 && !self.infcx.tcx.sess.source_map().is_imported(move_span)
             {
-                *in_pattern = true;
                 let mut sugg = vec![(pat.span.shrink_to_lo(), "ref ".to_string())];
                 if let Some(pat) = finder.parent_pat {
                     sugg.insert(0, (pat.span.shrink_to_lo(), "ref ".to_string()));
@@ -1364,7 +1351,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         }
         // Try to find predicates on *generic params* that would allow copying `ty`
         let mut suggestion =
-            if let Some(symbol) = tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
+            if let Some(symbol) = tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
                 format!(": {symbol}.clone()")
             } else {
                 ".clone()".to_owned()
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index 29cc749877b..0394a42ea9c 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -502,7 +502,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 let upvar = &self.upvars[upvar_field.unwrap().index()];
                 let upvar_hir_id = upvar.get_root_variable();
                 let upvar_name = upvar.to_string(tcx);
-                let upvar_span = tcx.hir().span(upvar_hir_id);
+                let upvar_span = tcx.hir_span(upvar_hir_id);
 
                 let place_name = self.describe_any_place(move_place.as_ref());
 
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index fddddf404db..56cc4327585 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -969,7 +969,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             }
         };
 
-        // If we can detect the expression to be an function or method call where the closure was
+        // If we can detect the expression to be a function or method call where the closure was
         // an argument, we point at the function or method definition argument...
         if let Some((callee_def_id, call_span, call_args)) = get_call_details() {
             let arg_pos = call_args
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 50a18b04de4..d1d783c22e3 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -628,7 +628,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
 
             if let Some(def_hir) = defined_hir {
                 let upvars_map = self.infcx.tcx.upvars_mentioned(def_id).unwrap();
-                let upvar_def_span = self.infcx.tcx.hir().span(def_hir);
+                let upvar_def_span = self.infcx.tcx.hir_span(def_hir);
                 let upvar_span = upvars_map.get(&def_hir).unwrap().span;
                 diag.subdiagnostic(VarHereDenote::Defined { span: upvar_def_span });
                 diag.subdiagnostic(VarHereDenote::Captured { span: upvar_span });
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index 412aaf70c3f..45f5eaa514b 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -291,7 +291,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
         match *error_region {
             ty::ReEarlyParam(ebr) => ebr.has_name().then(|| {
                 let def_id = tcx.generics_of(self.mir_def_id()).region_param(ebr, tcx).def_id;
-                let span = tcx.hir().span_if_local(def_id).unwrap_or(DUMMY_SP);
+                let span = tcx.hir_span_if_local(def_id).unwrap_or(DUMMY_SP);
                 RegionName { name: ebr.name, source: RegionNameSource::NamedEarlyParamRegion(span) }
             }),
 
@@ -302,7 +302,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
             ty::ReLateParam(late_param) => match late_param.kind {
                 ty::LateParamRegionKind::Named(region_def_id, name) => {
                     // Get the span to point to, even if we don't use the name.
-                    let span = tcx.hir().span_if_local(region_def_id).unwrap_or(DUMMY_SP);
+                    let span = tcx.hir_span_if_local(region_def_id).unwrap_or(DUMMY_SP);
                     debug!(
                         "bound region named: {:?}, is_named: {:?}",
                         name,
diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs
index 693d22abbe6..14ed6a27a7a 100644
--- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs
@@ -70,7 +70,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         debug!("get_upvar_name_and_span_for_region: upvar_hir_id={upvar_hir_id:?}");
 
         let upvar_name = tcx.hir_name(upvar_hir_id);
-        let upvar_span = tcx.hir().span(upvar_hir_id);
+        let upvar_span = tcx.hir_span(upvar_hir_id);
         debug!(
             "get_upvar_name_and_span_for_region: upvar_name={upvar_name:?} upvar_span={upvar_span:?}",
         );
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 84b7b8c6a2d..240bd20053b 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -35,7 +35,7 @@ use rustc_infer::infer::{
 };
 use rustc_middle::mir::*;
 use rustc_middle::query::Providers;
-use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt, TypingMode, fold_regions};
+use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt, TypingMode};
 use rustc_middle::{bug, span_bug};
 use rustc_mir_dataflow::impls::{
     EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
@@ -103,11 +103,8 @@ pub fn provide(providers: &mut Providers) {
 }
 
 fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
-    let (input_body, promoted) = tcx.mir_promoted(def);
-    debug!("run query mir_borrowck: {}", tcx.def_path_str(def));
-
+    let (input_body, _) = tcx.mir_promoted(def);
     let input_body: &Body<'_> = &input_body.borrow();
-
     if input_body.should_skip() || input_body.tainted_by_errors.is_some() {
         debug!("Skipping borrowck because of injected body or tainted body");
         // Let's make up a borrowck result! Fun times!
@@ -120,7 +117,7 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
         return tcx.arena.alloc(result);
     }
 
-    let borrowck_result = do_mir_borrowck(tcx, input_body, &*promoted.borrow(), None).0;
+    let borrowck_result = do_mir_borrowck(tcx, def, None).0;
     debug!("mir_borrowck done");
 
     tcx.arena.alloc(borrowck_result)
@@ -131,15 +128,16 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
 /// Use `consumer_options: None` for the default behavior of returning
 /// [`BorrowCheckResult`] only. Otherwise, return [`BodyWithBorrowckFacts`] according
 /// to the given [`ConsumerOptions`].
-#[instrument(skip(tcx, input_body, input_promoted), fields(id=?input_body.source.def_id()), level = "debug")]
+#[instrument(skip(tcx), level = "debug")]
 fn do_mir_borrowck<'tcx>(
     tcx: TyCtxt<'tcx>,
-    input_body: &Body<'tcx>,
-    input_promoted: &IndexSlice<Promoted, Body<'tcx>>,
+    def: LocalDefId,
     consumer_options: Option<ConsumerOptions>,
 ) -> (BorrowCheckResult<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
-    let def = input_body.source.def_id().expect_local();
     let infcx = BorrowckInferCtxt::new(tcx, def);
+    let (input_body, promoted) = tcx.mir_promoted(def);
+    let input_body: &Body<'_> = &input_body.borrow();
+    let input_promoted: &IndexSlice<_, _> = &promoted.borrow();
     if let Some(e) = input_body.tainted_by_errors {
         infcx.set_tainted_by_errors(e);
     }
@@ -173,12 +171,6 @@ fn do_mir_borrowck<'tcx>(
     let free_regions = nll::replace_regions_in_mir(&infcx, &mut body_owned, &mut promoted);
     let body = &body_owned; // no further changes
 
-    // FIXME(-Znext-solver): A bit dubious that we're only registering
-    // predefined opaques in the typeck root.
-    if infcx.next_trait_solver() && !infcx.tcx.is_typeck_child(body.source.def_id()) {
-        infcx.register_predefined_opaques_for_next_solver(def);
-    }
-
     let location_table = PoloniusLocationTable::new(body);
 
     let move_data = MoveData::gather_moves(body, tcx, |_| true);
@@ -433,7 +425,12 @@ pub(crate) struct BorrowckInferCtxt<'tcx> {
 
 impl<'tcx> BorrowckInferCtxt<'tcx> {
     pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
-        let infcx = tcx.infer_ctxt().build(TypingMode::analysis_in_body(tcx, def_id));
+        let typing_mode = if tcx.use_typing_mode_borrowck() {
+            TypingMode::borrowck(tcx, def_id)
+        } else {
+            TypingMode::analysis_in_body(tcx, def_id)
+        };
+        let infcx = tcx.infer_ctxt().build(typing_mode);
         let param_env = tcx.param_env(def_id);
         BorrowckInferCtxt { infcx, reg_var_to_origin: RefCell::new(Default::default()), param_env }
     }
@@ -480,28 +477,6 @@ impl<'tcx> BorrowckInferCtxt<'tcx> {
 
         next_region
     }
-
-    /// With the new solver we prepopulate the opaque type storage during
-    /// MIR borrowck with the hidden types from HIR typeck. This is necessary
-    /// to avoid ambiguities as earlier goals can rely on the hidden type
-    /// of an opaque which is only constrained by a later goal.
-    fn register_predefined_opaques_for_next_solver(&self, def_id: LocalDefId) {
-        let tcx = self.tcx;
-        // OK to use the identity arguments for each opaque type key, since
-        // we remap opaques from HIR typeck back to their definition params.
-        for data in tcx.typeck(def_id).concrete_opaque_types.iter().map(|(k, v)| (*k, *v)) {
-            // HIR typeck did not infer the regions of the opaque, so we instantiate
-            // them with fresh inference variables.
-            let (key, hidden_ty) = fold_regions(tcx, data, |_, _| {
-                self.next_nll_region_var_in_universe(
-                    NllRegionVariableOrigin::Existential { from_forall: false },
-                    ty::UniverseIndex::ROOT,
-                )
-            });
-
-            self.inject_new_hidden_type_unchecked(key, hidden_ty);
-        }
-    }
 }
 
 impl<'tcx> Deref for BorrowckInferCtxt<'tcx> {
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
index ca8b9fb4e9d..a098450352f 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
@@ -1,22 +1,17 @@
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_errors::ErrorGuaranteed;
-use rustc_hir::OpaqueTyOrigin;
-use rustc_hir::def_id::LocalDefId;
-use rustc_infer::infer::outlives::env::OutlivesEnvironment;
-use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, TyCtxtInferExt as _};
+use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
 use rustc_macros::extension;
 use rustc_middle::ty::{
-    self, GenericArgKind, GenericArgs, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable,
-    TypeVisitableExt, TypingMode, fold_regions,
+    self, DefiningScopeKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable,
+    TypeVisitableExt, fold_regions,
 };
 use rustc_span::Span;
-use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt;
-use rustc_trait_selection::traits::ObligationCtxt;
+use rustc_trait_selection::opaque_types::check_opaque_type_parameter_valid;
 use tracing::{debug, instrument};
 
 use super::RegionInferenceContext;
 use crate::opaque_types::ConcreteOpaqueTypes;
-use crate::session_diagnostics::{LifetimeMismatchOpaqueParam, NonGenericOpaqueTypeParam};
+use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
 use crate::universal_regions::RegionClassification;
 
 impl<'tcx> RegionInferenceContext<'tcx> {
@@ -272,14 +267,21 @@ impl<'tcx> InferCtxt<'tcx> {
             return Ty::new_error(self.tcx, e);
         }
 
-        if let Err(guar) =
-            check_opaque_type_parameter_valid(self, opaque_type_key, instantiated_ty.span)
-        {
+        if let Err(guar) = check_opaque_type_parameter_valid(
+            self,
+            opaque_type_key,
+            instantiated_ty.span,
+            DefiningScopeKind::MirBorrowck,
+        ) {
             return Ty::new_error(self.tcx, guar);
         }
 
         let definition_ty = instantiated_ty
-            .remap_generic_params_to_declaration_params(opaque_type_key, self.tcx, false)
+            .remap_generic_params_to_declaration_params(
+                opaque_type_key,
+                self.tcx,
+                DefiningScopeKind::MirBorrowck,
+            )
             .ty;
 
         if let Err(e) = definition_ty.error_reported() {
@@ -289,156 +291,3 @@ impl<'tcx> InferCtxt<'tcx> {
         definition_ty
     }
 }
-
-/// Opaque type parameter validity check as documented in the [rustc-dev-guide chapter].
-///
-/// [rustc-dev-guide chapter]:
-/// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
-fn check_opaque_type_parameter_valid<'tcx>(
-    infcx: &InferCtxt<'tcx>,
-    opaque_type_key: OpaqueTypeKey<'tcx>,
-    span: Span,
-) -> Result<(), ErrorGuaranteed> {
-    let tcx = infcx.tcx;
-    let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
-    let opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id);
-    let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default();
-
-    for (i, arg) in opaque_type_key.iter_captured_args(tcx) {
-        let arg_is_param = match arg.unpack() {
-            GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
-            GenericArgKind::Lifetime(lt) => {
-                matches!(*lt, ty::ReEarlyParam(_) | ty::ReLateParam(_))
-                    || (lt.is_static() && opaque_env.param_equal_static(i))
-            }
-            GenericArgKind::Const(ct) => matches!(ct.kind(), ty::ConstKind::Param(_)),
-        };
-
-        if arg_is_param {
-            // Register if the same lifetime appears multiple times in the generic args.
-            // There is an exception when the opaque type *requires* the lifetimes to be equal.
-            // See [rustc-dev-guide chapter] § "An exception to uniqueness rule".
-            let seen_where = seen_params.entry(arg).or_default();
-            if !seen_where.first().is_some_and(|&prev_i| opaque_env.params_equal(i, prev_i)) {
-                seen_where.push(i);
-            }
-        } else {
-            // Prevent `fn foo() -> Foo<u32>` from being defining.
-            let opaque_param = opaque_generics.param_at(i, tcx);
-            let kind = opaque_param.kind.descr();
-
-            opaque_env.param_is_error(i)?;
-
-            return Err(infcx.dcx().emit_err(NonGenericOpaqueTypeParam {
-                ty: arg,
-                kind,
-                span,
-                param_span: tcx.def_span(opaque_param.def_id),
-            }));
-        }
-    }
-
-    for (_, indices) in seen_params {
-        if indices.len() > 1 {
-            let descr = opaque_generics.param_at(indices[0], tcx).kind.descr();
-            let spans: Vec<_> = indices
-                .into_iter()
-                .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id))
-                .collect();
-            #[allow(rustc::diagnostic_outside_of_impl)]
-            #[allow(rustc::untranslatable_diagnostic)]
-            return Err(infcx
-                .dcx()
-                .struct_span_err(span, "non-defining opaque type use in defining scope")
-                .with_span_note(spans, format!("{descr} used multiple times"))
-                .emit());
-        }
-    }
-
-    Ok(())
-}
-
-/// Computes if an opaque type requires a lifetime parameter to be equal to
-/// another one or to the `'static` lifetime.
-/// These requirements are derived from the explicit and implied bounds.
-struct LazyOpaqueTyEnv<'tcx> {
-    tcx: TyCtxt<'tcx>,
-    def_id: LocalDefId,
-
-    /// Equal parameters will have the same name. Computed Lazily.
-    /// Example:
-    ///     `type Opaque<'a: 'static, 'b: 'c, 'c: 'b> = impl Sized;`
-    ///     Identity args: `['a, 'b, 'c]`
-    ///     Canonical args: `['static, 'b, 'b]`
-    canonical_args: std::cell::OnceCell<ty::GenericArgsRef<'tcx>>,
-}
-
-impl<'tcx> LazyOpaqueTyEnv<'tcx> {
-    fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
-        Self { tcx, def_id, canonical_args: std::cell::OnceCell::new() }
-    }
-
-    fn param_equal_static(&self, param_index: usize) -> bool {
-        self.get_canonical_args()[param_index].expect_region().is_static()
-    }
-
-    fn params_equal(&self, param1: usize, param2: usize) -> bool {
-        let canonical_args = self.get_canonical_args();
-        canonical_args[param1] == canonical_args[param2]
-    }
-
-    fn param_is_error(&self, param_index: usize) -> Result<(), ErrorGuaranteed> {
-        self.get_canonical_args()[param_index].error_reported()
-    }
-
-    fn get_canonical_args(&self) -> ty::GenericArgsRef<'tcx> {
-        if let Some(&canonical_args) = self.canonical_args.get() {
-            return canonical_args;
-        }
-
-        let &Self { tcx, def_id, .. } = self;
-        let origin = tcx.local_opaque_ty_origin(def_id);
-        let parent = match origin {
-            OpaqueTyOrigin::FnReturn { parent, .. }
-            | OpaqueTyOrigin::AsyncFn { parent, .. }
-            | OpaqueTyOrigin::TyAlias { parent, .. } => parent,
-        };
-        let param_env = tcx.param_env(parent);
-        let args = GenericArgs::identity_for_item(tcx, parent).extend_to(
-            tcx,
-            def_id.to_def_id(),
-            |param, _| {
-                tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()).into()
-            },
-        );
-
-        // FIXME(#132279): It feels wrong to use `non_body_analysis` here given that we're
-        // in a body here.
-        let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
-        let ocx = ObligationCtxt::new(&infcx);
-
-        let wf_tys = ocx.assumed_wf_types(param_env, parent).unwrap_or_else(|_| {
-            tcx.dcx().span_delayed_bug(tcx.def_span(def_id), "error getting implied bounds");
-            Default::default()
-        });
-        let outlives_env = OutlivesEnvironment::new(&infcx, parent, param_env, wf_tys);
-
-        let mut seen = vec![tcx.lifetimes.re_static];
-        let canonical_args = fold_regions(tcx, args, |r1, _| {
-            if r1.is_error() {
-                r1
-            } else if let Some(&r2) = seen.iter().find(|&&r2| {
-                let free_regions = outlives_env.free_region_map();
-                free_regions.sub_free_regions(tcx, r1, r2)
-                    && free_regions.sub_free_regions(tcx, r2, r1)
-            }) {
-                r2
-            } else {
-                seen.push(r1);
-                r1
-            }
-        });
-        self.canonical_args.set(canonical_args).unwrap();
-        canonical_args
-    }
-}
diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs
index 4be5d0dbf42..5143b2fa205 100644
--- a/compiler/rustc_borrowck/src/session_diagnostics.rs
+++ b/compiler/rustc_borrowck/src/session_diagnostics.rs
@@ -295,17 +295,6 @@ pub(crate) struct MoveBorrow<'a> {
 }
 
 #[derive(Diagnostic)]
-#[diag(borrowck_opaque_type_non_generic_param, code = E0792)]
-pub(crate) struct NonGenericOpaqueTypeParam<'a, 'tcx> {
-    pub ty: GenericArg<'tcx>,
-    pub kind: &'a str,
-    #[primary_span]
-    pub span: Span,
-    #[label]
-    pub param_span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(borrowck_opaque_type_lifetime_mismatch)]
 pub(crate) struct LifetimeMismatchOpaqueParam<'tcx> {
     pub arg: GenericArg<'tcx>,
diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
index 3f03834f8d7..603dc90bafc 100644
--- a/compiler/rustc_builtin_macros/messages.ftl
+++ b/compiler/rustc_builtin_macros/messages.ftl
@@ -79,6 +79,7 @@ builtin_macros_autodiff_ret_activity = invalid return activity {$act} in {$mode}
 builtin_macros_autodiff_ty_activity = {$act} can not be used for this type
 builtin_macros_autodiff_unknown_activity = did not recognize Activity: `{$act}`
 
+builtin_macros_autodiff_width = autodiff width must fit u32, but is {$width}
 builtin_macros_bad_derive_target = `derive` may only be applied to `struct`s, `enum`s and `union`s
     .label = not applicable here
     .label2 = not a `struct`, `enum` or `union`
diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
index 1c1b2c88f76..ea406e70666 100644
--- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
+++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
@@ -21,15 +21,15 @@ pub(crate) fn expand(
 
     // Allow using `#[alloc_error_handler]` on an item statement
     // FIXME - if we get deref patterns, use them to reduce duplication here
-    let (item, is_stmt, sig_span) = if let Annotatable::Item(item) = &item
+    let (item, ident, is_stmt, sig_span) = if let Annotatable::Item(item) = &item
         && let ItemKind::Fn(fn_kind) = &item.kind
     {
-        (item, false, ecx.with_def_site_ctxt(fn_kind.sig.span))
+        (item, fn_kind.ident, false, ecx.with_def_site_ctxt(fn_kind.sig.span))
     } else if let Annotatable::Stmt(stmt) = &item
         && let StmtKind::Item(item) = &stmt.kind
         && let ItemKind::Fn(fn_kind) = &item.kind
     {
-        (item, true, ecx.with_def_site_ctxt(fn_kind.sig.span))
+        (item, fn_kind.ident, true, ecx.with_def_site_ctxt(fn_kind.sig.span))
     } else {
         ecx.dcx().emit_err(errors::AllocErrorMustBeFn { span: item.span() });
         return vec![orig_item];
@@ -39,7 +39,7 @@ pub(crate) fn expand(
     let span = ecx.with_def_site_ctxt(item.span);
 
     // Generate item statements for the allocator methods.
-    let stmts = thin_vec![generate_handler(ecx, item.ident, span, sig_span)];
+    let stmts = thin_vec![generate_handler(ecx, ident, span, sig_span)];
 
     // Generate anonymous constant serving as container for the allocator methods.
     let const_ty = ecx.ty(sig_span, TyKind::Tup(ThinVec::new()));
@@ -85,6 +85,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span
     let kind = ItemKind::Fn(Box::new(Fn {
         defaultness: ast::Defaultness::Final,
         sig,
+        ident: Ident::from_str_and_span("__rg_oom", span),
         generics: Generics::default(),
         contract: None,
         body,
@@ -93,6 +94,6 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span
 
     let attrs = thin_vec![cx.attr_word(sym::rustc_std_internal_symbol, span)];
 
-    let item = cx.item(span, Ident::from_str_and_span("__rg_oom", span), attrs, kind);
+    let item = cx.item(span, attrs, kind);
     cx.stmt_item(sig_span, item)
 }
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index eb5b345e49e..3e8ddb8abd4 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -10,7 +10,7 @@ use rustc_index::bit_set::GrowableBitSet;
 use rustc_parse::exp;
 use rustc_parse::parser::{ExpKeywordPair, Parser};
 use rustc_session::lint;
-use rustc_span::{ErrorGuaranteed, Ident, InnerSpan, Span, Symbol, kw};
+use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, kw};
 use rustc_target::asm::InlineAsmArch;
 use smallvec::smallvec;
 use {rustc_ast as ast, rustc_parse_format as parse};
@@ -888,7 +888,6 @@ pub(super) fn expand_global_asm<'cx>(
             };
             match mac {
                 Ok(inline_asm) => MacEager::items(smallvec![P(ast::Item {
-                    ident: Ident::empty(),
                     attrs: ast::AttrVec::new(),
                     id: ast::DUMMY_NODE_ID,
                     kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)),
diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs
index a949ab94f3a..ea7248ca539 100644
--- a/compiler/rustc_builtin_macros/src/assert/context.rs
+++ b/compiler/rustc_builtin_macros/src/assert/context.rs
@@ -112,7 +112,6 @@ impl<'cx, 'a> Context<'cx, 'a> {
             self.span,
             self.cx.item(
                 self.span,
-                Ident::empty(),
                 thin_vec![self.cx.attr_nested_word(sym::allow, sym::unused_imports, self.span)],
                 ItemKind::Use(UseTree {
                     prefix: self.cx.path(self.span, self.cx.std_path(&[sym::asserting])),
diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs
index be11711757e..7f99f75b2b9 100644
--- a/compiler/rustc_builtin_macros/src/autodiff.rs
+++ b/compiler/rustc_builtin_macros/src/autodiff.rs
@@ -12,12 +12,12 @@ mod llvm_enzyme {
         valid_ty_for_activity,
     };
     use rustc_ast::ptr::P;
-    use rustc_ast::token::{Token, TokenKind};
+    use rustc_ast::token::{Lit, LitKind, Token, TokenKind};
     use rustc_ast::tokenstream::*;
     use rustc_ast::visit::AssocCtxt::*;
     use rustc_ast::{
-        self as ast, AssocItemKind, BindingMode, FnRetTy, FnSig, Generics, ItemKind, MetaItemInner,
-        PatKind, TyKind,
+        self as ast, AssocItemKind, BindingMode, ExprKind, FnRetTy, FnSig, Generics, ItemKind,
+        MetaItemInner, PatKind, QSelf, TyKind,
     };
     use rustc_expand::base::{Annotatable, ExtCtxt};
     use rustc_span::{Ident, Span, Symbol, kw, sym};
@@ -45,6 +45,16 @@ mod llvm_enzyme {
         }
     }
     fn first_ident(x: &MetaItemInner) -> rustc_span::Ident {
+        if let Some(l) = x.lit() {
+            match l.kind {
+                ast::LitKind::Int(val, _) => {
+                    // get an Ident from a lit
+                    return rustc_span::Ident::from_str(val.get().to_string().as_str());
+                }
+                _ => {}
+            }
+        }
+
         let segments = &x.meta_item().unwrap().path.segments;
         assert!(segments.len() == 1);
         segments[0].ident
@@ -54,6 +64,14 @@ mod llvm_enzyme {
         first_ident(x).name.to_string()
     }
 
+    fn width(x: &MetaItemInner) -> Option<u128> {
+        let lit = x.lit()?;
+        match lit.kind {
+            ast::LitKind::Int(x, _) => Some(x.get()),
+            _ => return None,
+        }
+    }
+
     pub(crate) fn from_ast(
         ecx: &mut ExtCtxt<'_>,
         meta_item: &ThinVec<MetaItemInner>,
@@ -65,9 +83,32 @@ mod llvm_enzyme {
             dcx.emit_err(errors::AutoDiffInvalidMode { span: meta_item[1].span(), mode });
             return AutoDiffAttrs::error();
         };
+
+        // Now we check, whether the user wants autodiff in batch/vector mode, or scalar mode.
+        // If he doesn't specify an integer (=width), we default to scalar mode, thus width=1.
+        let mut first_activity = 2;
+
+        let width = if let [_, _, x, ..] = &meta_item[..]
+            && let Some(x) = width(x)
+        {
+            first_activity = 3;
+            match x.try_into() {
+                Ok(x) => x,
+                Err(_) => {
+                    dcx.emit_err(errors::AutoDiffInvalidWidth {
+                        span: meta_item[2].span(),
+                        width: x,
+                    });
+                    return AutoDiffAttrs::error();
+                }
+            }
+        } else {
+            1
+        };
+
         let mut activities: Vec<DiffActivity> = vec![];
         let mut errors = false;
-        for x in &meta_item[2..] {
+        for x in &meta_item[first_activity..] {
             let activity_str = name(&x);
             let res = DiffActivity::from_str(&activity_str);
             match res {
@@ -98,7 +139,20 @@ mod llvm_enzyme {
             (&DiffActivity::None, activities.as_slice())
         };
 
-        AutoDiffAttrs { mode, ret_activity: *ret_activity, input_activity: input_activity.to_vec() }
+        AutoDiffAttrs {
+            mode,
+            width,
+            ret_activity: *ret_activity,
+            input_activity: input_activity.to_vec(),
+        }
+    }
+
+    fn meta_item_inner_to_ts(t: &MetaItemInner, ts: &mut Vec<TokenTree>) {
+        let comma: Token = Token::new(TokenKind::Comma, Span::default());
+        let val = first_ident(t);
+        let t = Token::from_ast_ident(val);
+        ts.push(TokenTree::Token(t, Spacing::Joint));
+        ts.push(TokenTree::Token(comma.clone(), Spacing::Alone));
     }
 
     /// We expand the autodiff macro to generate a new placeholder function which passes
@@ -146,26 +200,26 @@ mod llvm_enzyme {
         }
         let dcx = ecx.sess.dcx();
         // first get the annotable item:
-        let (sig, is_impl): (FnSig, bool) = match &item {
+        let (primal, sig, is_impl): (Ident, FnSig, bool) = match &item {
             Annotatable::Item(iitem) => {
-                let sig = match &iitem.kind {
-                    ItemKind::Fn(box ast::Fn { sig, .. }) => sig,
+                let (ident, sig) = match &iitem.kind {
+                    ItemKind::Fn(box ast::Fn { ident, sig, .. }) => (ident, sig),
                     _ => {
                         dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
                         return vec![item];
                     }
                 };
-                (sig.clone(), false)
+                (*ident, sig.clone(), false)
             }
             Annotatable::AssocItem(assoc_item, Impl { of_trait: false }) => {
-                let sig = match &assoc_item.kind {
-                    ast::AssocItemKind::Fn(box ast::Fn { sig, .. }) => sig,
+                let (ident, sig) = match &assoc_item.kind {
+                    ast::AssocItemKind::Fn(box ast::Fn { ident, sig, .. }) => (ident, sig),
                     _ => {
                         dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
                         return vec![item];
                     }
                 };
-                (sig.clone(), true)
+                (*ident, sig.clone(), true)
             }
             _ => {
                 dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
@@ -184,11 +238,9 @@ mod llvm_enzyme {
         let has_ret = has_ret(&sig.decl.output);
         let sig_span = ecx.with_call_site_ctxt(sig.span);
 
-        let (vis, primal) = match &item {
-            Annotatable::Item(iitem) => (iitem.vis.clone(), iitem.ident.clone()),
-            Annotatable::AssocItem(assoc_item, _) => {
-                (assoc_item.vis.clone(), assoc_item.ident.clone())
-            }
+        let vis = match &item {
+            Annotatable::Item(iitem) => iitem.vis.clone(),
+            Annotatable::AssocItem(assoc_item, _) => assoc_item.vis.clone(),
             _ => {
                 dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
                 return vec![item];
@@ -197,27 +249,49 @@ mod llvm_enzyme {
 
         // create TokenStream from vec elemtents:
         // meta_item doesn't have a .tokens field
-        let comma: Token = Token::new(TokenKind::Comma, Span::default());
         let mut ts: Vec<TokenTree> = vec![];
         if meta_item_vec.len() < 2 {
             // At the bare minimum, we need a fnc name and a mode, even for a dummy function with no
             // input and output args.
             dcx.emit_err(errors::AutoDiffMissingConfig { span: item.span() });
             return vec![item];
+        }
+
+        meta_item_inner_to_ts(&meta_item_vec[1], &mut ts);
+
+        // Now, if the user gave a width (vector aka batch-mode ad), then we copy it.
+        // If it is not given, we default to 1 (scalar mode).
+        let start_position;
+        let kind: LitKind = LitKind::Integer;
+        let symbol;
+        if meta_item_vec.len() >= 3
+            && let Some(width) = width(&meta_item_vec[2])
+        {
+            start_position = 3;
+            symbol = Symbol::intern(&width.to_string());
         } else {
-            for t in meta_item_vec.clone()[1..].iter() {
-                let val = first_ident(t);
-                let t = Token::from_ast_ident(val);
-                ts.push(TokenTree::Token(t, Spacing::Joint));
-                ts.push(TokenTree::Token(comma.clone(), Spacing::Alone));
-            }
+            start_position = 2;
+            symbol = sym::integer(1);
+        }
+        let l: Lit = Lit { kind, symbol, suffix: None };
+        let t = Token::new(TokenKind::Literal(l), Span::default());
+        let comma = Token::new(TokenKind::Comma, Span::default());
+        ts.push(TokenTree::Token(t, Spacing::Joint));
+        ts.push(TokenTree::Token(comma.clone(), Spacing::Alone));
+
+        for t in meta_item_vec.clone()[start_position..].iter() {
+            meta_item_inner_to_ts(t, &mut ts);
         }
+
         if !has_ret {
             // We don't want users to provide a return activity if the function doesn't return anything.
             // For simplicity, we just add a dummy token to the end of the list.
             let t = Token::new(TokenKind::Ident(sym::None, false.into()), Span::default());
             ts.push(TokenTree::Token(t, Spacing::Joint));
+            ts.push(TokenTree::Token(comma, Spacing::Alone));
         }
+        // We remove the last, trailing comma.
+        ts.pop();
         let ts: TokenStream = TokenStream::from_iter(ts);
 
         let x: AutoDiffAttrs = from_ast(ecx, &meta_item_vec, has_ret);
@@ -237,12 +311,12 @@ mod llvm_enzyme {
         let d_body = gen_enzyme_body(
             ecx, &x, n_active, &sig, &d_sig, primal, &new_args, span, sig_span, idents, errored,
         );
-        let d_ident = first_ident(&meta_item_vec[0]);
 
         // The first element of it is the name of the function to be generated
         let asdf = Box::new(ast::Fn {
             defaultness: ast::Defaultness::Final,
             sig: d_sig,
+            ident: first_ident(&meta_item_vec[0]),
             generics: Generics::default(),
             contract: None,
             body: Some(d_body),
@@ -323,14 +397,12 @@ mod llvm_enzyme {
                 id: ast::DUMMY_NODE_ID,
                 span,
                 vis,
-                ident: d_ident,
                 kind: assoc_item,
                 tokens: None,
             });
             Annotatable::AssocItem(d_fn, Impl { of_trait: false })
         } else {
-            let mut d_fn =
-                ecx.item(span, d_ident, thin_vec![d_attr, inline_never], ItemKind::Fn(asdf));
+            let mut d_fn = ecx.item(span, thin_vec![d_attr, inline_never], ItemKind::Fn(asdf));
             d_fn.vis = vis;
             Annotatable::Item(d_fn)
         };
@@ -474,6 +546,8 @@ mod llvm_enzyme {
             return body;
         }
 
+        // Everything from here onwards just tries to fullfil the return type. Fun!
+
         // having an active-only return means we'll drop the original return type.
         // So that can be treated identical to not having one in the first place.
         let primal_ret = has_ret(&sig.decl.output) && !x.has_active_only_ret();
@@ -501,86 +575,65 @@ mod llvm_enzyme {
             return body;
         }
 
-        let mut exprs = ThinVec::<P<ast::Expr>>::new();
-        if primal_ret {
-            // We have both primal ret and active floats.
-            // primal ret is first, by construction.
-            exprs.push(primal_call);
-        }
-
-        // Now construct default placeholder for each active float.
-        // Is there something nicer than f32::default() and f64::default()?
+        let mut exprs: P<ast::Expr> = primal_call.clone();
         let d_ret_ty = match d_sig.decl.output {
             FnRetTy::Ty(ref ty) => ty.clone(),
             FnRetTy::Default(span) => {
                 panic!("Did not expect Default ret ty: {:?}", span);
             }
         };
-        let mut d_ret_ty = match d_ret_ty.kind.clone() {
-            TyKind::Tup(ref tys) => tys.clone(),
-            TyKind::Path(_, rustc_ast::Path { segments, .. }) => {
-                if let [segment] = &segments[..]
-                    && segment.args.is_none()
-                {
-                    let id = vec![segments[0].ident];
-                    let kind = TyKind::Path(None, ecx.path(span, id));
-                    let ty = P(rustc_ast::Ty { kind, id: ast::DUMMY_NODE_ID, span, tokens: None });
-                    thin_vec![ty]
-                } else {
-                    panic!("Expected tuple or simple path return type");
-                }
-            }
-            _ => {
-                // We messed up construction of d_sig
-                panic!("Did not expect non-tuple ret ty: {:?}", d_ret_ty);
-            }
-        };
-
-        if x.mode.is_fwd() && x.ret_activity == DiffActivity::Dual {
-            assert!(d_ret_ty.len() == 2);
-            // both should be identical, by construction
-            let arg = d_ret_ty[0].kind.is_simple_path().unwrap();
-            let arg2 = d_ret_ty[1].kind.is_simple_path().unwrap();
-            assert!(arg == arg2);
-            let sl: Vec<Symbol> = vec![arg, kw::Default];
-            let tmp = ecx.def_site_path(&sl);
-            let default_call_expr = ecx.expr_path(ecx.path(span, tmp));
-            let default_call_expr = ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]);
-            exprs.push(default_call_expr);
-        } else if x.mode.is_rev() {
-            if primal_ret {
-                // We have extra handling above for the primal ret
-                d_ret_ty = d_ret_ty[1..].to_vec().into();
-            }
 
-            for arg in d_ret_ty.iter() {
-                let arg = arg.kind.is_simple_path().unwrap();
-                let sl: Vec<Symbol> = vec![arg, kw::Default];
-                let tmp = ecx.def_site_path(&sl);
-                let default_call_expr = ecx.expr_path(ecx.path(span, tmp));
+        if x.mode.is_fwd() {
+            // Fwd mode is easy. If the return activity is Const, we support arbitrary types.
+            // Otherwise, we only support a scalar, a pair of scalars, or an array of scalars.
+            // We checked that (on a best-effort base) in the preceding gen_enzyme_decl function.
+            // In all three cases, we can return `std::hint::black_box(<T>::default())`.
+            if x.ret_activity == DiffActivity::Const {
+                // Here we call the primal function, since our dummy function has the same return
+                // type due to the Const return activity.
+                exprs = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![exprs]);
+            } else {
+                let q = QSelf { ty: d_ret_ty.clone(), path_span: span, position: 0 };
+                let y =
+                    ExprKind::Path(Some(P(q)), ecx.path_ident(span, Ident::from_str("default")));
+                let default_call_expr = ecx.expr(span, y);
                 let default_call_expr =
                     ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]);
-                exprs.push(default_call_expr);
-            }
-        }
-
-        let ret: P<ast::Expr>;
-        match &exprs[..] {
-            [] => {
-                assert!(!has_ret(&d_sig.decl.output));
-                // We don't have to match the return type.
-                return body;
+                exprs = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![default_call_expr]);
             }
-            [arg] => {
-                ret = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![arg.clone()]);
-            }
-            args => {
-                let ret_tuple: P<ast::Expr> = ecx.expr_tuple(span, args.into());
-                ret = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![ret_tuple]);
+        } else if x.mode.is_rev() {
+            if x.width == 1 {
+                // We either have `-> ArbitraryType` or `-> (ArbitraryType, repeated_float_scalars)`.
+                match d_ret_ty.kind {
+                    TyKind::Tup(ref args) => {
+                        // We have a tuple return type. We need to create a tuple of the same size
+                        // and fill it with default values.
+                        let mut exprs2 = thin_vec![exprs];
+                        for arg in args.iter().skip(1) {
+                            let arg = arg.kind.is_simple_path().unwrap();
+                            let sl: Vec<Symbol> = vec![arg, kw::Default];
+                            let tmp = ecx.def_site_path(&sl);
+                            let default_call_expr = ecx.expr_path(ecx.path(span, tmp));
+                            let default_call_expr =
+                                ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]);
+                            exprs2.push(default_call_expr);
+                        }
+                        exprs = ecx.expr_tuple(new_decl_span, exprs2);
+                    }
+                    _ => {
+                        // Interestingly, even the `-> ArbitraryType` case
+                        // ends up getting matched and handled correctly above,
+                        // so we don't have to handle any other case for now.
+                        panic!("Unsupported return type: {:?}", d_ret_ty);
+                    }
+                }
             }
+            exprs = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![exprs]);
+        } else {
+            unreachable!("Unsupported mode: {:?}", x.mode);
         }
-        assert!(has_ret(&d_sig.decl.output));
-        body.stmts.push(ecx.stmt_expr(ret));
+
+        body.stmts.push(ecx.stmt_expr(exprs));
 
         body
     }
@@ -688,50 +741,55 @@ mod llvm_enzyme {
             match activity {
                 DiffActivity::Active => {
                     act_ret.push(arg.ty.clone());
+                    // if width =/= 1, then push [arg.ty; width] to act_ret
                 }
                 DiffActivity::ActiveOnly => {
                     // We will add the active scalar to the return type.
                     // This is handled later.
                 }
                 DiffActivity::Duplicated | DiffActivity::DuplicatedOnly => {
-                    let mut shadow_arg = arg.clone();
-                    // We += into the shadow in reverse mode.
-                    shadow_arg.ty = P(assure_mut_ref(&arg.ty));
-                    let old_name = if let PatKind::Ident(_, ident, _) = arg.pat.kind {
-                        ident.name
-                    } else {
-                        debug!("{:#?}", &shadow_arg.pat);
-                        panic!("not an ident?");
-                    };
-                    let name: String = format!("d{}", old_name);
-                    new_inputs.push(name.clone());
-                    let ident = Ident::from_str_and_span(&name, shadow_arg.pat.span);
-                    shadow_arg.pat = P(ast::Pat {
-                        id: ast::DUMMY_NODE_ID,
-                        kind: PatKind::Ident(BindingMode::NONE, ident, None),
-                        span: shadow_arg.pat.span,
-                        tokens: shadow_arg.pat.tokens.clone(),
-                    });
-                    d_inputs.push(shadow_arg);
+                    for i in 0..x.width {
+                        let mut shadow_arg = arg.clone();
+                        // We += into the shadow in reverse mode.
+                        shadow_arg.ty = P(assure_mut_ref(&arg.ty));
+                        let old_name = if let PatKind::Ident(_, ident, _) = arg.pat.kind {
+                            ident.name
+                        } else {
+                            debug!("{:#?}", &shadow_arg.pat);
+                            panic!("not an ident?");
+                        };
+                        let name: String = format!("d{}_{}", old_name, i);
+                        new_inputs.push(name.clone());
+                        let ident = Ident::from_str_and_span(&name, shadow_arg.pat.span);
+                        shadow_arg.pat = P(ast::Pat {
+                            id: ast::DUMMY_NODE_ID,
+                            kind: PatKind::Ident(BindingMode::NONE, ident, None),
+                            span: shadow_arg.pat.span,
+                            tokens: shadow_arg.pat.tokens.clone(),
+                        });
+                        d_inputs.push(shadow_arg.clone());
+                    }
                 }
                 DiffActivity::Dual | DiffActivity::DualOnly => {
-                    let mut shadow_arg = arg.clone();
-                    let old_name = if let PatKind::Ident(_, ident, _) = arg.pat.kind {
-                        ident.name
-                    } else {
-                        debug!("{:#?}", &shadow_arg.pat);
-                        panic!("not an ident?");
-                    };
-                    let name: String = format!("b{}", old_name);
-                    new_inputs.push(name.clone());
-                    let ident = Ident::from_str_and_span(&name, shadow_arg.pat.span);
-                    shadow_arg.pat = P(ast::Pat {
-                        id: ast::DUMMY_NODE_ID,
-                        kind: PatKind::Ident(BindingMode::NONE, ident, None),
-                        span: shadow_arg.pat.span,
-                        tokens: shadow_arg.pat.tokens.clone(),
-                    });
-                    d_inputs.push(shadow_arg);
+                    for i in 0..x.width {
+                        let mut shadow_arg = arg.clone();
+                        let old_name = if let PatKind::Ident(_, ident, _) = arg.pat.kind {
+                            ident.name
+                        } else {
+                            debug!("{:#?}", &shadow_arg.pat);
+                            panic!("not an ident?");
+                        };
+                        let name: String = format!("b{}_{}", old_name, i);
+                        new_inputs.push(name.clone());
+                        let ident = Ident::from_str_and_span(&name, shadow_arg.pat.span);
+                        shadow_arg.pat = P(ast::Pat {
+                            id: ast::DUMMY_NODE_ID,
+                            kind: PatKind::Ident(BindingMode::NONE, ident, None),
+                            span: shadow_arg.pat.span,
+                            tokens: shadow_arg.pat.tokens.clone(),
+                        });
+                        d_inputs.push(shadow_arg.clone());
+                    }
                 }
                 DiffActivity::Const => {
                     // Nothing to do here.
@@ -787,23 +845,48 @@ mod llvm_enzyme {
         d_decl.inputs = d_inputs.into();
 
         if x.mode.is_fwd() {
+            let ty = match d_decl.output {
+                FnRetTy::Ty(ref ty) => ty.clone(),
+                FnRetTy::Default(span) => {
+                    // We want to return std::hint::black_box(()).
+                    let kind = TyKind::Tup(ThinVec::new());
+                    let ty = P(rustc_ast::Ty { kind, id: ast::DUMMY_NODE_ID, span, tokens: None });
+                    d_decl.output = FnRetTy::Ty(ty.clone());
+                    assert!(matches!(x.ret_activity, DiffActivity::None));
+                    // this won't be used below, so any type would be fine.
+                    ty
+                }
+            };
+
             if let DiffActivity::Dual = x.ret_activity {
-                let ty = match d_decl.output {
-                    FnRetTy::Ty(ref ty) => ty.clone(),
-                    FnRetTy::Default(span) => {
-                        panic!("Did not expect Default ret ty: {:?}", span);
-                    }
+                let kind = if x.width == 1 {
+                    // Dual can only be used for f32/f64 ret.
+                    // In that case we return now a tuple with two floats.
+                    TyKind::Tup(thin_vec![ty.clone(), ty.clone()])
+                } else {
+                    // We have to return [T; width+1], +1 for the primal return.
+                    let anon_const = rustc_ast::AnonConst {
+                        id: ast::DUMMY_NODE_ID,
+                        value: ecx.expr_usize(span, 1 + x.width as usize),
+                    };
+                    TyKind::Array(ty.clone(), anon_const)
                 };
-                // Dual can only be used for f32/f64 ret.
-                // In that case we return now a tuple with two floats.
-                let kind = TyKind::Tup(thin_vec![ty.clone(), ty.clone()]);
                 let ty = P(rustc_ast::Ty { kind, id: ty.id, span: ty.span, tokens: None });
                 d_decl.output = FnRetTy::Ty(ty);
             }
             if let DiffActivity::DualOnly = x.ret_activity {
                 // No need to change the return type,
-                // we will just return the shadow in place
-                // of the primal return.
+                // we will just return the shadow in place of the primal return.
+                // However, if we have a width > 1, then we don't return -> T, but -> [T; width]
+                if x.width > 1 {
+                    let anon_const = rustc_ast::AnonConst {
+                        id: ast::DUMMY_NODE_ID,
+                        value: ecx.expr_usize(span, x.width as usize),
+                    };
+                    let kind = TyKind::Array(ty.clone(), anon_const);
+                    let ty = P(rustc_ast::Ty { kind, id: ty.id, span: ty.span, tokens: None });
+                    d_decl.output = FnRetTy::Ty(ty);
+                }
             }
         }
 
diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs
index c3656e8244f..44cf215c662 100644
--- a/compiler/rustc_builtin_macros/src/deriving/clone.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs
@@ -34,8 +34,8 @@ pub(crate) fn expand_deriving_clone(
     let is_simple;
     match item {
         Annotatable::Item(annitem) => match &annitem.kind {
-            ItemKind::Struct(_, Generics { params, .. })
-            | ItemKind::Enum(_, Generics { params, .. }) => {
+            ItemKind::Struct(_, _, Generics { params, .. })
+            | ItemKind::Enum(_, _, Generics { params, .. }) => {
                 let container_id = cx.current_expansion.id.expn_data().parent.expect_local();
                 let has_derive_copy = cx.resolver.has_derive_copy(container_id);
                 if has_derive_copy
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
index 7958e037555..aa01da3151e 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
@@ -21,7 +21,7 @@ pub(crate) fn expand_deriving_partial_ord(
 
     // Order in which to perform matching
     let discr_then_data = if let Annotatable::Item(item) = item
-        && let ItemKind::Enum(def, _) = &item.kind
+        && let ItemKind::Enum(_, def, _) = &item.kind
     {
         let dataful: Vec<bool> = def.variants.iter().map(|v| !v.data.fields().is_empty()).collect();
         match dataful.iter().filter(|&&b| b).count() {
diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
index 46b79e09780..446d8afeedd 100644
--- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
@@ -30,7 +30,7 @@ pub(crate) fn expand_deriving_coerce_pointee(
     item.visit_with(&mut DetectNonGenericPointeeAttr { cx });
 
     let (name_ident, generics) = if let Annotatable::Item(aitem) = item
-        && let ItemKind::Struct(struct_data, g) = &aitem.kind
+        && let ItemKind::Struct(ident, struct_data, g) = &aitem.kind
     {
         if !matches!(
             struct_data,
@@ -40,7 +40,7 @@ pub(crate) fn expand_deriving_coerce_pointee(
             cx.dcx().emit_err(RequireOneField { span });
             return;
         }
-        (aitem.ident, g)
+        (*ident, g)
     } else {
         cx.dcx().emit_err(RequireTransparent { span });
         return;
@@ -108,7 +108,6 @@ pub(crate) fn expand_deriving_coerce_pointee(
         push(Annotatable::Item(
             cx.item(
                 span,
-                Ident::empty(),
                 attrs.clone(),
                 ast::ItemKind::Impl(Box::new(ast::Impl {
                     safety: ast::Safety::Default,
@@ -153,7 +152,6 @@ pub(crate) fn expand_deriving_coerce_pointee(
         let trait_ref = cx.trait_ref(trait_path);
         let item = cx.item(
             span,
-            Ident::empty(),
             attrs.clone(),
             ast::ItemKind::Impl(Box::new(ast::Impl {
                 safety: ast::Safety::Default,
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 03ee59de70e..b9197be4442 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -487,28 +487,28 @@ impl<'a> TraitDef<'a> {
                 );
 
                 let newitem = match &item.kind {
-                    ast::ItemKind::Struct(struct_def, generics) => self.expand_struct_def(
+                    ast::ItemKind::Struct(ident, struct_def, generics) => self.expand_struct_def(
                         cx,
                         struct_def,
-                        item.ident,
+                        *ident,
                         generics,
                         from_scratch,
                         is_packed,
                     ),
-                    ast::ItemKind::Enum(enum_def, generics) => {
+                    ast::ItemKind::Enum(ident, enum_def, generics) => {
                         // We ignore `is_packed` here, because `repr(packed)`
                         // enums cause an error later on.
                         //
                         // This can only cause further compilation errors
                         // downstream in blatantly illegal code, so it is fine.
-                        self.expand_enum_def(cx, enum_def, item.ident, generics, from_scratch)
+                        self.expand_enum_def(cx, enum_def, *ident, generics, from_scratch)
                     }
-                    ast::ItemKind::Union(struct_def, generics) => {
+                    ast::ItemKind::Union(ident, struct_def, generics) => {
                         if self.supports_unions {
                             self.expand_struct_def(
                                 cx,
                                 struct_def,
-                                item.ident,
+                                *ident,
                                 generics,
                                 from_scratch,
                                 is_packed,
@@ -596,7 +596,6 @@ impl<'a> TraitDef<'a> {
             P(ast::AssocItem {
                 id: ast::DUMMY_NODE_ID,
                 span: self.span,
-                ident,
                 vis: ast::Visibility {
                     span: self.span.shrink_to_lo(),
                     kind: ast::VisibilityKind::Inherited,
@@ -605,6 +604,7 @@ impl<'a> TraitDef<'a> {
                 attrs: ast::AttrVec::new(),
                 kind: ast::AssocItemKind::Type(Box::new(ast::TyAlias {
                     defaultness: ast::Defaultness::Final,
+                    ident,
                     generics: Generics::default(),
                     where_clauses: ast::TyAliasWhereClauses::default(),
                     bounds: Vec::new(),
@@ -789,7 +789,6 @@ impl<'a> TraitDef<'a> {
 
         cx.item(
             self.span,
-            Ident::empty(),
             attrs,
             ast::ItemKind::Impl(Box::new(ast::Impl {
                 safety: ast::Safety::Default,
@@ -1033,10 +1032,10 @@ impl<'a> MethodDef<'a> {
                 kind: ast::VisibilityKind::Inherited,
                 tokens: None,
             },
-            ident: method_ident,
             kind: ast::AssocItemKind::Fn(Box::new(ast::Fn {
                 defaultness,
                 sig,
+                ident: method_ident,
                 generics: fn_generics,
                 contract: None,
                 body: Some(body_block),
diff --git a/compiler/rustc_builtin_macros/src/edition_panic.rs b/compiler/rustc_builtin_macros/src/edition_panic.rs
index b39c9861fd6..ccfcc3079eb 100644
--- a/compiler/rustc_builtin_macros/src/edition_panic.rs
+++ b/compiler/rustc_builtin_macros/src/edition_panic.rs
@@ -74,11 +74,11 @@ pub(crate) fn use_panic_2021(mut span: Span) -> bool {
     // (To avoid using the edition of e.g. the assert!() or debug_assert!() definition.)
     loop {
         let expn = span.ctxt().outer_expn_data();
-        if let Some(features) = expn.allow_internal_unstable {
-            if features.iter().any(|&f| f == sym::edition_panic) {
-                span = expn.call_site;
-                continue;
-            }
+        if let Some(features) = expn.allow_internal_unstable
+            && features.contains(&sym::edition_panic)
+        {
+            span = expn.call_site;
+            continue;
         }
         break expn.edition >= Edition::Edition2021;
     }
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index 30597944124..4bbe212f429 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -203,6 +203,14 @@ mod autodiff {
     }
 
     #[derive(Diagnostic)]
+    #[diag(builtin_macros_autodiff_width)]
+    pub(crate) struct AutoDiffInvalidWidth {
+        #[primary_span]
+        pub(crate) span: Span,
+        pub(crate) width: u128,
+    }
+
+    #[derive(Diagnostic)]
     #[diag(builtin_macros_autodiff)]
     pub(crate) struct AutoDiffInvalidApplication {
         #[primary_span]
diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs
index 90d79235820..4b1958bce32 100644
--- a/compiler/rustc_builtin_macros/src/global_allocator.rs
+++ b/compiler/rustc_builtin_macros/src/global_allocator.rs
@@ -25,15 +25,15 @@ pub(crate) fn expand(
 
     // Allow using `#[global_allocator]` on an item statement
     // FIXME - if we get deref patterns, use them to reduce duplication here
-    let (item, is_stmt, ty_span) = if let Annotatable::Item(item) = &item
-        && let ItemKind::Static(box ast::StaticItem { ty, .. }) = &item.kind
+    let (item, ident, is_stmt, ty_span) = if let Annotatable::Item(item) = &item
+        && let ItemKind::Static(box ast::StaticItem { ident, ty, .. }) = &item.kind
     {
-        (item, false, ecx.with_def_site_ctxt(ty.span))
+        (item, *ident, false, ecx.with_def_site_ctxt(ty.span))
     } else if let Annotatable::Stmt(stmt) = &item
         && let StmtKind::Item(item) = &stmt.kind
-        && let ItemKind::Static(box ast::StaticItem { ty, .. }) = &item.kind
+        && let ItemKind::Static(box ast::StaticItem { ident, ty, .. }) = &item.kind
     {
-        (item, true, ecx.with_def_site_ctxt(ty.span))
+        (item, *ident, true, ecx.with_def_site_ctxt(ty.span))
     } else {
         ecx.dcx().emit_err(errors::AllocMustStatics { span: item.span() });
         return vec![orig_item];
@@ -41,7 +41,7 @@ pub(crate) fn expand(
 
     // Generate a bunch of new items using the AllocFnFactory
     let span = ecx.with_def_site_ctxt(item.span);
-    let f = AllocFnFactory { span, ty_span, global: item.ident, cx: ecx };
+    let f = AllocFnFactory { span, ty_span, global: ident, cx: ecx };
 
     // Generate item statements for the allocator methods.
     let stmts = ALLOCATOR_METHODS.iter().map(|method| f.allocator_fn(method)).collect();
@@ -80,17 +80,13 @@ impl AllocFnFactory<'_, '_> {
         let kind = ItemKind::Fn(Box::new(Fn {
             defaultness: ast::Defaultness::Final,
             sig,
+            ident: Ident::from_str_and_span(&global_fn_name(method.name), self.span),
             generics: Generics::default(),
             contract: None,
             body,
             define_opaque: None,
         }));
-        let item = self.cx.item(
-            self.span,
-            Ident::from_str_and_span(&global_fn_name(method.name), self.span),
-            self.attrs(),
-            kind,
-        );
+        let item = self.cx.item(self.span, self.attrs(), kind);
         self.cx.stmt_item(self.ty_span, item)
     }
 
diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
index ee6475c8b8e..8862965c053 100644
--- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
+++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
@@ -92,7 +92,12 @@ impl<'a> CollectProcMacros<'a> {
         }
     }
 
-    fn collect_custom_derive(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) {
+    fn collect_custom_derive(
+        &mut self,
+        item: &'a ast::Item,
+        function_name: Ident,
+        attr: &'a ast::Attribute,
+    ) {
         let Some((trait_name, proc_attrs)) =
             parse_macro_name_and_helper_attrs(self.dcx, attr, "derive")
         else {
@@ -104,7 +109,7 @@ impl<'a> CollectProcMacros<'a> {
                 id: item.id,
                 span: item.span,
                 trait_name,
-                function_name: item.ident,
+                function_name,
                 attrs: proc_attrs,
             }));
         } else {
@@ -118,12 +123,12 @@ impl<'a> CollectProcMacros<'a> {
         }
     }
 
-    fn collect_attr_proc_macro(&mut self, item: &'a ast::Item) {
+    fn collect_attr_proc_macro(&mut self, item: &'a ast::Item, function_name: Ident) {
         if self.in_root && item.vis.kind.is_pub() {
             self.macros.push(ProcMacro::Attr(ProcMacroDef {
                 id: item.id,
                 span: item.span,
-                function_name: item.ident,
+                function_name,
             }));
         } else {
             let msg = if !self.in_root {
@@ -136,12 +141,12 @@ impl<'a> CollectProcMacros<'a> {
         }
     }
 
-    fn collect_bang_proc_macro(&mut self, item: &'a ast::Item) {
+    fn collect_bang_proc_macro(&mut self, item: &'a ast::Item, function_name: Ident) {
         if self.in_root && item.vis.kind.is_pub() {
             self.macros.push(ProcMacro::Bang(ProcMacroDef {
                 id: item.id,
                 span: item.span,
-                function_name: item.ident,
+                function_name,
             }));
         } else {
             let msg = if !self.in_root {
@@ -165,12 +170,6 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
             }
         }
 
-        // First up, make sure we're checking a bare function. If we're not then
-        // we're just not interested in this item.
-        //
-        // If we find one, try to locate a `#[proc_macro_derive]` attribute on it.
-        let is_fn = matches!(item.kind, ast::ItemKind::Fn(..));
-
         let mut found_attr: Option<&'a ast::Attribute> = None;
 
         for attr in &item.attrs {
@@ -214,7 +213,11 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
             return;
         };
 
-        if !is_fn {
+        // Make sure we're checking a bare function. If we're not then we're
+        // just not interested any further in this item.
+        let fn_ident = if let ast::ItemKind::Fn(fn_) = &item.kind {
+            fn_.ident
+        } else {
             self.dcx
                 .create_err(errors::AttributeOnlyBeUsedOnBareFunctions {
                     span: attr.span,
@@ -222,7 +225,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
                 })
                 .emit();
             return;
-        }
+        };
 
         if self.is_test_crate {
             return;
@@ -238,12 +241,13 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
             return;
         }
 
+        // Try to locate a `#[proc_macro_derive]` attribute.
         if attr.has_name(sym::proc_macro_derive) {
-            self.collect_custom_derive(item, attr);
+            self.collect_custom_derive(item, fn_ident, attr);
         } else if attr.has_name(sym::proc_macro_attribute) {
-            self.collect_attr_proc_macro(item);
+            self.collect_attr_proc_macro(item, fn_ident);
         } else if attr.has_name(sym::proc_macro) {
-            self.collect_bang_proc_macro(item);
+            self.collect_bang_proc_macro(item, fn_ident);
         };
 
         let prev_in_root = mem::replace(&mut self.in_root, false);
@@ -278,7 +282,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P<ast::Item> {
     let span = DUMMY_SP.with_def_site_ctxt(expn_id.to_expn_id());
 
     let proc_macro = Ident::new(sym::proc_macro, span);
-    let krate = cx.item(span, proc_macro, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None));
+    let krate = cx.item(span, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None, proc_macro));
 
     let bridge = Ident::new(sym::bridge, span);
     let client = Ident::new(sym::client, span);
diff --git a/compiler/rustc_builtin_macros/src/standard_library_imports.rs b/compiler/rustc_builtin_macros/src/standard_library_imports.rs
index ba63b185e09..a1ee53b7ca2 100644
--- a/compiler/rustc_builtin_macros/src/standard_library_imports.rs
+++ b/compiler/rustc_builtin_macros/src/standard_library_imports.rs
@@ -43,9 +43,8 @@ pub fn inject(
 
     let item = cx.item(
         span,
-        Ident::new(name, ident_span),
         thin_vec![cx.attr_word(sym::macro_use, span)],
-        ast::ItemKind::ExternCrate(None),
+        ast::ItemKind::ExternCrate(None, Ident::new(name, ident_span)),
     );
 
     krate.items.insert(0, item);
@@ -68,7 +67,6 @@ pub fn inject(
     // Inject the relevant crate's prelude.
     let use_item = cx.item(
         span,
-        Ident::empty(),
         thin_vec![cx.attr_word(sym::prelude_import, span)],
         ast::ItemKind::Use(ast::UseTree {
             prefix: cx.path(span, import_path),
diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs
index 239f8657284..1cef4f9514c 100644
--- a/compiler/rustc_builtin_macros/src/test.rs
+++ b/compiler/rustc_builtin_macros/src/test.rs
@@ -51,21 +51,28 @@ pub(crate) fn expand_test_case(
             return vec![];
         }
     };
-    item = item.map(|mut item| {
-        let test_path_symbol = Symbol::intern(&item_path(
-            // skip the name of the root module
-            &ecx.current_expansion.module.mod_path[1..],
-            &item.ident,
-        ));
-        item.vis = ast::Visibility {
-            span: item.vis.span,
-            kind: ast::VisibilityKind::Public,
-            tokens: None,
-        };
-        item.ident.span = item.ident.span.with_ctxt(sp.ctxt());
-        item.attrs.push(ecx.attr_name_value_str(sym::rustc_test_marker, test_path_symbol, sp));
-        item
-    });
+
+    // `#[test_case]` is valid on functions, consts, and statics. Only modify
+    // the item in those cases.
+    match &mut item.kind {
+        ast::ItemKind::Fn(box ast::Fn { ident, .. })
+        | ast::ItemKind::Const(box ast::ConstItem { ident, .. })
+        | ast::ItemKind::Static(box ast::StaticItem { ident, .. }) => {
+            ident.span = ident.span.with_ctxt(sp.ctxt());
+            let test_path_symbol = Symbol::intern(&item_path(
+                // skip the name of the root module
+                &ecx.current_expansion.module.mod_path[1..],
+                ident,
+            ));
+            item.vis = ast::Visibility {
+                span: item.vis.span,
+                kind: ast::VisibilityKind::Public,
+                tokens: None,
+            };
+            item.attrs.push(ecx.attr_name_value_str(sym::rustc_test_marker, test_path_symbol, sp));
+        }
+        _ => {}
+    }
 
     let ret = if is_stmt {
         Annotatable::Stmt(P(ecx.stmt_item(item.span, item)))
@@ -162,17 +169,17 @@ pub(crate) fn expand_test_or_bench(
     let ret_ty_sp = cx.with_def_site_ctxt(fn_.sig.decl.output.span());
     let attr_sp = cx.with_def_site_ctxt(attr_sp);
 
-    let test_id = Ident::new(sym::test, attr_sp);
+    let test_ident = Ident::new(sym::test, attr_sp);
 
     // creates test::$name
-    let test_path = |name| cx.path(ret_ty_sp, vec![test_id, Ident::from_str_and_span(name, sp)]);
+    let test_path = |name| cx.path(ret_ty_sp, vec![test_ident, Ident::from_str_and_span(name, sp)]);
 
     // creates test::ShouldPanic::$name
     let should_panic_path = |name| {
         cx.path(
             sp,
             vec![
-                test_id,
+                test_ident,
                 Ident::from_str_and_span("ShouldPanic", sp),
                 Ident::from_str_and_span(name, sp),
             ],
@@ -184,7 +191,7 @@ pub(crate) fn expand_test_or_bench(
         cx.path(
             sp,
             vec![
-                test_id,
+                test_ident,
                 Ident::from_str_and_span("TestType", sp),
                 Ident::from_str_and_span(name, sp),
             ],
@@ -223,7 +230,7 @@ pub(crate) fn expand_test_or_bench(
                             // super::$test_fn(b)
                             cx.expr_call(
                                 ret_ty_sp,
-                                cx.expr_path(cx.path(sp, vec![item.ident])),
+                                cx.expr_path(cx.path(sp, vec![fn_.ident])),
                                 thin_vec![cx.expr_ident(sp, b)],
                             ),
                         ],
@@ -249,7 +256,7 @@ pub(crate) fn expand_test_or_bench(
                             // $test_fn()
                             cx.expr_call(
                                 ret_ty_sp,
-                                cx.expr_path(cx.path(sp, vec![item.ident])),
+                                cx.expr_path(cx.path(sp, vec![fn_.ident])),
                                 ThinVec::new(),
                             ), // )
                         ],
@@ -262,15 +269,14 @@ pub(crate) fn expand_test_or_bench(
     let test_path_symbol = Symbol::intern(&item_path(
         // skip the name of the root module
         &cx.current_expansion.module.mod_path[1..],
-        &item.ident,
+        &fn_.ident,
     ));
 
-    let location_info = get_location_info(cx, &item);
+    let location_info = get_location_info(cx, &fn_);
 
     let mut test_const =
         cx.item(
             sp,
-            Ident::new(item.ident.name, sp),
             thin_vec![
                 // #[cfg(test)]
                 cx.attr_nested_word(sym::cfg, sym::test, attr_sp),
@@ -283,6 +289,7 @@ pub(crate) fn expand_test_or_bench(
             ast::ItemKind::Const(
                 ast::ConstItem {
                     defaultness: ast::Defaultness::Final,
+                    ident: Ident::new(fn_.ident.name, sp),
                     generics: ast::Generics::default(),
                     ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))),
                     define_opaque: None,
@@ -380,7 +387,8 @@ pub(crate) fn expand_test_or_bench(
     });
 
     // extern crate test
-    let test_extern = cx.item(sp, test_id, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None));
+    let test_extern =
+        cx.item(sp, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None, test_ident));
 
     debug!("synthetic test item:\n{}\n", pprust::item_to_string(&test_const));
 
@@ -434,8 +442,8 @@ fn not_testable_error(cx: &ExtCtxt<'_>, attr_sp: Span, item: Option<&ast::Item>)
         .emit();
 }
 
-fn get_location_info(cx: &ExtCtxt<'_>, item: &ast::Item) -> (Symbol, usize, usize, usize, usize) {
-    let span = item.ident.span;
+fn get_location_info(cx: &ExtCtxt<'_>, fn_: &ast::Fn) -> (Symbol, usize, usize, usize, usize) {
+    let span = fn_.ident.span;
     let (source_file, lo_line, lo_col, hi_line, hi_col) =
         cx.sess.source_map().span_to_location_info(span);
 
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index 768b459ec5e..56a67b0534d 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -134,7 +134,8 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
         if let Some(name) = get_test_name(&item) {
             debug!("this is a test item");
 
-            let test = Test { span: item.span, ident: item.ident, name };
+            // `unwrap` is ok because only functions, consts, and static should reach here.
+            let test = Test { span: item.span, ident: item.kind.ident().unwrap(), name };
             self.tests.push(test);
         }
 
@@ -142,19 +143,12 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
         // mods or tests inside of functions will break things
         if let ast::ItemKind::Mod(
             _,
+            _,
             ModKind::Loaded(.., ast::ModSpans { inner_span: span, .. }, _),
         ) = item.kind
         {
             let prev_tests = mem::take(&mut self.tests);
-            walk_item_kind(
-                &mut item.kind,
-                item.span,
-                item.id,
-                &mut item.ident,
-                &mut item.vis,
-                (),
-                self,
-            );
+            walk_item_kind(&mut item.kind, item.span, item.id, &mut item.vis, (), self);
             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.
@@ -181,9 +175,9 @@ impl<'a> Visitor<'a> for InnerItemLinter<'_> {
 }
 
 fn entry_point_type(item: &ast::Item, at_root: bool) -> EntryPointType {
-    match item.kind {
-        ast::ItemKind::Fn(..) => {
-            rustc_ast::entry::entry_point_type(&item.attrs, at_root, Some(item.ident.name))
+    match &item.kind {
+        ast::ItemKind::Fn(fn_) => {
+            rustc_ast::entry::entry_point_type(&item.attrs, at_root, Some(fn_.ident.name))
         }
         _ => EntryPointType::None,
     }
@@ -295,7 +289,7 @@ fn generate_test_harness(
 fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
     let sp = cx.def_site;
     let ecx = &cx.ext_cx;
-    let test_id = Ident::new(sym::test, sp);
+    let test_ident = Ident::new(sym::test, sp);
 
     let runner_name = match cx.panic_strategy {
         PanicStrategy::Unwind => "test_main_static",
@@ -303,10 +297,9 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
     };
 
     // test::test_main_static(...)
-    let mut test_runner = cx
-        .test_runner
-        .clone()
-        .unwrap_or_else(|| ecx.path(sp, vec![test_id, Ident::from_str_and_span(runner_name, sp)]));
+    let mut test_runner = cx.test_runner.clone().unwrap_or_else(|| {
+        ecx.path(sp, vec![test_ident, Ident::from_str_and_span(runner_name, sp)])
+    });
 
     test_runner.span = sp;
 
@@ -317,7 +310,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
     // extern crate test
     let test_extern_stmt = ecx.stmt_item(
         sp,
-        ecx.item(sp, test_id, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None)),
+        ecx.item(sp, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None, test_ident)),
     );
 
     // #[rustc_main]
@@ -340,23 +333,24 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
     let decl = ecx.fn_decl(ThinVec::new(), ast::FnRetTy::Ty(main_ret_ty));
     let sig = ast::FnSig { decl, header: ast::FnHeader::default(), span: sp };
     let defaultness = ast::Defaultness::Final;
+
+    // Honor the reexport_test_harness_main attribute
+    let main_ident = match cx.reexport_test_harness_main {
+        Some(sym) => Ident::new(sym, sp.with_ctxt(SyntaxContext::root())),
+        None => Ident::new(sym::main, sp),
+    };
+
     let main = ast::ItemKind::Fn(Box::new(ast::Fn {
         defaultness,
         sig,
+        ident: main_ident,
         generics: ast::Generics::default(),
         contract: None,
         body: Some(main_body),
         define_opaque: None,
     }));
 
-    // Honor the reexport_test_harness_main attribute
-    let main_id = match cx.reexport_test_harness_main {
-        Some(sym) => Ident::new(sym, sp.with_ctxt(SyntaxContext::root())),
-        None => Ident::new(sym::main, sp),
-    };
-
     let main = P(ast::Item {
-        ident: main_id,
         attrs: thin_vec![main_attr, coverage_attr, doc_hidden_attr],
         id: ast::DUMMY_NODE_ID,
         kind: main,
diff --git a/compiler/rustc_codegen_cranelift/.cirrus.yml b/compiler/rustc_codegen_cranelift/.cirrus.yml
index 1ec99eb3d17..ee5de8b42f4 100644
--- a/compiler/rustc_codegen_cranelift/.cirrus.yml
+++ b/compiler/rustc_codegen_cranelift/.cirrus.yml
@@ -1,20 +1,21 @@
-task:
-  name: freebsd
-  freebsd_instance:
-    image: freebsd-13-2-release-amd64
-  setup_rust_script:
-    - pkg install -y git-tiny binutils
-    - curl https://sh.rustup.rs -sSf --output rustup.sh
-    - sh rustup.sh --default-toolchain none -y --profile=minimal
-  target_cache:
-    folder: build/cg_clif
-  prepare_script:
-    - . $HOME/.cargo/env
-    - ./y.sh prepare
-  test_script:
-    - . $HOME/.cargo/env
-    # Disabling incr comp reduces cache size and incr comp doesn't save as much
-    # on CI anyway.
-    - export CARGO_BUILD_INCREMENTAL=false
-    # Skip rand as it fails on FreeBSD due to rust-random/rand#1355
-    - ./y.sh test --skip-test test.rust-random/rand
+# FIXME re-enable once https://github.com/rust-lang/rust/issues/134863 is fixed.
+# task:
+#   name: freebsd
+#   freebsd_instance:
+#     image: freebsd-13-2-release-amd64
+#   setup_rust_script:
+#     - pkg install -y git-tiny binutils
+#     - curl https://sh.rustup.rs -sSf --output rustup.sh
+#     - sh rustup.sh --default-toolchain none -y --profile=minimal
+#   target_cache:
+#     folder: build/cg_clif
+#   prepare_script:
+#     - . $HOME/.cargo/env
+#     - ./y.sh prepare
+#   test_script:
+#     - . $HOME/.cargo/env
+#     # Disabling incr comp reduces cache size and incr comp doesn't save as much
+#     # on CI anyway.
+#     - export CARGO_BUILD_INCREMENTAL=false
+#     # Skip rand as it fails on FreeBSD due to rust-random/rand#1355
+#     - ./y.sh test --skip-test test.rust-random/rand
diff --git a/compiler/rustc_codegen_cranelift/.github/actions/github-release/README.md b/compiler/rustc_codegen_cranelift/.github/actions/github-release/README.md
deleted file mode 100644
index c70ba8f4953..00000000000
--- a/compiler/rustc_codegen_cranelift/.github/actions/github-release/README.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# github-release
-
-An action used to publish GitHub releases for `wasmtime`.
-
-As of the time of this writing there's a few actions floating around which
-perform github releases but they all tend to have their set of drawbacks.
-Additionally nothing handles deleting releases which we need for our rolling
-`dev` release.
-
-To handle all this this action rolls-its-own implementation using the
-actions/toolkit repository and packages published there. These run in a Docker
-container and take various inputs to orchestrate the release from the build.
-
-More comments can be found in `main.js`.
-
-Testing this is really hard. If you want to try though run `npm install` and
-then `node main.js`. You'll have to configure a bunch of env vars though to get
-anything reasonably working.
diff --git a/compiler/rustc_codegen_cranelift/.github/actions/github-release/action.yml b/compiler/rustc_codegen_cranelift/.github/actions/github-release/action.yml
deleted file mode 100644
index 36e5209f50c..00000000000
--- a/compiler/rustc_codegen_cranelift/.github/actions/github-release/action.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-name: 'rustc_codegen_cranelift github releases'
-description: 'rustc_codegen_cranelift github releases'
-inputs:
-  token:
-    description: ''
-    required: true
-  files:
-    description: ''
-    required: true
-runs:
-  using: 'node16'
-  main: 'main.js'
diff --git a/compiler/rustc_codegen_cranelift/.github/actions/github-release/main.js b/compiler/rustc_codegen_cranelift/.github/actions/github-release/main.js
deleted file mode 100644
index 1eb2b7f23b2..00000000000
--- a/compiler/rustc_codegen_cranelift/.github/actions/github-release/main.js
+++ /dev/null
@@ -1,162 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-const core = require('@actions/core');
-const path = require("path");
-const fs = require("fs");
-const github = require('@actions/github');
-const glob = require('glob');
-
-function sleep(milliseconds) {
-  return new Promise(resolve => setTimeout(resolve, milliseconds))
-}
-
-async function runOnce() {
-  // Load all our inputs and env vars. Note that `getInput` reads from `INPUT_*`
-  const files = core.getInput('files');
-  const token = core.getInput('token');
-  const slug = process.env.GITHUB_REPOSITORY;
-  const owner = slug.split('/')[0];
-  const repo = slug.split('/')[1];
-  const sha = process.env.GITHUB_SHA;
-  let name = 'dev';
-  if (process.env.GITHUB_REF.startsWith('refs/tags/v')) {
-    name = process.env.GITHUB_REF.substring(10);
-  }
-
-  core.info(`files: ${files}`);
-  core.info(`name: ${name}`);
-  core.info(`token: ${token}`);
-
-  const octokit = github.getOctokit(token);
-
-  // For the `dev` release we may need to update the tag to point to the new
-  // commit on this branch. All other names should already have tags associated
-  // with them.
-  if (name == 'dev') {
-    let tag = null;
-    try {
-      tag = await octokit.request("GET /repos/:owner/:repo/git/refs/tags/:name", { owner, repo, name });
-      core.info(`found existing tag`);
-      console.log("tag: ", JSON.stringify(tag.data, null, 2));
-    } catch (e) {
-      // ignore if this tag doesn't exist
-      core.info(`no existing tag found`);
-    }
-
-    if (tag === null || tag.data.object.sha !== sha) {
-      core.info(`updating existing tag or creating new one`);
-
-      try {
-        core.info(`updating dev tag`);
-        await octokit.rest.git.updateRef({
-          owner,
-          repo,
-          ref: 'tags/dev',
-          sha,
-          force: true,
-        });
-      } catch (e) {
-        console.log("ERROR: ", JSON.stringify(e.response, null, 2));
-        core.info(`creating dev tag`);
-        try {
-          await octokit.rest.git.createRef({
-            owner,
-            repo,
-            ref: 'refs/tags/dev',
-            sha,
-          });
-        } catch (e) {
-          // we might race with others, so assume someone else has created the
-          // tag by this point.
-          console.log("failed to create tag: ", JSON.stringify(e.response, null, 2));
-        }
-      }
-
-      console.log("double-checking tag is correct");
-      tag = await octokit.request("GET /repos/:owner/:repo/git/refs/tags/:name", { owner, repo, name });
-      if (tag.data.object.sha !== sha) {
-        console.log("tag: ", JSON.stringify(tag.data, null, 2));
-        throw new Error("tag didn't work");
-      }
-    } else {
-      core.info(`existing tag works`);
-    }
-  }
-
-  // Delete a previous release
-  try {
-    core.info(`fetching release`);
-    let release = await octokit.rest.repos.getReleaseByTag({ owner, repo, tag: name });
-    console.log("found release: ", JSON.stringify(release.data, null, 2));
-    await octokit.rest.repos.deleteRelease({
-      owner,
-      repo,
-      release_id: release.data.id,
-    });
-    console.log("deleted release");
-  } catch (e) {
-    console.log("ERROR: ", JSON.stringify(e, null, 2));
-  }
-
-  console.log("creating a release");
-  let release = await octokit.rest.repos.createRelease({
-    owner,
-    repo,
-    tag_name: name,
-    prerelease: name === 'dev',
-  });
-
-  // Delete all assets from a previous run
-  for (const asset of release.data.assets) {
-    console.log(`deleting prior asset ${asset.id}`);
-    await octokit.rest.repos.deleteReleaseAsset({
-      owner,
-      repo,
-      asset_id: asset.id,
-    });
-  }
-
-  // Upload all the relevant assets for this release as just general blobs.
-  for (const file of glob.sync(files)) {
-    const size = fs.statSync(file).size;
-    const name = path.basename(file);
-    core.info(`upload ${file}`);
-    await octokit.rest.repos.uploadReleaseAsset({
-      data: fs.createReadStream(file),
-      headers: { 'content-length': size, 'content-type': 'application/octet-stream' },
-      name,
-      url: release.data.upload_url,
-    });
-  }
-}
-
-async function run() {
-  const retries = 10;
-  for (let i = 0; i < retries; i++) {
-    try {
-      await runOnce();
-      break;
-    } catch (e) {
-      if (i === retries - 1)
-        throw e;
-      logError(e);
-      console.log("RETRYING after 10s");
-      await sleep(10000)
-    }
-  }
-}
-
-function logError(e) {
-  console.log("ERROR: ", e.message);
-  try {
-    console.log(JSON.stringify(e, null, 2));
-  } catch (e) {
-    // ignore json errors for now
-  }
-  console.log(e.stack);
-}
-
-run().catch(err => {
-  logError(err);
-  core.setFailed(err.message);
-});
diff --git a/compiler/rustc_codegen_cranelift/.github/actions/github-release/package-lock.json b/compiler/rustc_codegen_cranelift/.github/actions/github-release/package-lock.json
deleted file mode 100644
index dd3b2a048f0..00000000000
--- a/compiler/rustc_codegen_cranelift/.github/actions/github-release/package-lock.json
+++ /dev/null
@@ -1,571 +0,0 @@
-{
-  "name": "rustc_codegen_cranelift-github-release",
-  "version": "0.0.0",
-  "lockfileVersion": 2,
-  "requires": true,
-  "packages": {
-    "": {
-      "name": "rustc_codegen_cranelift-github-release",
-      "version": "0.0.0",
-      "dependencies": {
-        "@actions/core": "^1.9.1",
-        "@actions/github": "^5.1.0",
-        "glob": "^7.1.5"
-      }
-    },
-    "node_modules/@actions/core": {
-      "version": "1.9.1",
-      "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz",
-      "integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==",
-      "dependencies": {
-        "@actions/http-client": "^2.0.1",
-        "uuid": "^8.3.2"
-      }
-    },
-    "node_modules/@actions/github": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.0.tgz",
-      "integrity": "sha512-tuI80F7JQIhg77ZTTgUAPpVD7ZnP9oHSPN8xw7LOwtA4vEMbAjWJNbmLBfV7xua7r016GyjzWLuec5cs8f/a8A==",
-      "dependencies": {
-        "@actions/http-client": "^2.0.1",
-        "@octokit/core": "^3.6.0",
-        "@octokit/plugin-paginate-rest": "^2.17.0",
-        "@octokit/plugin-rest-endpoint-methods": "^5.13.0"
-      }
-    },
-    "node_modules/@actions/http-client": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
-      "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
-      "dependencies": {
-        "tunnel": "^0.0.6"
-      }
-    },
-    "node_modules/@octokit/auth-token": {
-      "version": "2.5.0",
-      "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz",
-      "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==",
-      "dependencies": {
-        "@octokit/types": "^6.0.3"
-      }
-    },
-    "node_modules/@octokit/core": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz",
-      "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==",
-      "dependencies": {
-        "@octokit/auth-token": "^2.4.4",
-        "@octokit/graphql": "^4.5.8",
-        "@octokit/request": "^5.6.3",
-        "@octokit/request-error": "^2.0.5",
-        "@octokit/types": "^6.0.3",
-        "before-after-hook": "^2.2.0",
-        "universal-user-agent": "^6.0.0"
-      }
-    },
-    "node_modules/@octokit/endpoint": {
-      "version": "6.0.12",
-      "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz",
-      "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==",
-      "dependencies": {
-        "@octokit/types": "^6.0.3",
-        "is-plain-object": "^5.0.0",
-        "universal-user-agent": "^6.0.0"
-      }
-    },
-    "node_modules/@octokit/graphql": {
-      "version": "4.8.0",
-      "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz",
-      "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==",
-      "dependencies": {
-        "@octokit/request": "^5.6.0",
-        "@octokit/types": "^6.0.3",
-        "universal-user-agent": "^6.0.0"
-      }
-    },
-    "node_modules/@octokit/openapi-types": {
-      "version": "12.11.0",
-      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz",
-      "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ=="
-    },
-    "node_modules/@octokit/plugin-paginate-rest": {
-      "version": "2.21.3",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz",
-      "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==",
-      "dependencies": {
-        "@octokit/types": "^6.40.0"
-      },
-      "peerDependencies": {
-        "@octokit/core": ">=2"
-      }
-    },
-    "node_modules/@octokit/plugin-rest-endpoint-methods": {
-      "version": "5.16.2",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz",
-      "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==",
-      "dependencies": {
-        "@octokit/types": "^6.39.0",
-        "deprecation": "^2.3.1"
-      },
-      "peerDependencies": {
-        "@octokit/core": ">=3"
-      }
-    },
-    "node_modules/@octokit/request": {
-      "version": "5.6.3",
-      "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
-      "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==",
-      "dependencies": {
-        "@octokit/endpoint": "^6.0.1",
-        "@octokit/request-error": "^2.1.0",
-        "@octokit/types": "^6.16.1",
-        "is-plain-object": "^5.0.0",
-        "node-fetch": "^2.6.7",
-        "universal-user-agent": "^6.0.0"
-      }
-    },
-    "node_modules/@octokit/request-error": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
-      "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
-      "dependencies": {
-        "@octokit/types": "^6.0.3",
-        "deprecation": "^2.0.0",
-        "once": "^1.4.0"
-      }
-    },
-    "node_modules/@octokit/types": {
-      "version": "6.41.0",
-      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz",
-      "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==",
-      "dependencies": {
-        "@octokit/openapi-types": "^12.11.0"
-      }
-    },
-    "node_modules/balanced-match": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
-    },
-    "node_modules/before-after-hook": {
-      "version": "2.2.3",
-      "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
-      "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
-    },
-    "node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "node_modules/concat-map": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
-    },
-    "node_modules/deprecation": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
-      "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
-    },
-    "node_modules/fs.realpath": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
-    },
-    "node_modules/glob": {
-      "version": "7.2.3",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
-      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
-      "dependencies": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.1.1",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
-      },
-      "engines": {
-        "node": "*"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/inflight": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
-      "dependencies": {
-        "once": "^1.3.0",
-        "wrappy": "1"
-      }
-    },
-    "node_modules/inherits": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
-    },
-    "node_modules/is-plain-object": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
-      "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
-      "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/node-fetch": {
-      "version": "2.6.7",
-      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
-      "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
-      "dependencies": {
-        "whatwg-url": "^5.0.0"
-      },
-      "engines": {
-        "node": "4.x || >=6.0.0"
-      },
-      "peerDependencies": {
-        "encoding": "^0.1.0"
-      },
-      "peerDependenciesMeta": {
-        "encoding": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/once": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
-      "dependencies": {
-        "wrappy": "1"
-      }
-    },
-    "node_modules/path-is-absolute": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/tr46": {
-      "version": "0.0.3",
-      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
-      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
-    },
-    "node_modules/tunnel": {
-      "version": "0.0.6",
-      "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
-      "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
-      "engines": {
-        "node": ">=0.6.11 <=0.7.0 || >=0.7.3"
-      }
-    },
-    "node_modules/universal-user-agent": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
-      "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
-    },
-    "node_modules/uuid": {
-      "version": "8.3.2",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
-      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
-      "bin": {
-        "uuid": "dist/bin/uuid"
-      }
-    },
-    "node_modules/webidl-conversions": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
-      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
-    },
-    "node_modules/whatwg-url": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
-      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
-      "dependencies": {
-        "tr46": "~0.0.3",
-        "webidl-conversions": "^3.0.0"
-      }
-    },
-    "node_modules/wrappy": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
-    }
-  },
-  "dependencies": {
-    "@actions/core": {
-      "version": "1.9.1",
-      "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz",
-      "integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==",
-      "requires": {
-        "@actions/http-client": "^2.0.1",
-        "uuid": "^8.3.2"
-      }
-    },
-    "@actions/github": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.0.tgz",
-      "integrity": "sha512-tuI80F7JQIhg77ZTTgUAPpVD7ZnP9oHSPN8xw7LOwtA4vEMbAjWJNbmLBfV7xua7r016GyjzWLuec5cs8f/a8A==",
-      "requires": {
-        "@actions/http-client": "^2.0.1",
-        "@octokit/core": "^3.6.0",
-        "@octokit/plugin-paginate-rest": "^2.17.0",
-        "@octokit/plugin-rest-endpoint-methods": "^5.13.0"
-      }
-    },
-    "@actions/http-client": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
-      "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
-      "requires": {
-        "tunnel": "^0.0.6"
-      }
-    },
-    "@octokit/auth-token": {
-      "version": "2.5.0",
-      "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz",
-      "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==",
-      "requires": {
-        "@octokit/types": "^6.0.3"
-      }
-    },
-    "@octokit/core": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz",
-      "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==",
-      "requires": {
-        "@octokit/auth-token": "^2.4.4",
-        "@octokit/graphql": "^4.5.8",
-        "@octokit/request": "^5.6.3",
-        "@octokit/request-error": "^2.0.5",
-        "@octokit/types": "^6.0.3",
-        "before-after-hook": "^2.2.0",
-        "universal-user-agent": "^6.0.0"
-      }
-    },
-    "@octokit/endpoint": {
-      "version": "6.0.12",
-      "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz",
-      "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==",
-      "requires": {
-        "@octokit/types": "^6.0.3",
-        "is-plain-object": "^5.0.0",
-        "universal-user-agent": "^6.0.0"
-      }
-    },
-    "@octokit/graphql": {
-      "version": "4.8.0",
-      "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz",
-      "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==",
-      "requires": {
-        "@octokit/request": "^5.6.0",
-        "@octokit/types": "^6.0.3",
-        "universal-user-agent": "^6.0.0"
-      }
-    },
-    "@octokit/openapi-types": {
-      "version": "12.11.0",
-      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz",
-      "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ=="
-    },
-    "@octokit/plugin-paginate-rest": {
-      "version": "2.21.3",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz",
-      "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==",
-      "requires": {
-        "@octokit/types": "^6.40.0"
-      }
-    },
-    "@octokit/plugin-rest-endpoint-methods": {
-      "version": "5.16.2",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz",
-      "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==",
-      "requires": {
-        "@octokit/types": "^6.39.0",
-        "deprecation": "^2.3.1"
-      }
-    },
-    "@octokit/request": {
-      "version": "5.6.3",
-      "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
-      "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==",
-      "requires": {
-        "@octokit/endpoint": "^6.0.1",
-        "@octokit/request-error": "^2.1.0",
-        "@octokit/types": "^6.16.1",
-        "is-plain-object": "^5.0.0",
-        "node-fetch": "^2.6.7",
-        "universal-user-agent": "^6.0.0"
-      }
-    },
-    "@octokit/request-error": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
-      "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
-      "requires": {
-        "@octokit/types": "^6.0.3",
-        "deprecation": "^2.0.0",
-        "once": "^1.4.0"
-      }
-    },
-    "@octokit/types": {
-      "version": "6.41.0",
-      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz",
-      "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==",
-      "requires": {
-        "@octokit/openapi-types": "^12.11.0"
-      }
-    },
-    "balanced-match": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
-    },
-    "before-after-hook": {
-      "version": "2.2.3",
-      "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
-      "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
-    },
-    "brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "requires": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "concat-map": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
-    },
-    "deprecation": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
-      "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
-    },
-    "fs.realpath": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
-    },
-    "glob": {
-      "version": "7.2.3",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
-      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
-      "requires": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.1.1",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
-      }
-    },
-    "inflight": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
-      "requires": {
-        "once": "^1.3.0",
-        "wrappy": "1"
-      }
-    },
-    "inherits": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
-    },
-    "is-plain-object": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
-      "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
-    },
-    "minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
-      "requires": {
-        "brace-expansion": "^1.1.7"
-      }
-    },
-    "node-fetch": {
-      "version": "2.6.7",
-      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
-      "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
-      "requires": {
-        "whatwg-url": "^5.0.0"
-      }
-    },
-    "once": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
-      "requires": {
-        "wrappy": "1"
-      }
-    },
-    "path-is-absolute": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="
-    },
-    "tr46": {
-      "version": "0.0.3",
-      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
-      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
-    },
-    "tunnel": {
-      "version": "0.0.6",
-      "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
-      "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
-    },
-    "universal-user-agent": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
-      "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
-    },
-    "uuid": {
-      "version": "8.3.2",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
-      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
-    },
-    "webidl-conversions": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
-      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
-    },
-    "whatwg-url": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
-      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
-      "requires": {
-        "tr46": "~0.0.3",
-        "webidl-conversions": "^3.0.0"
-      }
-    },
-    "wrappy": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
-    }
-  }
-}
diff --git a/compiler/rustc_codegen_cranelift/.github/actions/github-release/package.json b/compiler/rustc_codegen_cranelift/.github/actions/github-release/package.json
deleted file mode 100644
index d9c23f8873e..00000000000
--- a/compiler/rustc_codegen_cranelift/.github/actions/github-release/package.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "name": "rustc_codegen_cranelift-github-release",
-  "version": "0.0.0",
-  "license": "Apache-2.0 WITH LLVM-exception",
-  "main": "main.js",
-  "dependencies": {
-    "@actions/core": "^1.9.1",
-    "@actions/github": "^5.1.0",
-    "glob": "^7.1.5"
-  }
-}
diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml
index 30dc5cb1615..6ad041a796c 100644
--- a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml
+++ b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml
@@ -25,7 +25,10 @@ jobs:
           - os: ubuntu-latest
             env:
               TARGET_TRIPLE: x86_64-unknown-linux-gnu
-          - os: macos-latest
+          - os: ubuntu-24.04-arm
+            env:
+              TARGET_TRIPLE: aarch64-unknown-linux-gnu
+          - os: macos-13
             env:
               TARGET_TRIPLE: x86_64-apple-darwin
           - os: macos-latest
@@ -56,13 +59,6 @@ jobs:
       if: matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu'
       run: rustup set default-host x86_64-pc-windows-gnu
 
-    - name: Use x86_64 compiler on macOS
-      if: matrix.os == 'macos-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-apple-darwin'
-      run: rustup set default-host x86_64-apple-darwin
-
-    - name: Prepare dependencies
-      run: ./y.sh prepare
-
     - name: Build
       run: ./y.sh build --sysroot none
 
diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml
index 61a4c1270c9..6fd288d195c 100644
--- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml
+++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml
@@ -53,13 +53,12 @@ jobs:
           - os: ubuntu-latest
             env:
               TARGET_TRIPLE: x86_64-unknown-linux-gnu
-          - os: macos-latest
-            env:
-              TARGET_TRIPLE: x86_64-apple-darwin
-          - os: ubuntu-latest
+          - os: ubuntu-24.04-arm
             env:
               TARGET_TRIPLE: aarch64-unknown-linux-gnu
-            apt_deps: gcc-aarch64-linux-gnu qemu-user
+          - os: macos-13
+            env:
+              TARGET_TRIPLE: x86_64-apple-darwin
           - os: macos-latest
             env:
               TARGET_TRIPLE: aarch64-apple-darwin
@@ -95,10 +94,6 @@ jobs:
       if: matrix.os == 'windows-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu'
       run: rustup set default-host x86_64-pc-windows-gnu
 
-    - name: Use x86_64 compiler on macOS
-      if: matrix.os == 'macos-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-apple-darwin'
-      run: rustup set default-host x86_64-apple-darwin
-
     - name: Install toolchain and emulator
       if: matrix.apt_deps != null
       run: |
@@ -170,9 +165,6 @@ jobs:
         sudo apt update
         sudo apt install -y hyperfine
 
-    - name: Prepare dependencies
-      run: ./y.sh prepare
-
     - name: Build
       run: ./y.sh build --sysroot none
 
@@ -192,7 +184,10 @@ jobs:
           - os: ubuntu-22.04
             env:
               TARGET_TRIPLE: x86_64-unknown-linux-gnu
-          - os: macos-latest
+          - os: ubuntu-24.04-arm
+            env:
+              TARGET_TRIPLE: aarch64-unknown-linux-gnu
+          - os: macos-13
             env:
               TARGET_TRIPLE: x86_64-apple-darwin
           - os: macos-latest
@@ -218,13 +213,6 @@ jobs:
       if: matrix.os == 'windows-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu'
       run: rustup set default-host x86_64-pc-windows-gnu
 
-    - name: Use x86_64 compiler on macOS
-      if: matrix.os == 'macos-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-apple-darwin'
-      run: rustup set default-host x86_64-apple-darwin
-
-    - name: Prepare dependencies
-      run: ./y.sh prepare
-
     - name: Build backend
       run: ./y.sh build --sysroot none
 
@@ -273,12 +261,9 @@ jobs:
           rmdir artifacts/ # verify all artifacts are represented in release/
           ls -R release/
 
-      - run: npm install --production
-        working-directory: .github/actions/github-release
-
-      - name: Publish Release
-        uses: ./.github/actions/github-release
-        with:
-          files: "release/*"
-          token: ${{ github.token }}
-        continue-on-error: true
+      - name: Publish release
+        env:
+          GH_TOKEN: ${{ github.token }}
+        run: |
+          gh release delete --cleanup-tag -y dev || true
+          gh release create --target $GITHUB_SHA --prerelease dev release/*
diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml b/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml
index 70c214ce8b1..9253ab96353 100644
--- a/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml
+++ b/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml
@@ -22,9 +22,6 @@ jobs:
         path: build/cg_clif
         key: ${{ runner.os }}-rustc-test-cargo-build-target-${{ hashFiles('rust-toolchain', 'Cargo.lock') }}
 
-    - name: Prepare dependencies
-      run: ./y.sh prepare
-
     - name: Test
       run: ./scripts/test_bootstrap.sh
 
@@ -50,8 +47,5 @@ jobs:
         sudo apt update
         sudo apt install -y ripgrep
 
-    - name: Prepare dependencies
-      run: ./y.sh prepare
-
     - name: Test
       run: ./scripts/test_rustc_tests.sh
diff --git a/compiler/rustc_codegen_cranelift/.vscode/settings.json b/compiler/rustc_codegen_cranelift/.vscode/settings.json
index 491646ce59b..68bd93aea89 100644
--- a/compiler/rustc_codegen_cranelift/.vscode/settings.json
+++ b/compiler/rustc_codegen_cranelift/.vscode/settings.json
@@ -1,41 +1,40 @@
 {
     "editor.formatOnSave": true,
-
     // in case rustc.source is disabled for performance reasons; disable the errors about this
-    "rust-analyzer.diagnostics.disabled": ["unresolved-extern-crate", "unresolved-macro-call"],
+    "rust-analyzer.diagnostics.disabled": [
+        "unresolved-extern-crate",
+        "unresolved-macro-call"
+    ],
     "rust-analyzer.rustc.source": "discover",
     "rust-analyzer.imports.granularity.enforce": true,
     "rust-analyzer.imports.granularity.group": "module",
     "rust-analyzer.imports.prefix": "crate",
-    "rust-analyzer.cargo.features": ["unstable-features"],
+    "rust-analyzer.cargo.features": [
+        "unstable-features"
+    ],
     "rust-analyzer.linkedProjects": [
         "./Cargo.toml",
         "./build_system/Cargo.toml",
         {
+            "sysroot_src": "./build/stdlib/library",
             "crates": [
                 {
                     "root_module": "./example/mini_core.rs",
-                    "edition": "2018",
+                    "edition": "2015",
                     "deps": [],
                     "cfg": [],
                 },
                 {
                     "root_module": "./example/mini_core_hello_world.rs",
-                    "edition": "2018",
-                    "deps": [{ "crate": 0, "name": "mini_core" }],
-                    "cfg": [],
-                },
-                {
-                    "root_module": "./example/mod_bench.rs",
-                    "edition": "2018",
-                    "deps": [],
+                    "edition": "2015",
+                    "deps": [
+                        {
+                            "crate": 0,
+                            "name": "mini_core"
+                        }
+                    ],
                     "cfg": [],
                 },
-            ]
-        },
-        {
-            "sysroot_src": "./build/stdlib/library",
-            "crates": [
                 {
                     "root_module": "./example/std_example.rs",
                     "edition": "2015",
diff --git a/compiler/rustc_codegen_cranelift/.zed/settings.json b/compiler/rustc_codegen_cranelift/.zed/settings.json
index e93bed36949..4338a347331 100644
--- a/compiler/rustc_codegen_cranelift/.zed/settings.json
+++ b/compiler/rustc_codegen_cranelift/.zed/settings.json
@@ -5,7 +5,10 @@
       "initialization_options": {
         "diagnostics": {
           // in case rustc.source is disabled for performance reasons; disable the errors about this
-          "disabled": ["unresolved-extern-crate", "unresolved-macro-call"]
+          "disabled": [
+            "unresolved-extern-crate",
+            "unresolved-macro-call"
+          ]
         },
         "rustc": {
           "source": "discover"
@@ -18,22 +21,25 @@
           "prefix": "crate"
         },
         "cargo": {
-          "features": ["unstable-features"]
+          "features": [
+            "unstable-features"
+          ]
         },
         "linkedProjects": [
           "./Cargo.toml",
           "./build_system/Cargo.toml",
           {
+            "sysroot_src": "./build/stdlib/library",
             "crates": [
               {
                 "root_module": "./example/mini_core.rs",
-                "edition": "2018",
+                "edition": "2015",
                 "deps": [],
                 "cfg": []
               },
               {
                 "root_module": "./example/mini_core_hello_world.rs",
-                "edition": "2018",
+                "edition": "2015",
                 "deps": [
                   {
                     "crate": 0,
@@ -43,17 +49,6 @@
                 "cfg": []
               },
               {
-                "root_module": "./example/mod_bench.rs",
-                "edition": "2018",
-                "deps": [],
-                "cfg": []
-              }
-            ]
-          },
-          {
-            "sysroot_src": "./build/stdlib/library",
-            "crates": [
-              {
                 "root_module": "./example/std_example.rs",
                 "edition": "2015",
                 "deps": [],
diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock
index ca66ec5c6e9..e5f1896b923 100644
--- a/compiler/rustc_codegen_cranelift/Cargo.lock
+++ b/compiler/rustc_codegen_cranelift/Cargo.lock
@@ -42,27 +42,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
+name = "cranelift-assembler-x64"
+version = "0.118.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e4b56ebe316895d3fa37775d0a87b0c889cc933f5c8b253dbcc7c7bcb7fe7e4"
+dependencies = [
+ "cranelift-assembler-x64-meta",
+]
+
+[[package]]
+name = "cranelift-assembler-x64-meta"
+version = "0.118.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95cabbc01dfbd7dcd6c329ca44f0212910309c221797ac736a67a5bc8857fe1b"
+
+[[package]]
 name = "cranelift-bforest"
-version = "0.116.1"
+version = "0.118.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e15d04a0ce86cb36ead88ad68cf693ffd6cda47052b9e0ac114bc47fd9cd23c4"
+checksum = "76ffe46df300a45f1dc6f609dc808ce963f0e3a2e971682c479a2d13e3b9b8ef"
 dependencies = [
  "cranelift-entity",
 ]
 
 [[package]]
 name = "cranelift-bitset"
-version = "0.116.1"
+version = "0.118.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c6e3969a7ce267259ce244b7867c5d3bc9e65b0a87e81039588dfdeaede9f34"
+checksum = "b265bed7c51e1921fdae6419791d31af77d33662ee56d7b0fa0704dc8d231cab"
 
 [[package]]
 name = "cranelift-codegen"
-version = "0.116.1"
+version = "0.118.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c22032c4cb42558371cf516bb47f26cdad1819d3475c133e93c49f50ebf304e"
+checksum = "e606230a7e3a6897d603761baee0d19f88d077f17b996bb5089488a29ae96e41"
 dependencies = [
  "bumpalo",
+ "cranelift-assembler-x64",
  "cranelift-bforest",
  "cranelift-bitset",
  "cranelift-codegen-meta",
@@ -71,7 +87,7 @@ dependencies = [
  "cranelift-entity",
  "cranelift-isle",
  "gimli",
- "hashbrown 0.14.5",
+ "hashbrown",
  "log",
  "regalloc2",
  "rustc-hash",
@@ -82,42 +98,43 @@ dependencies = [
 
 [[package]]
 name = "cranelift-codegen-meta"
-version = "0.116.1"
+version = "0.118.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c904bc71c61b27fc57827f4a1379f29de64fe95653b620a3db77d59655eee0b8"
+checksum = "8a63bffafc23bc60969ad528e138788495999d935f0adcfd6543cb151ca8637d"
 dependencies = [
+ "cranelift-assembler-x64",
  "cranelift-codegen-shared",
 ]
 
 [[package]]
 name = "cranelift-codegen-shared"
-version = "0.116.1"
+version = "0.118.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40180f5497572f644ce88c255480981ae2ec1d7bb4d8e0c0136a13b87a2f2ceb"
+checksum = "af50281b67324b58e843170a6a5943cf6d387c06f7eeacc9f5696e4ab7ae7d7e"
 
 [[package]]
 name = "cranelift-control"
-version = "0.116.1"
+version = "0.118.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26d132c6d0bd8a489563472afc171759da0707804a65ece7ceb15a8c6d7dd5ef"
+checksum = "8c20c1b38d1abfbcebb0032e497e71156c0e3b8dcb3f0a92b9863b7bcaec290c"
 dependencies = [
  "arbitrary",
 ]
 
 [[package]]
 name = "cranelift-entity"
-version = "0.116.1"
+version = "0.118.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b2d0d9618275474fbf679dd018ac6e009acbd6ae6850f6a67be33fb3b00b323"
+checksum = "0c2c67d95507c51b4a1ff3f3555fe4bfec36b9e13c1b684ccc602736f5d5f4a2"
 dependencies = [
  "cranelift-bitset",
 ]
 
 [[package]]
 name = "cranelift-frontend"
-version = "0.116.1"
+version = "0.118.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fac41e16729107393174b0c9e3730fb072866100e1e64e80a1a963b2e484d57"
+checksum = "4e002691cc69c38b54fc7ec93e5be5b744f627d027031d991cc845d1d512d0ce"
 dependencies = [
  "cranelift-codegen",
  "log",
@@ -127,15 +144,15 @@ dependencies = [
 
 [[package]]
 name = "cranelift-isle"
-version = "0.116.1"
+version = "0.118.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ca20d576e5070044d0a72a9effc2deacf4d6aa650403189d8ea50126483944d"
+checksum = "e93588ed1796cbcb0e2ad160403509e2c5d330d80dd6e0014ac6774c7ebac496"
 
 [[package]]
 name = "cranelift-jit"
-version = "0.116.1"
+version = "0.118.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e65c42755a719b09662b00c700daaf76cc35d5ace1f5c002ad404b591ff1978"
+checksum = "17f6682f0b193d6b7873cc8e7ed67e8776a8a26f50eeabf88534e9be618b9a03"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -153,9 +170,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-module"
-version = "0.116.1"
+version = "0.118.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d55612bebcf16ff7306c8a6f5bdb6d45662b8aa1ee058ecce8807ad87db719b"
+checksum = "ff19784c6de05116e63e6a34791012bd927b2a4eac56233039c46f1b6a4edac8"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -164,9 +181,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-native"
-version = "0.116.1"
+version = "0.118.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8dee82f3f1f2c4cba9177f1cc5e350fe98764379bcd29340caa7b01f85076c7"
+checksum = "e5b09bdd6407bf5d89661b80cf926ce731c9e8cc184bf49102267a2369a8358e"
 dependencies = [
  "cranelift-codegen",
  "libc",
@@ -175,9 +192,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-object"
-version = "0.116.1"
+version = "0.118.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aad5a6d3e379493c3f8b35dc61c93d0bf5f27003bbe20614e0200b0ec372ef52"
+checksum = "685e8661a30d1cb69509f589ac643adeee79c5f63c0da316431b9fad29e6d3b4"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -228,12 +245,6 @@ dependencies = [
 
 [[package]]
 name = "hashbrown"
-version = "0.14.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
-
-[[package]]
-name = "hashbrown"
 version = "0.15.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
@@ -248,7 +259,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
 dependencies = [
  "equivalent",
- "hashbrown 0.15.2",
+ "hashbrown",
 ]
 
 [[package]]
@@ -295,7 +306,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
 dependencies = [
  "crc32fast",
- "hashbrown 0.15.2",
+ "hashbrown",
  "indexmap",
  "memchr",
 ]
@@ -326,7 +337,7 @@ checksum = "145c1c267e14f20fb0f88aa76a1c5ffec42d592c1d28b3cd9148ae35916158d3"
 dependencies = [
  "allocator-api2",
  "bumpalo",
- "hashbrown 0.15.2",
+ "hashbrown",
  "log",
  "rustc-hash",
  "smallvec",
@@ -425,9 +436,9 @@ checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
 
 [[package]]
 name = "wasmtime-jit-icache-coherence"
-version = "29.0.1"
+version = "31.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec5e8552e01692e6c2e5293171704fed8abdec79d1a6995a0870ab190e5747d1"
+checksum = "a54f6c6c7e9d7eeee32dfcc10db7f29d505ee7dd28d00593ea241d5f70698e64"
 dependencies = [
  "anyhow",
  "cfg-if",
diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml
index 670d6f4eef5..08b60de14c1 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.116.0", default-features = false, features = ["std", "unwind", "all-native-arch"] }
-cranelift-frontend = { version = "0.116.0" }
-cranelift-module = { version = "0.116.0" }
-cranelift-native = { version = "0.116.0" }
-cranelift-jit = { version = "0.116.0", optional = true }
-cranelift-object = { version = "0.116.0" }
+cranelift-codegen = { version = "0.118.0", default-features = false, features = ["std", "timing", "unwind", "all-native-arch"] }
+cranelift-frontend = { version = "0.118.0" }
+cranelift-module = { version = "0.118.0" }
+cranelift-native = { version = "0.118.0" }
+cranelift-jit = { version = "0.118.0", optional = true }
+cranelift-object = { version = "0.118.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-29.0.0", version = "0.116.0" }
-#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" }
-#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" }
-#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" }
-#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" }
-#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" }
+#cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" }
+#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" }
+#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" }
+#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" }
+#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" }
+#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" }
 
 # Uncomment to use local checkout of cranelift
 #cranelift-codegen = { path = "../wasmtime/cranelift/codegen" }
diff --git a/compiler/rustc_codegen_cranelift/Readme.md b/compiler/rustc_codegen_cranelift/Readme.md
index 18a840f8a50..28edb5795ce 100644
--- a/compiler/rustc_codegen_cranelift/Readme.md
+++ b/compiler/rustc_codegen_cranelift/Readme.md
@@ -49,13 +49,13 @@ If you want to build the backend manually, you can download it from GitHub and b
 ```bash
 $ git clone https://github.com/rust-lang/rustc_codegen_cranelift
 $ cd rustc_codegen_cranelift
-$ ./y.sh prepare
 $ ./y.sh build
 ```
 
 To run the test suite replace the last command with:
 
 ```bash
+$ ./y.sh prepare # only needs to be run the first time
 $ ./test.sh
 ```
 
diff --git a/compiler/rustc_codegen_cranelift/build_system/prepare.rs b/compiler/rustc_codegen_cranelift/build_system/prepare.rs
index 11f73bdb61f..ba5cc9a29f5 100644
--- a/compiler/rustc_codegen_cranelift/build_system/prepare.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/prepare.rs
@@ -91,6 +91,13 @@ impl GitRepo {
 
     fn verify_checksum(&self, dirs: &Dirs) {
         let download_dir = self.download_dir(dirs);
+        if !download_dir.exists() {
+            eprintln!(
+                "Missing directory {download_dir}: Please run ./y.sh prepare to download.",
+                download_dir = download_dir.display(),
+            );
+            std::process::exit(1);
+        }
         let actual_hash = format!("{:016x}", hash_dir(&download_dir));
         if actual_hash != self.content_hash {
             eprintln!(
diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs
index ea7e94c345a..eec89c026b2 100644
--- a/compiler/rustc_codegen_cranelift/build_system/tests.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs
@@ -1,5 +1,4 @@
 use std::ffi::OsStr;
-use std::fs;
 use std::path::PathBuf;
 use std::process::Command;
 
@@ -100,6 +99,34 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[
         runner.run_out_command("gen_block_iterate", &[]);
     }),
     TestCase::build_bin_and_run("aot.raw-dylib", "example/raw-dylib.rs", &[]),
+    TestCase::custom("test.sysroot", &|runner| {
+        apply_patches(
+            &runner.dirs,
+            "sysroot_tests",
+            &runner.stdlib_source.join("library"),
+            &SYSROOT_TESTS_SRC.to_path(&runner.dirs),
+        );
+
+        SYSROOT_TESTS.clean(&runner.dirs);
+
+        let mut target_compiler = runner.target_compiler.clone();
+        // coretests and alloctests produce a bunch of warnings. When running
+        // in rust's CI warnings are denied, so we have to override that here.
+        target_compiler.rustflags.push("--cap-lints=allow".to_owned());
+        // The standard library may have been compiled with -Zrandomize-layout.
+        target_compiler.rustflags.extend(["--cfg".to_owned(), "randomized_layouts".to_owned()]);
+
+        if runner.is_native {
+            let mut test_cmd = SYSROOT_TESTS.test(&target_compiler, &runner.dirs);
+            test_cmd.args(["-p", "coretests", "-p", "alloctests", "--tests", "--", "-q"]);
+            spawn_and_wait(test_cmd);
+        } else {
+            eprintln!("Cross-Compiling: Not running tests");
+            let mut build_cmd = SYSROOT_TESTS.build(&target_compiler, &runner.dirs);
+            build_cmd.args(["-p", "coretests", "-p", "alloctests", "--tests"]);
+            spawn_and_wait(build_cmd);
+        }
+    }),
 ];
 
 pub(crate) static RAND_REPO: GitRepo = GitRepo::github(
@@ -126,9 +153,9 @@ static PORTABLE_SIMD_SRC: RelPath = RelPath::build("portable-simd");
 
 static PORTABLE_SIMD: CargoProject = CargoProject::new(&PORTABLE_SIMD_SRC, "portable-simd_target");
 
-static LIBCORE_TESTS_SRC: RelPath = RelPath::build("coretests");
+static SYSROOT_TESTS_SRC: RelPath = RelPath::build("sysroot_tests");
 
-static LIBCORE_TESTS: CargoProject = CargoProject::new(&LIBCORE_TESTS_SRC, "coretests_target");
+static SYSROOT_TESTS: CargoProject = CargoProject::new(&SYSROOT_TESTS_SRC, "sysroot_tests_target");
 
 const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[
     TestCase::custom("test.rust-random/rand", &|runner| {
@@ -147,31 +174,6 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[
             spawn_and_wait(build_cmd);
         }
     }),
-    TestCase::custom("test.libcore", &|runner| {
-        apply_patches(
-            &runner.dirs,
-            "coretests",
-            &runner.stdlib_source.join("library/coretests"),
-            &LIBCORE_TESTS_SRC.to_path(&runner.dirs),
-        );
-
-        let source_lockfile = runner.dirs.source_dir.join("patches/coretests-lock.toml");
-        let target_lockfile = LIBCORE_TESTS_SRC.to_path(&runner.dirs).join("Cargo.lock");
-        fs::copy(source_lockfile, target_lockfile).unwrap();
-
-        LIBCORE_TESTS.clean(&runner.dirs);
-
-        if runner.is_native {
-            let mut test_cmd = LIBCORE_TESTS.test(&runner.target_compiler, &runner.dirs);
-            test_cmd.arg("--").arg("-q");
-            spawn_and_wait(test_cmd);
-        } else {
-            eprintln!("Cross-Compiling: Not running tests");
-            let mut build_cmd = LIBCORE_TESTS.build(&runner.target_compiler, &runner.dirs);
-            build_cmd.arg("--tests");
-            spawn_and_wait(build_cmd);
-        }
-    }),
     TestCase::custom("test.regex", &|runner| {
         REGEX_REPO.patch(&runner.dirs);
 
@@ -330,10 +332,8 @@ impl<'a> TestRunner<'a> {
         target_compiler.rustflags.extend(rustflags_from_env("RUSTFLAGS"));
         target_compiler.rustdocflags.extend(rustflags_from_env("RUSTDOCFLAGS"));
 
-        let jit_supported = use_unstable_features
-            && is_native
-            && target_compiler.triple.contains("x86_64")
-            && !target_compiler.triple.contains("windows");
+        let jit_supported =
+            use_unstable_features && is_native && !target_compiler.triple.contains("windows");
 
         Self { is_native, jit_supported, skip_tests, dirs, target_compiler, stdlib_source }
     }
@@ -374,21 +374,7 @@ impl<'a> TestRunner<'a> {
                 TestCaseCmd::JitBin { source, args } => {
                     let mut jit_cmd = self.rustc_command([
                         "-Zunstable-options",
-                        "-Cllvm-args=mode=jit",
-                        "-Cprefer-dynamic",
-                        source,
-                        "--cfg",
-                        "jit",
-                    ]);
-                    if !args.is_empty() {
-                        jit_cmd.env("CG_CLIF_JIT_ARGS", args);
-                    }
-                    spawn_and_wait(jit_cmd);
-
-                    eprintln!("[JIT-lazy] {testname}");
-                    let mut jit_cmd = self.rustc_command([
-                        "-Zunstable-options",
-                        "-Cllvm-args=mode=jit-lazy",
+                        "-Cllvm-args=jit-mode",
                         "-Cprefer-dynamic",
                         source,
                         "--cfg",
diff --git a/compiler/rustc_codegen_cranelift/build_system/utils.rs b/compiler/rustc_codegen_cranelift/build_system/utils.rs
index c2114caf869..f2399768459 100644
--- a/compiler/rustc_codegen_cranelift/build_system/utils.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/utils.rs
@@ -105,7 +105,11 @@ impl CargoProject {
             .arg(self.manifest_path(dirs))
             .arg("--target-dir")
             .arg(self.target_dir(dirs))
-            .arg("--locked");
+            .arg("--locked")
+            // bootstrap sets both RUSTC and RUSTC_WRAPPER to the same wrapper. RUSTC is already
+            // respected by the rustc-clif wrapper, but RUSTC_WRAPPER will misinterpret rustc-clif
+            // as filename, so we need to unset it.
+            .env_remove("RUSTC_WRAPPER");
 
         if dirs.frozen {
             cmd.arg("--frozen");
diff --git a/compiler/rustc_codegen_cranelift/config.txt b/compiler/rustc_codegen_cranelift/config.txt
index f578cbef35e..6ae4767adfd 100644
--- a/compiler/rustc_codegen_cranelift/config.txt
+++ b/compiler/rustc_codegen_cranelift/config.txt
@@ -32,9 +32,9 @@ aot.issue-59326
 aot.neon
 aot.gen_block_iterate
 aot.raw-dylib
+test.sysroot
 
 testsuite.extended_sysroot
 test.rust-random/rand
-test.libcore
 test.regex
 test.portable-simd
diff --git a/compiler/rustc_codegen_cranelift/docs/usage.md b/compiler/rustc_codegen_cranelift/docs/usage.md
index 135a51ce392..dbe36109f83 100644
--- a/compiler/rustc_codegen_cranelift/docs/usage.md
+++ b/compiler/rustc_codegen_cranelift/docs/usage.md
@@ -38,14 +38,7 @@ $ $cg_clif_dir/dist/cargo-clif jit
 or
 
 ```bash
-$ $cg_clif_dir/dist/rustc-clif -Zunstable-features -Cllvm-args=mode=jit -Cprefer-dynamic my_crate.rs
-```
-
-There is also an experimental lazy jit mode. In this mode functions are only compiled once they are
-first called.
-
-```bash
-$ $cg_clif_dir/dist/cargo-clif lazy-jit
+$ $cg_clif_dir/dist/rustc-clif -Cllvm-args=jit-mode -Cprefer-dynamic my_crate.rs
 ```
 
 ## Shell
@@ -54,7 +47,7 @@ These are a few functions that allow you to easily run rust code from the shell
 
 ```bash
 function jit_naked() {
-    echo "$@" | $cg_clif_dir/dist/rustc-clif - -Zunstable-options -Cllvm-args=mode=jit-lazy -Cprefer-dynamic
+    echo "$@" | $cg_clif_dir/dist/rustc-clif - -Zunstable-options -Cllvm-args=jit-mode-Cprefer-dynamic
 }
 
 function jit() {
diff --git a/compiler/rustc_codegen_cranelift/example/gen_block_iterate.rs b/compiler/rustc_codegen_cranelift/example/gen_block_iterate.rs
index 25bfe542d22..de9a3d550ec 100644
--- a/compiler/rustc_codegen_cranelift/example/gen_block_iterate.rs
+++ b/compiler/rustc_codegen_cranelift/example/gen_block_iterate.rs
@@ -6,16 +6,25 @@
 #![feature(gen_blocks)]
 
 fn foo() -> impl Iterator<Item = u32> {
-    gen { yield 42; for x in 3..6 { yield x } }
+    gen {
+        yield 42;
+        for x in 3..6 {
+            yield x
+        }
+    }
 }
 
 fn moved() -> impl Iterator<Item = u32> {
     let mut x = "foo".to_string();
     gen move {
         yield 42;
-        if x == "foo" { return }
+        if x == "foo" {
+            return;
+        }
         x.clear();
-        for x in 3..6 { yield x }
+        for x in 3..6 {
+            yield x
+        }
     }
 }
 
@@ -32,5 +41,4 @@ fn main() {
     let mut iter = moved();
     assert_eq!(iter.next(), Some(42));
     assert_eq!(iter.next(), None);
-
 }
diff --git a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch
index 4a06dc3f7ef..16c8488acdb 100644
--- a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch
+++ b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch
@@ -10,20 +10,20 @@ Cranelift doesn't support them yet
  library/core/tests/atomic.rs          |  4 ---
  4 files changed, 4 insertions(+), 50 deletions(-)
 
-diff --git a/tests/lib.rs b/tests/lib.rs
+diff --git a/coretests/tests/lib.rs b/coretests/tests/lib.rs
 index 1e336bf..35e6f54 100644
---- a/tests/lib.rs
-+++ b/tests/lib.rs
+--- a/coretests/tests/lib.rs
++++ b/coretests/tests/lib.rs
 @@ -2,5 +2,4 @@
  // tidy-alphabetical-start
 -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))]
  #![cfg_attr(test, feature(cfg_match))]
  #![feature(alloc_layout_extra)]
  #![feature(array_chunks)]
-diff --git a/tests/atomic.rs b/tests/atomic.rs
+diff --git a/coretests/tests/atomic.rs b/coretests/tests/atomic.rs
 index b735957..ea728b6 100644
---- a/tests/atomic.rs
-+++ b/tests/atomic.rs
+--- a/coretests/tests/atomic.rs
++++ b/coretests/tests/atomic.rs
 @@ -185,10 +185,6 @@ fn atomic_alignment() {
      assert_eq!(align_of::<AtomicU64>(), size_of::<AtomicU64>());
      #[cfg(target_has_atomic = "64")]
diff --git a/compiler/rustc_codegen_cranelift/patches/0028-coretests-Disable-long-running-tests.patch b/compiler/rustc_codegen_cranelift/patches/0028-coretests-Disable-long-running-tests.patch
deleted file mode 100644
index f5ae66c0eb1..00000000000
--- a/compiler/rustc_codegen_cranelift/patches/0028-coretests-Disable-long-running-tests.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From eb703e627e7a84f1cd8d0d87f0f69da1f0acf765 Mon Sep 17 00:00:00 2001
-From: bjorn3 <bjorn3@users.noreply.github.com>
-Date: Fri, 3 Dec 2021 12:16:30 +0100
-Subject: [PATCH] Disable long running tests
-
----
- library/core/tests/slice.rs | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/tests/slice.rs b/tests/slice.rs
-index 8402833..84592e0 100644
---- a/tests/slice.rs
-+++ b/tests/slice.rs
-@@ -1809,6 +1809,7 @@ fn sort_unstable() {
-     }
- }
- 
-+/*
- #[test]
- #[cfg(not(target_arch = "wasm32"))]
- #[cfg_attr(miri, ignore)] // Miri is too slow
-@@ -1914,6 +1915,7 @@ fn select_nth_unstable() {
-     v.select_nth_unstable(0);
-     assert!(v == [0xDEADBEEF]);
- }
-+*/
- 
- #[test]
- #[should_panic(expected = "index 0 greater than length of slice")]
-@@ -2462,6 +2462,7 @@ take_tests! {
- #[cfg(not(miri))] // unused in Miri
- const EMPTY_MAX: &'static [()] = &[(); usize::MAX];
- 
-+/*
- // can't be a constant due to const mutability rules
- #[cfg(not(miri))] // unused in Miri
- macro_rules! empty_max_mut {
-@@ -2485,6 +2486,7 @@ take_tests! {
-     (split_off_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()),
-     (split_off_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()),
- }
-+*/
- 
- #[test]
- fn test_slice_from_ptr_range() {
--- 
-2.26.2.7.g19db9cfb68
-
diff --git a/compiler/rustc_codegen_cranelift/patches/0028-sysroot_tests-Disable-long-running-tests.patch b/compiler/rustc_codegen_cranelift/patches/0028-sysroot_tests-Disable-long-running-tests.patch
new file mode 100644
index 00000000000..357b8d306cf
--- /dev/null
+++ b/compiler/rustc_codegen_cranelift/patches/0028-sysroot_tests-Disable-long-running-tests.patch
@@ -0,0 +1,105 @@
+From eb703e627e7a84f1cd8d0d87f0f69da1f0acf765 Mon Sep 17 00:00:00 2001
+From: bjorn3 <bjorn3@users.noreply.github.com>
+Date: Fri, 3 Dec 2021 12:16:30 +0100
+Subject: [PATCH] Disable long running tests
+
+---
+ library/coretests/tests/slice.rs | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/coretests/tests/slice.rs b/coretests/tests/slice.rs
+index 8402833..84592e0 100644
+--- a/coretests/tests/slice.rs
++++ b/coretests/tests/slice.rs
+@@ -1809,6 +1809,7 @@ fn sort_unstable() {
+     }
+ }
+ 
++/*
+ #[test]
+ #[cfg(not(target_arch = "wasm32"))]
+ #[cfg_attr(miri, ignore)] // Miri is too slow
+@@ -1914,6 +1915,7 @@ fn select_nth_unstable() {
+     v.select_nth_unstable(0);
+     assert!(v == [0xDEADBEEF]);
+ }
++*/
+ 
+ #[test]
+ #[should_panic(expected = "index 0 greater than length of slice")]
+@@ -2462,6 +2462,7 @@ take_tests! {
+ #[cfg(not(miri))] // unused in Miri
+ const EMPTY_MAX: &'static [()] = &[(); usize::MAX];
+ 
++/*
+ // can't be a constant due to const mutability rules
+ #[cfg(not(miri))] // unused in Miri
+ macro_rules! empty_max_mut {
+@@ -2485,6 +2486,7 @@ take_tests! {
+     (split_off_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()),
+     (split_off_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()),
+ }
++*/
+ 
+ #[test]
+ fn test_slice_from_ptr_range() {
+diff --git a/alloctests/tests/sort/tests.rs b/alloctests/tests/sort/tests.rs
+index d321f8d..8b2040a 100644
+--- a/alloctests/tests/sort/tests.rs
++++ b/alloctests/tests/sort/tests.rs
+@@ -1,3 +1,5 @@
++#![cfg(any())]
++
+ use std::cell::Cell;
+ use std::cmp::Ordering;
+ use std::fmt::Debug;
+diff --git a/alloctests/tests/str.rs b/alloctests/tests/str.rs
+index 906fa2d..b82fa99 100644
+--- a/alloctests/tests/str.rs
++++ b/alloctests/tests/str.rs
+@@ -2234,7 +2234,7 @@ fn const_str_ptr() {
+     const C: *const u8 = B as *const u8;
+ 
+     // Miri does not deduplicate consts (https://github.com/rust-lang/miri/issues/131)
+-    #[cfg(not(miri))]
++    #[cfg(any())]
+     {
+         let foo = &A as *const u8;
+         assert_eq!(foo, C);
+diff --git a/alloctests/tests/task.rs b/alloctests/tests/task.rs
+index 390dec1..87df6e6 100644
+--- a/alloctests/tests/task.rs
++++ b/alloctests/tests/task.rs
+@@ -4,7 +4,7 @@ use alloc::task::{LocalWake, Wake};
+ use core::task::{LocalWaker, Waker};
+ 
+ #[test]
+-#[cfg_attr(miri, ignore)] // `will_wake` doesn't guarantee that this test will work, and indeed on Miri it can fail
++#[ignore] // `will_wake` doesn't guarantee that this test will work, and indeed on Miri it can fail
+ fn test_waker_will_wake_clone() {
+     struct NoopWaker;
+ 
+@@ -20,7 +20,7 @@ fn test_waker_will_wake_clone() {
+ }
+ 
+ #[test]
+-#[cfg_attr(miri, ignore)] // `will_wake` doesn't guarantee that this test will work, and indeed on Miri it can fail
++#[ignore] // `will_wake` doesn't guarantee that this test will work, and indeed on Miri it can fail
+ fn test_local_waker_will_wake_clone() {
+     struct NoopWaker;
+ 
+diff --git a/alloctests/tests/vec.rs b/alloctests/tests/vec.rs
+index f430d97..cfbd3cb 100644
+--- a/alloctests/tests/vec.rs
++++ b/alloctests/tests/vec.rs
+@@ -762,6 +762,7 @@ fn test_drain_inclusive_range() {
+ }
+ 
+ #[test]
++#[ignore]
+ fn test_drain_max_vec_size() {
+     let mut v = Vec::<()>::with_capacity(usize::MAX);
+     unsafe {
+-- 
+2.26.2.7.g19db9cfb68
+
diff --git a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch
deleted file mode 100644
index 34249ea4834..00000000000
--- a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From 175d52c5e1779764b66777db1e6f172c2dc365ff Mon Sep 17 00:00:00 2001
-From: bjorn3 <17426603+bjorn3@users.noreply.github.com>
-Date: Fri, 9 Aug 2024 15:44:51 +0000
-Subject: [PATCH] Disable f16 and f128 in compiler-builtins
-
----
- library/liballoc/Cargo.toml | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/library/liballoc/Cargo.toml b/library/liballoc/Cargo.toml
-index 7165c3e48af..968552ad435 100644
---- a/library/alloc/Cargo.toml
-+++ b/library/alloc/Cargo.toml
-@@ -11,7 +11,7 @@ test = { path = "../test" }
- bench = false
- 
- [dependencies]
- core = { path = "../core", public = true }
--compiler_builtins = { version = "=0.1.152", features = ['rustc-dep-of-std'] }
-+compiler_builtins = { version = "=0.1.152", features = ['rustc-dep-of-std', 'no-f16-f128'] }
- 
- [features]
- compiler-builtins-mem = ['compiler_builtins/mem']
--- 
-2.34.1
-
diff --git a/compiler/rustc_codegen_cranelift/patches/coretests-lock.toml b/compiler/rustc_codegen_cranelift/patches/coretests-lock.toml
deleted file mode 100644
index af8f28a193b..00000000000
--- a/compiler/rustc_codegen_cranelift/patches/coretests-lock.toml
+++ /dev/null
@@ -1,35 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "coretests"
-version = "0.0.0"
-dependencies = [
- "rand",
- "rand_xorshift",
-]
-
-[[package]]
-name = "rand"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
-dependencies = [
- "rand_core",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
-
-[[package]]
-name = "rand_xorshift"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
-dependencies = [
- "rand_core",
-]
diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain
index 481903c6afb..ceff15b1180 100644
--- a/compiler/rustc_codegen_cranelift/rust-toolchain
+++ b/compiler/rustc_codegen_cranelift/rust-toolchain
@@ -1,4 +1,4 @@
 [toolchain]
-channel = "nightly-2025-02-15"
+channel = "nightly-2025-03-30"
 components = ["rust-src", "rustc-dev", "llvm-tools"]
 profile = "minimal"
diff --git a/compiler/rustc_codegen_cranelift/rustfmt.toml b/compiler/rustc_codegen_cranelift/rustfmt.toml
index f31fa9c76ab..35c92663eb9 100644
--- a/compiler/rustc_codegen_cranelift/rustfmt.toml
+++ b/compiler/rustc_codegen_cranelift/rustfmt.toml
@@ -1,7 +1,3 @@
-ignore = [
-    "example/gen_block_iterate.rs", # uses edition 2024
-]
-
 # Matches rustfmt.toml of rustc
 style_edition = "2024"
 use_small_heuristics = "Max"
diff --git a/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs b/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs
index ebbb6879610..e6c63bf5e65 100644
--- a/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs
+++ b/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs
@@ -50,19 +50,7 @@ fn main() {
                 .chain([
                     "--".to_string(),
                     "-Zunstable-options".to_string(),
-                    "-Cllvm-args=mode=jit".to_string(),
-                ])
-                .collect()
-        }
-        Some("lazy-jit") => {
-            rustflags.push("-Cprefer-dynamic".to_owned());
-            args.remove(0);
-            IntoIterator::into_iter(["rustc".to_string()])
-                .chain(args)
-                .chain([
-                    "--".to_string(),
-                    "-Zunstable-options".to_string(),
-                    "-Cllvm-args=mode=jit-lazy".to_string(),
+                    "-Cllvm-args=jit-mode".to_string(),
                 ])
                 .collect()
         }
diff --git a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs
index 0252d5b3340..4595063c032 100755
--- a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs
+++ b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs
@@ -4,7 +4,7 @@
 pushd $(dirname "$0")/../
 RUSTC="$(pwd)/dist/rustc-clif"
 popd
-PROFILE=$1 OUTPUT=$2 exec $RUSTC -Zunstable-options -Cllvm-args=mode=jit -Cprefer-dynamic $0
+PROFILE=$1 OUTPUT=$2 exec $RUSTC -Zunstable-options -Cllvm-args=jit-mode -Cprefer-dynamic $0
 #*/
 
 //! This program filters away uninteresting samples and trims uninteresting frames for stackcollapse
diff --git a/compiler/rustc_codegen_cranelift/scripts/rustup.sh b/compiler/rustc_codegen_cranelift/scripts/rustup.sh
index 355282911c2..152c243aa6a 100755
--- a/compiler/rustc_codegen_cranelift/scripts/rustup.sh
+++ b/compiler/rustc_codegen_cranelift/scripts/rustup.sh
@@ -64,7 +64,7 @@ case $1 in
         cg_clif=$(pwd)
         pushd ../rust
         git fetch origin master
-        git checkout "$RUST_VERS"
+        git -c advice.detachedHead=false checkout "$RUST_VERS"
         "$cg_clif/git-fixed-subtree.sh" push --prefix=compiler/rustc_codegen_cranelift/ "$cg_clif" sync_from_rust
         popd
         git merge sync_from_rust -m "Sync from rust $RUST_VERS"
diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh
index 54f6baff4fe..ca6426f2ba9 100644
--- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh
+++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh
@@ -43,8 +43,31 @@ verbose-tests = false
 # disabled bootstrap will crash trying to copy llvm tools for the bootstrap
 # compiler.
 llvm-tools = false
+std-features = ["panic-unwind", "compiler-builtins-no-f16-f128"]
 
 EOF
+
+cat <<EOF | git apply -
+diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
+index cf4ef4ee310..fe78560fcaf 100644
+--- a/src/bootstrap/src/core/config/config.rs
++++ b/src/bootstrap/src/core/config/config.rs
+@@ -3138,13 +3138,6 @@ fn parse_download_ci_llvm(
+                     );
+                 }
+
+-                if b && self.is_running_on_ci {
+-                    // On CI, we must always rebuild LLVM if there were any modifications to it
+-                    panic!(
+-                        "\`llvm.download-ci-llvm\` cannot be set to \`true\` on CI. Use \`if-unchanged\` instead."
+-                    );
+-                }
+-
+                 // If download-ci-llvm=true we also want to check that CI llvm is available
+                 b && llvm::is_ci_llvm_available_for_target(self, asserts)
+             }
+EOF
+
 popd
 
 # Allow the testsuite to use llvm tools
diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
index 55230a0b598..95a4302b5e4 100755
--- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
+++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
@@ -27,7 +27,6 @@ done
 git checkout -- tests/ui/issues/auxiliary/issue-3136-a.rs # contains //~ERROR, but shouldn't be removed
 git checkout -- tests/ui/proc-macro/pretty-print-hack/
 git checkout -- tests/ui/entry-point/auxiliary/bad_main_functions.rs
-rm tests/ui/parser/unclosed-delimiter-in-dep.rs # submodule contains //~ERROR
 
 # missing features
 # ================
@@ -57,7 +56,6 @@ rm tests/ui/asm/x86_64/issue-96797.rs # const and sym inline asm operands don't
 rm tests/ui/asm/x86_64/goto.rs # inline asm labels not supported
 rm tests/ui/simd/simd-bitmask-notpow2.rs # non-pow-of-2 simd vector sizes
 rm -r tests/run-make/embed-source-dwarf # embedding sources in debuginfo
-rm tests/ui/simd-abi-checks.rs # vector types >128bits not yet supported
 
 # requires LTO
 rm -r tests/run-make/cdylib
@@ -120,6 +118,7 @@ rm tests/ui/mir/mir_raw_fat_ptr.rs # same
 rm tests/ui/consts/issue-33537.rs # same
 rm tests/ui/consts/const-mut-refs-crate.rs # same
 rm tests/ui/abi/large-byval-align.rs # exceeds implementation limit of Cranelift
+rm tests/ui/abi/simd-abi-checks-avx.rs # attempts to declare function with two different signatures
 
 # doesn't work due to the way the rustc test suite is invoked.
 # should work when using ./x.py test the way it is intended
@@ -136,7 +135,6 @@ rm -r tests/run-make/incr-add-rust-src-component
 # ============
 rm -r tests/run-make/extern-fn-explicit-align # argument alignment not yet supported
 rm -r tests/run-make/panic-abort-eh_frame # .eh_frame emitted with panic=abort
-rm tests/ui/deprecation/deprecated_inline_threshold.rs # missing deprecation warning for -Cinline-threshold
 
 # bugs in the test suite
 # ======================
@@ -150,48 +148,8 @@ rm tests/ui/intrinsics/panic-uninitialized-zeroed.rs # same
 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
-cp $(../dist/rustc-clif --print target-libdir)/libstd-*.so ../dist/lib/
 
-# prevent $(RUSTDOC) from picking up the sysroot built by x.py. It conflicts with the one used by
-# rustdoc-clif
 cat <<EOF | git apply -
-diff --git a/tests/run-make/tools.mk b/tests/run-make/tools.mk
-index ea06b620c4c..b969d0009c6 100644
---- a/tests/run-make/tools.mk
-+++ b/tests/run-make/tools.mk
-@@ -9,7 +9,7 @@ RUSTC_ORIGINAL := \$(RUSTC)
- BARE_RUSTC := \$(HOST_RPATH_ENV) '\$(RUSTC)'
- BARE_RUSTDOC := \$(HOST_RPATH_ENV) '\$(RUSTDOC)'
- RUSTC := \$(BARE_RUSTC) --out-dir \$(TMPDIR) -L \$(TMPDIR) \$(RUSTFLAGS) -Ainternal_features
--RUSTDOC := \$(BARE_RUSTDOC) -L \$(TARGET_RPATH_DIR)
-+RUSTDOC := \$(BARE_RUSTDOC)
- ifdef RUSTC_LINKER
- RUSTC := \$(RUSTC) -Clinker='\$(RUSTC_LINKER)'
- RUSTDOC := \$(RUSTDOC) -Clinker='\$(RUSTC_LINKER)'
-diff --git a/src/tools/run-make-support/src/rustdoc.rs b/src/tools/run-make-support/src/rustdoc.rs
-index 9607ff02f96..b7d97caf9a2 100644
---- a/src/tools/run-make-support/src/external_deps/rustdoc.rs
-+++ b/src/tools/run-make-support/src/external_deps/rustdoc.rs
-@@ -34,7 +34,6 @@ pub fn bare() -> Self {
-     #[track_caller]
-     pub fn new() -> Self {
-         let mut cmd = setup_common();
--        cmd.arg("-L").arg(env_var_os("TARGET_RPATH_DIR"));
-         Self { cmd }
-     }
-
-diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs
-index e7ae773ffa1d3..04bc2d7787da7 100644
---- a/src/tools/compiletest/src/runtest/run_make.rs
-+++ b/src/tools/compiletest/src/runtest/run_make.rs
-@@ -329,7 +329,6 @@ impl TestCx<'_> {
-             .arg(format!("run_make_support={}", &support_lib_path.to_string_lossy()))
-             .arg("--edition=2021")
-             .arg(&self.testpaths.file.join("rmake.rs"))
--            .arg("-Cprefer-dynamic")
-             // Provide necessary library search paths for rustc.
-             .env(dylib_env_var(), &env::join_paths(host_dylib_search_paths).unwrap());
-
 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
@@ -205,7 +163,19 @@ index 30387af428c..f7895b12961 100644
                  regex::escape(run_make_support::build_root().to_str().unwrap()),
                  "/build-root",
              )
-             .run();
+             .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
++++ b/src/tools/compiletest/src/runtest/run_make.rs
+@@ -109,7 +109,6 @@ pub(super) fn run_rmake_test(&self) {
+             // library or compiler features. Here, we force the stage 0 rustc to consider itself as
+             // a stable-channel compiler via \`RUSTC_BOOTSTRAP=-1\` to prevent *any* unstable
+             // library/compiler usages, even if stage 0 rustc is *actually* a nightly rustc.
+-            .env("RUSTC_BOOTSTRAP", "-1")
+             .arg("-o")
+             .arg(&recipe_bin)
+             // Specify library search paths for \`run_make_support\`.
 EOF
 
 echo "[TEST] rustc test suite"
diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
index e8076ce77ab..ddd119e0c61 100644
--- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
@@ -641,7 +641,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
                 .flat_map(|arg_abi| arg_abi.get_abi_param(fx.tcx).into_iter()),
         );
 
-        if fx.tcx.sess.target.is_like_osx && fx.tcx.sess.target.arch == "aarch64" {
+        if fx.tcx.sess.target.is_like_darwin && fx.tcx.sess.target.arch == "aarch64" {
             // Add any padding arguments needed for Apple AArch64.
             // There's no need to pad the argument list unless variadic arguments are actually being
             // passed.
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 125a9201831..adaa754491e 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -729,8 +729,10 @@ fn codegen_stmt<'tcx>(
                     let to_ty = fx.monomorphize(to_ty);
 
                     fn is_wide_ptr<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool {
-                        ty.builtin_deref(true)
-                            .is_some_and(|pointee_ty| has_ptr_meta(fx.tcx, pointee_ty))
+                        ty.builtin_deref(true).is_some_and(|pointee_ty| {
+                            fx.tcx
+                                .type_has_metadata(pointee_ty, ty::TypingEnv::fully_monomorphized())
+                        })
                     }
 
                     if is_wide_ptr(fx, from_ty) {
diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs
index 766278d8718..abe2972ba0c 100644
--- a/compiler/rustc_codegen_cranelift/src/common.rs
+++ b/compiler/rustc_codegen_cranelift/src/common.rs
@@ -71,7 +71,7 @@ fn clif_type_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<types::Typ
         },
         ty::FnPtr(..) => pointer_ty(tcx),
         ty::RawPtr(pointee_ty, _) | ty::Ref(_, pointee_ty, _) => {
-            if has_ptr_meta(tcx, *pointee_ty) {
+            if tcx.type_has_metadata(*pointee_ty, ty::TypingEnv::fully_monomorphized()) {
                 return None;
             } else {
                 pointer_ty(tcx)
@@ -91,7 +91,7 @@ fn clif_pair_type_from_ty<'tcx>(
             (clif_type_from_ty(tcx, types[0])?, clif_type_from_ty(tcx, types[1])?)
         }
         ty::RawPtr(pointee_ty, _) | ty::Ref(_, pointee_ty, _) => {
-            if has_ptr_meta(tcx, *pointee_ty) {
+            if tcx.type_has_metadata(*pointee_ty, ty::TypingEnv::fully_monomorphized()) {
                 (pointer_ty(tcx), pointer_ty(tcx))
             } else {
                 return None;
@@ -101,20 +101,6 @@ fn clif_pair_type_from_ty<'tcx>(
     })
 }
 
-/// Is a pointer to this type a wide ptr?
-pub(crate) fn has_ptr_meta<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
-    if ty.is_sized(tcx, ty::TypingEnv::fully_monomorphized()) {
-        return false;
-    }
-
-    let tail = tcx.struct_tail_for_codegen(ty, ty::TypingEnv::fully_monomorphized());
-    match tail.kind() {
-        ty::Foreign(..) => false,
-        ty::Str | ty::Slice(..) | ty::Dynamic(..) => true,
-        _ => bug!("unexpected unsized tail: {:?}", tail),
-    }
-}
-
 pub(crate) fn codegen_icmp_imm(
     fx: &mut FunctionCx<'_, '_, '_>,
     intcc: IntCC,
diff --git a/compiler/rustc_codegen_cranelift/src/config.rs b/compiler/rustc_codegen_cranelift/src/config.rs
index d784f6e9d9e..d328b33a704 100644
--- a/compiler/rustc_codegen_cranelift/src/config.rs
+++ b/compiler/rustc_codegen_cranelift/src/config.rs
@@ -1,21 +1,10 @@
-/// The mode to use for compilation.
-#[derive(Copy, Clone, Debug)]
-pub enum CodegenMode {
-    /// AOT compile the crate. This is the default.
-    Aot,
-    /// JIT compile and execute the crate.
-    Jit,
-    /// JIT compile and execute the crate, but only compile functions the first time they are used.
-    JitLazy,
-}
-
 /// Configuration of cg_clif as passed in through `-Cllvm-args` and various env vars.
 #[derive(Clone, Debug)]
 pub struct BackendConfig {
     /// Should the crate be AOT compiled or JIT executed.
     ///
-    /// Defaults to AOT compilation. Can be set using `-Cllvm-args=mode=...`.
-    pub codegen_mode: CodegenMode,
+    /// Defaults to AOT compilation. Can be set using `-Cllvm-args=jit-mode`.
+    pub jit_mode: bool,
 
     /// When JIT mode is enable pass these arguments to the program.
     ///
@@ -27,7 +16,7 @@ impl BackendConfig {
     /// Parse the configuration passed in using `-Cllvm-args`.
     pub fn from_opts(opts: &[String]) -> Result<Self, String> {
         let mut config = BackendConfig {
-            codegen_mode: CodegenMode::Aot,
+            jit_mode: false,
             jit_args: match std::env::var("CG_CLIF_JIT_ARGS") {
                 Ok(args) => args.split(' ').map(|arg| arg.to_string()).collect(),
                 Err(std::env::VarError::NotPresent) => vec![],
@@ -43,20 +32,9 @@ impl BackendConfig {
                 // testing cg_clif.
                 continue;
             }
-            if let Some((name, value)) = opt.split_once('=') {
-                match name {
-                    "mode" => {
-                        config.codegen_mode = match value {
-                            "aot" => CodegenMode::Aot,
-                            "jit" => CodegenMode::Jit,
-                            "jit-lazy" => CodegenMode::JitLazy,
-                            _ => return Err(format!("Unknown codegen mode `{}`", value)),
-                        };
-                    }
-                    _ => return Err(format!("Unknown option `{}`", name)),
-                }
-            } else {
-                return Err(format!("Invalid option `{}`", opt));
+            match &**opt {
+                "jit-mode" => config.jit_mode = true,
+                _ => return Err(format!("Unknown option `{}`", opt)),
             }
         }
 
diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs
index bcc70f4567f..c8527c3a57d 100644
--- a/compiler/rustc_codegen_cranelift/src/constant.rs
+++ b/compiler/rustc_codegen_cranelift/src/constant.rs
@@ -391,7 +391,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
         data.set_align(alloc.align.bytes());
 
         if let Some(section_name) = section_name {
-            let (segment_name, section_name) = if tcx.sess.target.is_like_osx {
+            let (segment_name, section_name) = if tcx.sess.target.is_like_darwin {
                 // See https://github.com/llvm/llvm-project/blob/main/llvm/lib/MC/MCSectionMachO.cpp
                 let mut parts = section_name.as_str().split(',');
                 let Some(segment_name) = parts.next() else {
diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs
index bba6567774d..286e02b986b 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs
@@ -58,7 +58,7 @@ impl DebugContext {
             // FIXME this should be configurable
             // macOS doesn't seem to support DWARF > 3
             // 5 version is required for md5 file hash
-            version: if tcx.sess.target.is_like_osx {
+            version: if tcx.sess.target.is_like_darwin {
                 3
             } else {
                 // FIXME change to version 5 once the gdb and lldb shipping with the latest debian
diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs
index 017d7784dc0..25b922c8be4 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs
@@ -6,7 +6,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 
-use crate::{DebugContext, FullyMonomorphizedLayoutCx, has_ptr_meta};
+use crate::{DebugContext, FullyMonomorphizedLayoutCx};
 
 #[derive(Default)]
 pub(crate) struct TypeDebugContext<'tcx> {
@@ -129,7 +129,7 @@ impl DebugContext {
 
         let name = type_names::compute_debuginfo_type_name(tcx, ptr_type, true);
 
-        if !has_ptr_meta(tcx, ptr_type) {
+        if !tcx.type_has_metadata(ptr_type, ty::TypingEnv::fully_monomorphized()) {
             let pointer_type_id =
                 self.dwarf.unit.add(self.dwarf.unit.root(), gimli::DW_TAG_pointer_type);
             let pointer_entry = self.dwarf.unit.get_mut(pointer_type_id);
diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
index fb7864ae612..444dc441286 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
@@ -331,7 +331,7 @@ fn produce_final_output_artifacts(
 }
 
 fn make_module(sess: &Session, name: String) -> UnwindModule<ObjectModule> {
-    let isa = crate::build_isa(sess);
+    let isa = crate::build_isa(sess, false);
 
     let mut builder =
         ObjectBuilder::new(isa, name + ".o", cranelift_module::default_libcall_names()).unwrap();
diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs
index 57c88f4b0f9..41f8bb9161c 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs
@@ -1,75 +1,27 @@
 //! The JIT driver uses [`cranelift_jit`] to JIT execute programs without writing any object
 //! files.
 
-use std::cell::RefCell;
 use std::ffi::CString;
 use std::os::raw::{c_char, c_int};
-use std::sync::{Mutex, OnceLock, mpsc};
 
-use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
 use cranelift_jit::{JITBuilder, JITModule};
 use rustc_codegen_ssa::CrateInfo;
 use rustc_middle::mir::mono::MonoItem;
 use rustc_session::Session;
 use rustc_span::sym;
 
+use crate::CodegenCx;
 use crate::debuginfo::TypeDebugContext;
 use crate::prelude::*;
 use crate::unwind_module::UnwindModule;
-use crate::{CodegenCx, CodegenMode};
 
-struct JitState {
-    jit_module: UnwindModule<JITModule>,
-}
-
-thread_local! {
-    static LAZY_JIT_STATE: RefCell<Option<JitState>> = const { RefCell::new(None) };
-}
-
-/// The Sender owned by the rustc thread
-static GLOBAL_MESSAGE_SENDER: OnceLock<Mutex<mpsc::Sender<UnsafeMessage>>> = OnceLock::new();
-
-/// A message that is sent from the jitted runtime to the rustc thread.
-/// Senders are responsible for upholding `Send` semantics.
-enum UnsafeMessage {
-    /// Request that the specified `Instance` be lazily jitted.
-    ///
-    /// Nothing accessible through `instance_ptr` may be moved or mutated by the sender after
-    /// this message is sent.
-    JitFn {
-        instance_ptr: *const Instance<'static>,
-        trampoline_ptr: *const u8,
-        tx: mpsc::Sender<*const u8>,
-    },
-}
-unsafe impl Send for UnsafeMessage {}
-
-impl UnsafeMessage {
-    /// Send the message.
-    fn send(self) {
-        thread_local! {
-            /// The Sender owned by the local thread
-            static LOCAL_MESSAGE_SENDER: mpsc::Sender<UnsafeMessage> =
-                GLOBAL_MESSAGE_SENDER
-                    .get().unwrap()
-                    .lock().unwrap()
-                    .clone();
-        }
-        LOCAL_MESSAGE_SENDER.with(|sender| {
-            sender.send(self).expect("rustc thread hung up before lazy JIT request was sent")
-        })
-    }
-}
-
-fn create_jit_module(tcx: TyCtxt<'_>, hotswap: bool) -> (UnwindModule<JITModule>, CodegenCx) {
+fn create_jit_module(tcx: TyCtxt<'_>) -> (UnwindModule<JITModule>, CodegenCx) {
     let crate_info = CrateInfo::new(tcx, "dummy_target_cpu".to_string());
 
-    let isa = crate::build_isa(tcx.sess);
+    let isa = crate::build_isa(tcx.sess, true);
     let mut jit_builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
-    jit_builder.hotswap(hotswap);
     crate::compiler_builtins::register_functions_for_jit(&mut jit_builder);
     jit_builder.symbol_lookup_fn(dep_symbol_lookup_fn(tcx.sess, crate_info));
-    jit_builder.symbol("__clif_jit_fn", clif_jit_fn as *const u8);
     let mut jit_module = UnwindModule::new(JITModule::new(jit_builder), false);
 
     let cx = crate::CodegenCx::new(tcx, jit_module.isa(), false, sym::dummy_cgu_name);
@@ -79,7 +31,7 @@ fn create_jit_module(tcx: TyCtxt<'_>, hotswap: bool) -> (UnwindModule<JITModule>
     (jit_module, cx)
 }
 
-pub(crate) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode, jit_args: Vec<String>) -> ! {
+pub(crate) fn run_jit(tcx: TyCtxt<'_>, jit_args: Vec<String>) -> ! {
     if !tcx.sess.opts.output_types.should_codegen() {
         tcx.dcx().fatal("JIT mode doesn't work with `cargo check`");
     }
@@ -88,8 +40,7 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode, jit_args: Vec<
         tcx.dcx().fatal("can't jit non-executable crate");
     }
 
-    let (mut jit_module, mut cx) =
-        create_jit_module(tcx, matches!(codegen_mode, CodegenMode::JitLazy));
+    let (mut jit_module, mut cx) = create_jit_module(tcx);
     let mut cached_context = Context::new();
 
     let cgus = tcx.collect_and_partition_mono_items(()).codegen_units;
@@ -105,21 +56,15 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode, jit_args: Vec<
         super::predefine_mono_items(tcx, &mut jit_module, &mono_items);
         for (mono_item, _) in mono_items {
             match mono_item {
-                MonoItem::Fn(inst) => match codegen_mode {
-                    CodegenMode::Aot => unreachable!(),
-                    CodegenMode::Jit => {
-                        codegen_and_compile_fn(
-                            tcx,
-                            &mut cx,
-                            &mut cached_context,
-                            &mut jit_module,
-                            inst,
-                        );
-                    }
-                    CodegenMode::JitLazy => {
-                        codegen_shim(tcx, &mut cached_context, &mut jit_module, inst)
-                    }
-                },
+                MonoItem::Fn(inst) => {
+                    codegen_and_compile_fn(
+                        tcx,
+                        &mut cx,
+                        &mut cached_context,
+                        &mut jit_module,
+                        inst,
+                    );
+                }
                 MonoItem::Static(def_id) => {
                     crate::constant::codegen_static(tcx, &mut jit_module, def_id);
                 }
@@ -161,41 +106,17 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode, jit_args: Vec<
     let start_func_id = jit_module.declare_function("main", Linkage::Import, &start_sig).unwrap();
     let finalized_start: *const u8 = jit_module.module.get_finalized_function(start_func_id);
 
-    LAZY_JIT_STATE.with(|lazy_jit_state| {
-        let mut lazy_jit_state = lazy_jit_state.borrow_mut();
-        assert!(lazy_jit_state.is_none());
-        *lazy_jit_state = Some(JitState { jit_module });
-    });
-
     let f: extern "C" fn(c_int, *const *const c_char) -> c_int =
         unsafe { ::std::mem::transmute(finalized_start) };
 
-    let (tx, rx) = mpsc::channel();
-    GLOBAL_MESSAGE_SENDER.set(Mutex::new(tx)).unwrap();
-
-    // Spawn the jitted runtime in a new thread so that this rustc thread can handle messages
-    // (eg to lazily JIT further functions as required)
-    std::thread::spawn(move || {
-        let mut argv = args.iter().map(|arg| arg.as_ptr()).collect::<Vec<_>>();
+    let mut argv = args.iter().map(|arg| arg.as_ptr()).collect::<Vec<_>>();
 
-        // Push a null pointer as a terminating argument. This is required by POSIX and
-        // useful as some dynamic linkers use it as a marker to jump over.
-        argv.push(std::ptr::null());
+    // Push a null pointer as a terminating argument. This is required by POSIX and
+    // useful as some dynamic linkers use it as a marker to jump over.
+    argv.push(std::ptr::null());
 
-        let ret = f(args.len() as c_int, argv.as_ptr());
-        std::process::exit(ret);
-    });
-
-    // Handle messages
-    loop {
-        match rx.recv().unwrap() {
-            // lazy JIT compilation request - compile requested instance and return pointer to result
-            UnsafeMessage::JitFn { instance_ptr, trampoline_ptr, tx } => {
-                tx.send(jit_fn(instance_ptr, trampoline_ptr))
-                    .expect("jitted runtime hung up before response to lazy JIT request was sent");
-            }
-        }
-    }
+    let ret = f(args.len() as c_int, argv.as_ptr());
+    std::process::exit(ret);
 }
 
 pub(crate) fn codegen_and_compile_fn<'tcx>(
@@ -227,58 +148,6 @@ pub(crate) fn codegen_and_compile_fn<'tcx>(
     });
 }
 
-extern "C" fn clif_jit_fn(
-    instance_ptr: *const Instance<'static>,
-    trampoline_ptr: *const u8,
-) -> *const u8 {
-    // send the JIT request to the rustc thread, with a channel for the response
-    let (tx, rx) = mpsc::channel();
-    UnsafeMessage::JitFn { instance_ptr, trampoline_ptr, tx }.send();
-
-    // block on JIT compilation result
-    rx.recv().expect("rustc thread hung up before responding to sent lazy JIT request")
-}
-
-fn jit_fn(instance_ptr: *const Instance<'static>, trampoline_ptr: *const u8) -> *const u8 {
-    rustc_middle::ty::tls::with(|tcx| {
-        // lift is used to ensure the correct lifetime for instance.
-        let instance = tcx.lift(unsafe { *instance_ptr }).unwrap();
-
-        LAZY_JIT_STATE.with(|lazy_jit_state| {
-            let mut lazy_jit_state = lazy_jit_state.borrow_mut();
-            let lazy_jit_state = lazy_jit_state.as_mut().unwrap();
-            let jit_module = &mut lazy_jit_state.jit_module;
-
-            let name = tcx.symbol_name(instance).name;
-            let sig = crate::abi::get_function_sig(
-                tcx,
-                jit_module.target_config().default_call_conv,
-                instance,
-            );
-            let func_id = jit_module.declare_function(name, Linkage::Export, &sig).unwrap();
-
-            let current_ptr = jit_module.module.read_got_entry(func_id);
-
-            // If the function's GOT entry has already been updated to point at something other
-            // than the shim trampoline, don't re-jit but just return the new pointer instead.
-            // This does not need synchronization as this code is executed only by a sole rustc
-            // thread.
-            if current_ptr != trampoline_ptr {
-                return current_ptr;
-            }
-
-            jit_module.module.prepare_for_function_redefine(func_id).unwrap();
-
-            let mut cx = crate::CodegenCx::new(tcx, jit_module.isa(), false, sym::dummy_cgu_name);
-            codegen_and_compile_fn(tcx, &mut cx, &mut Context::new(), jit_module, instance);
-
-            assert!(cx.global_asm.is_empty());
-            jit_module.finalize_definitions();
-            jit_module.module.get_finalized_function(func_id)
-        })
-    })
-}
-
 fn dep_symbol_lookup_fn(
     sess: &Session,
     crate_info: CrateInfo,
@@ -326,57 +195,3 @@ fn dep_symbol_lookup_fn(
         None
     })
 }
-
-fn codegen_shim<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    cached_context: &mut Context,
-    module: &mut UnwindModule<JITModule>,
-    inst: Instance<'tcx>,
-) {
-    let pointer_type = module.target_config().pointer_type();
-
-    let name = tcx.symbol_name(inst).name;
-    let sig = crate::abi::get_function_sig(tcx, module.target_config().default_call_conv, inst);
-    let func_id = module.declare_function(name, Linkage::Export, &sig).unwrap();
-
-    let instance_ptr = Box::into_raw(Box::new(inst));
-
-    let jit_fn = module
-        .declare_function(
-            "__clif_jit_fn",
-            Linkage::Import,
-            &Signature {
-                call_conv: module.target_config().default_call_conv,
-                params: vec![AbiParam::new(pointer_type), AbiParam::new(pointer_type)],
-                returns: vec![AbiParam::new(pointer_type)],
-            },
-        )
-        .unwrap();
-
-    let context = cached_context;
-    context.clear();
-    let trampoline = &mut context.func;
-    trampoline.signature = sig.clone();
-
-    let mut builder_ctx = FunctionBuilderContext::new();
-    let mut trampoline_builder = FunctionBuilder::new(trampoline, &mut builder_ctx);
-
-    let trampoline_fn = module.declare_func_in_func(func_id, trampoline_builder.func);
-    let jit_fn = module.declare_func_in_func(jit_fn, trampoline_builder.func);
-    let sig_ref = trampoline_builder.func.import_signature(sig);
-
-    let entry_block = trampoline_builder.create_block();
-    trampoline_builder.append_block_params_for_function_params(entry_block);
-    let fn_args = trampoline_builder.func.dfg.block_params(entry_block).to_vec();
-
-    trampoline_builder.switch_to_block(entry_block);
-    let instance_ptr = trampoline_builder.ins().iconst(pointer_type, instance_ptr as u64 as i64);
-    let trampoline_ptr = trampoline_builder.ins().func_addr(pointer_type, trampoline_fn);
-    let jitted_fn = trampoline_builder.ins().call(jit_fn, &[instance_ptr, trampoline_ptr]);
-    let jitted_fn = trampoline_builder.func.dfg.inst_results(jitted_fn)[0];
-    let call_inst = trampoline_builder.ins().call_indirect(sig_ref, jitted_fn, &fn_args);
-    let ret_vals = trampoline_builder.func.dfg.inst_results(call_inst).to_vec();
-    trampoline_builder.ins().return_(&ret_vals);
-
-    module.define_function(func_id, context).unwrap();
-}
diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs
index 310b226814d..fbc33a64285 100644
--- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs
+++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs
@@ -612,6 +612,15 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
             generated_asm.push_str(".att_syntax\n");
         }
 
+        if self.arch == InlineAsmArch::AArch64 {
+            for feature in &self.tcx.codegen_fn_attrs(self.enclosing_def_id).target_features {
+                if feature.name == sym::neon {
+                    continue;
+                }
+                writeln!(generated_asm, ".arch_extension {}", feature.name).unwrap();
+            }
+        }
+
         // The actual inline asm
         for piece in self.template {
             match piece {
@@ -652,6 +661,20 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
                                         .emit(&mut generated_asm, InlineAsmArch::X86_64, *modifier)
                                         .unwrap(),
                                 },
+                                InlineAsmArch::AArch64 => match reg {
+                                    InlineAsmReg::AArch64(reg) if reg.vreg_index().is_some() => {
+                                        // rustc emits v0 rather than q0
+                                        reg.emit(
+                                            &mut generated_asm,
+                                            InlineAsmArch::AArch64,
+                                            Some(modifier.unwrap_or('q')),
+                                        )
+                                        .unwrap()
+                                    }
+                                    _ => reg
+                                        .emit(&mut generated_asm, InlineAsmArch::AArch64, *modifier)
+                                        .unwrap(),
+                                },
                                 _ => reg.emit(&mut generated_asm, self.arch, *modifier).unwrap(),
                             }
                         }
@@ -665,6 +688,15 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
         }
         generated_asm.push('\n');
 
+        if self.arch == InlineAsmArch::AArch64 {
+            for feature in &self.tcx.codegen_fn_attrs(self.enclosing_def_id).target_features {
+                if feature.name == sym::neon {
+                    continue;
+                }
+                writeln!(generated_asm, ".arch_extension no{}", feature.name).unwrap();
+            }
+        }
+
         if is_x86 && self.options.contains(InlineAsmOptions::ATT_SYNTAX) {
             generated_asm.push_str(".intel_syntax noprefix\n");
         }
@@ -809,7 +841,13 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
             }
             InlineAsmArch::AArch64 => {
                 generated_asm.push_str("    str ");
-                reg.emit(generated_asm, InlineAsmArch::AArch64, None).unwrap();
+                match reg {
+                    InlineAsmReg::AArch64(reg) if reg.vreg_index().is_some() => {
+                        // rustc emits v0 rather than q0
+                        reg.emit(generated_asm, InlineAsmArch::AArch64, Some('q')).unwrap()
+                    }
+                    _ => reg.emit(generated_asm, InlineAsmArch::AArch64, None).unwrap(),
+                }
                 writeln!(generated_asm, ", [x19, 0x{:x}]", offset.bytes()).unwrap();
             }
             InlineAsmArch::RiscV64 => {
@@ -851,7 +889,13 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
             }
             InlineAsmArch::AArch64 => {
                 generated_asm.push_str("    ldr ");
-                reg.emit(generated_asm, InlineAsmArch::AArch64, None).unwrap();
+                match reg {
+                    InlineAsmReg::AArch64(reg) if reg.vreg_index().is_some() => {
+                        // rustc emits v0 rather than q0
+                        reg.emit(generated_asm, InlineAsmArch::AArch64, Some('q')).unwrap()
+                    }
+                    _ => reg.emit(generated_asm, InlineAsmArch::AArch64, None).unwrap(),
+                }
                 writeln!(generated_asm, ", [x19, 0x{:x}]", offset.bytes()).unwrap();
             }
             InlineAsmArch::RiscV64 => {
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs
index 720a0d8fbf5..eb0dfbb69c3 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs
@@ -54,6 +54,14 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>(
             );
         }
 
+        "llvm.fptosi.sat.v4i32.v4f32" => {
+            intrinsic_args!(fx, args => (a); intrinsic);
+
+            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| {
+                fx.bcx.ins().fcvt_to_sint_sat(types::I32, lane)
+            });
+        }
+
         _ => {
             fx.tcx
                 .dcx()
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs
index 4c59c81296b..387c87d123a 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs
@@ -1,5 +1,9 @@
 //! Emulate AArch64 LLVM intrinsics
 
+use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece};
+use rustc_target::asm::*;
+
+use crate::inline_asm::{CInlineAsmOperand, codegen_inline_asm_inner};
 use crate::intrinsics::*;
 use crate::prelude::*;
 
@@ -17,7 +21,7 @@ pub(crate) fn codegen_aarch64_llvm_intrinsic_call<'tcx>(
             fx.bcx.ins().fence();
         }
 
-        "llvm.aarch64.neon.ld1x4.v16i8.p0i8" => {
+        "llvm.aarch64.neon.ld1x4.v16i8.p0" => {
             intrinsic_args!(fx, args => (ptr); intrinsic);
 
             let ptr = ptr.load_scalar(fx);
@@ -49,6 +53,121 @@ pub(crate) fn codegen_aarch64_llvm_intrinsic_call<'tcx>(
             });
         }
 
+        "llvm.aarch64.neon.fcvtns.v4i32.v4f32" => {
+            intrinsic_args!(fx, args => (a); intrinsic);
+
+            // Note: Using inline asm instead of fcvt_to_sint as the latter rounds to zero rather than to nearest
+
+            let a_ptr = a.force_stack(fx).0.get_addr(fx);
+            let res_place = CPlace::new_stack_slot(fx, ret.layout());
+            let res_ptr = res_place.to_ptr().get_addr(fx);
+
+            codegen_inline_asm_inner(
+                fx,
+                &[InlineAsmTemplatePiece::String(
+                    "ldr     q0, [x0]
+                     fcvtns  v0.4s, v0.4s
+                     str     q0, [x1]"
+                        .into(),
+                )],
+                &[
+                    CInlineAsmOperand::In {
+                        reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64(
+                            AArch64InlineAsmReg::x0,
+                        )),
+                        value: a_ptr,
+                    },
+                    CInlineAsmOperand::In {
+                        reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64(
+                            AArch64InlineAsmReg::x1,
+                        )),
+                        value: res_ptr,
+                    },
+                ],
+                InlineAsmOptions::NOSTACK,
+            );
+            let res = res_place.to_cvalue(fx);
+            ret.write_cvalue_transmute(fx, res);
+        }
+
+        "llvm.aarch64.neon.frecpe.v4f32" => {
+            intrinsic_args!(fx, args => (a); intrinsic);
+
+            let a_ptr = a.force_stack(fx).0.get_addr(fx);
+            let res_place = CPlace::new_stack_slot(fx, ret.layout());
+            let res_ptr = res_place.to_ptr().get_addr(fx);
+
+            codegen_inline_asm_inner(
+                fx,
+                &[InlineAsmTemplatePiece::String(
+                    "ldr     q0, [x0]
+                     frecpe  v0.4s, v0.4s
+                     str     q0, [x1]"
+                        .into(),
+                )],
+                &[
+                    CInlineAsmOperand::In {
+                        reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64(
+                            AArch64InlineAsmReg::x0,
+                        )),
+                        value: a_ptr,
+                    },
+                    CInlineAsmOperand::In {
+                        reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64(
+                            AArch64InlineAsmReg::x1,
+                        )),
+                        value: res_ptr,
+                    },
+                ],
+                InlineAsmOptions::NOSTACK,
+            );
+            let res = res_place.to_cvalue(fx);
+            ret.write_cvalue_transmute(fx, res);
+        }
+
+        "llvm.aarch64.neon.frecps.v4f32" => {
+            intrinsic_args!(fx, args => (a, b); intrinsic);
+
+            let a_ptr = a.force_stack(fx).0.get_addr(fx);
+            let b_ptr = b.force_stack(fx).0.get_addr(fx);
+            let res_place = CPlace::new_stack_slot(fx, ret.layout());
+            let res_ptr = res_place.to_ptr().get_addr(fx);
+
+            codegen_inline_asm_inner(
+                fx,
+                &[InlineAsmTemplatePiece::String(
+                    "ldr     q0, [x0]
+                     ldr     q1, [x1]
+                     frecps  v0.4s, v0.4s, v1.4s
+                     str     q0, [x2]"
+                        .into(),
+                )],
+                &[
+                    CInlineAsmOperand::In {
+                        reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64(
+                            AArch64InlineAsmReg::x0,
+                        )),
+                        value: a_ptr,
+                    },
+                    CInlineAsmOperand::In {
+                        reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64(
+                            AArch64InlineAsmReg::x1,
+                        )),
+                        value: b_ptr,
+                    },
+                    CInlineAsmOperand::In {
+                        reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64(
+                            AArch64InlineAsmReg::x2,
+                        )),
+                        value: res_ptr,
+                    },
+                ],
+                InlineAsmOptions::NOSTACK,
+            );
+            let res = res_place.to_cvalue(fx);
+            ret.write_cvalue_transmute(fx, res);
+        }
+
         _ if intrinsic.starts_with("llvm.aarch64.neon.sqadd.v")
             || intrinsic.starts_with("llvm.aarch64.neon.uqadd.v") =>
         {
@@ -134,7 +253,7 @@ pub(crate) fn codegen_aarch64_llvm_intrinsic_call<'tcx>(
             }
             let res = CValue::by_val(
                 fx.bcx.ins().uextend(types::I32, res_val),
-                fx.layout_of(fx.tcx.types.u32),
+                fx.layout_of(fx.tcx.types.i32),
             );
             ret.write_cvalue(fx, res);
         }
diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs
index ab3386a9b4c..e7afaff3b42 100644
--- a/compiler/rustc_codegen_cranelift/src/lib.rs
+++ b/compiler/rustc_codegen_cranelift/src/lib.rs
@@ -42,7 +42,6 @@ use std::sync::Arc;
 use cranelift_codegen::isa::TargetIsa;
 use cranelift_codegen::settings::{self, Configurable};
 use rustc_codegen_ssa::CodegenResults;
-use rustc_codegen_ssa::back::versioned_llvm_target;
 use rustc_codegen_ssa::traits::CodegenBackend;
 use rustc_metadata::EncodedMetadata;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
@@ -214,15 +213,14 @@ impl CodegenBackend for CraneliftCodegenBackend {
             BackendConfig::from_opts(&tcx.sess.opts.cg.llvm_args)
                 .unwrap_or_else(|err| tcx.sess.dcx().fatal(err))
         });
-        match config.codegen_mode {
-            CodegenMode::Aot => driver::aot::run_aot(tcx, metadata, need_metadata_module),
-            CodegenMode::Jit | CodegenMode::JitLazy => {
-                #[cfg(feature = "jit")]
-                driver::jit::run_jit(tcx, config.codegen_mode, config.jit_args);
-
-                #[cfg(not(feature = "jit"))]
-                tcx.dcx().fatal("jit support was disabled when compiling rustc_codegen_cranelift");
-            }
+        if config.jit_mode {
+            #[cfg(feature = "jit")]
+            driver::jit::run_jit(tcx, config.jit_args);
+
+            #[cfg(not(feature = "jit"))]
+            tcx.dcx().fatal("jit support was disabled when compiling rustc_codegen_cranelift");
+        } else {
+            driver::aot::run_aot(tcx, metadata, need_metadata_module)
         }
     }
 
@@ -247,21 +245,19 @@ fn enable_verifier(sess: &Session) -> bool {
 }
 
 fn target_triple(sess: &Session) -> target_lexicon::Triple {
-    // FIXME(madsmtm): Use `sess.target.llvm_target` once target-lexicon supports unversioned macOS.
-    // See <https://github.com/bytecodealliance/target-lexicon/pull/113>
-    match versioned_llvm_target(sess).parse() {
+    match sess.target.llvm_target.parse() {
         Ok(triple) => triple,
         Err(err) => sess.dcx().fatal(format!("target not recognized: {}", err)),
     }
 }
 
-fn build_isa(sess: &Session) -> Arc<dyn TargetIsa + 'static> {
+fn build_isa(sess: &Session, jit: bool) -> Arc<dyn TargetIsa + 'static> {
     use target_lexicon::BinaryFormat;
 
     let target_triple = crate::target_triple(sess);
 
     let mut flags_builder = settings::builder();
-    flags_builder.enable("is_pic").unwrap();
+    flags_builder.set("is_pic", if jit { "false" } else { "true" }).unwrap();
     let enable_verifier = if enable_verifier(sess) { "true" } else { "false" };
     flags_builder.set("enable_verifier", enable_verifier).unwrap();
     flags_builder.set("regalloc_checker", enable_verifier).unwrap();
diff --git a/compiler/rustc_codegen_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs
index f44e2459a78..2a4d1e3ae57 100644
--- a/compiler/rustc_codegen_cranelift/src/num.rs
+++ b/compiler/rustc_codegen_cranelift/src/num.rs
@@ -395,8 +395,12 @@ pub(crate) fn codegen_ptr_binop<'tcx>(
     in_lhs: CValue<'tcx>,
     in_rhs: CValue<'tcx>,
 ) -> CValue<'tcx> {
-    let is_thin_ptr =
-        in_lhs.layout().ty.builtin_deref(true).map(|ty| !has_ptr_meta(fx.tcx, ty)).unwrap_or(true);
+    let is_thin_ptr = in_lhs
+        .layout()
+        .ty
+        .builtin_deref(true)
+        .map(|ty| !fx.tcx.type_has_metadata(ty, ty::TypingEnv::fully_monomorphized()))
+        .unwrap_or(true);
 
     if is_thin_ptr {
         match bin_op {
diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs
index cc739fefcd0..f8a19589fdd 100644
--- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs
+++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs
@@ -746,7 +746,7 @@ impl<'tcx> CPlace<'tcx> {
         };
 
         let (field_ptr, field_layout) = codegen_field(fx, base, extra, layout, field);
-        if has_ptr_meta(fx.tcx, field_layout.ty) {
+        if fx.tcx.type_has_metadata(field_layout.ty, ty::TypingEnv::fully_monomorphized()) {
             CPlace::for_ptr_with_extra(field_ptr, extra.unwrap(), field_layout)
         } else {
             CPlace::for_ptr(field_ptr, field_layout)
@@ -832,7 +832,7 @@ impl<'tcx> CPlace<'tcx> {
 
     pub(crate) fn place_deref(self, fx: &mut FunctionCx<'_, '_, 'tcx>) -> CPlace<'tcx> {
         let inner_layout = fx.layout_of(self.layout().ty.builtin_deref(true).unwrap());
-        if has_ptr_meta(fx.tcx, inner_layout.ty) {
+        if fx.tcx.type_has_metadata(inner_layout.ty, ty::TypingEnv::fully_monomorphized()) {
             let (addr, extra) = self.to_cvalue(fx).load_scalar_pair(fx);
             CPlace::for_ptr_with_extra(Pointer::new(addr), extra, inner_layout)
         } else {
@@ -845,7 +845,7 @@ impl<'tcx> CPlace<'tcx> {
         fx: &mut FunctionCx<'_, '_, 'tcx>,
         layout: TyAndLayout<'tcx>,
     ) -> CValue<'tcx> {
-        if has_ptr_meta(fx.tcx, self.layout().ty) {
+        if fx.tcx.type_has_metadata(self.layout().ty, ty::TypingEnv::fully_monomorphized()) {
             let (ptr, extra) = self.to_ptr_unsized();
             CValue::by_val_pair(ptr.get_addr(fx), extra, layout)
         } else {
diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs
index 474475f311f..acb39374628 100644
--- a/compiler/rustc_codegen_gcc/src/consts.rs
+++ b/compiler/rustc_codegen_gcc/src/consts.rs
@@ -131,7 +131,7 @@ impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> {
             // will use load-unaligned instructions instead, and thus avoiding the crash.
             //
             // We could remove this hack whenever we decide to drop macOS 10.10 support.
-            if self.tcx.sess.target.options.is_like_osx {
+            if self.tcx.sess.target.options.is_like_darwin {
                 // The `inspect` method is okay here because we checked for provenance, and
                 // because we are doing this access to inspect the final interpreter state
                 // (not as part of the interpreter execution).
diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl
index 17f2e7ca9f7..41391b096cc 100644
--- a/compiler/rustc_codegen_llvm/messages.ftl
+++ b/compiler/rustc_codegen_llvm/messages.ftl
@@ -56,6 +56,8 @@ codegen_llvm_prepare_thin_lto_module_with_llvm_err = failed to prepare thin LTO
 codegen_llvm_run_passes = failed to run LLVM passes
 codegen_llvm_run_passes_with_llvm_err = failed to run LLVM passes: {$llvm_err}
 
+codegen_llvm_sanitizer_kcfi_arity_requires_llvm_21_0_0 = `-Zsanitizer-kcfi-arity` requires LLVM 21.0.0 or later.
+
 codegen_llvm_sanitizer_memtag_requires_mte =
     `-Zsanitizer=memtag` requires `-Ctarget-feature=+mte`
 
diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs
index 71059338151..8294e29d07d 100644
--- a/compiler/rustc_codegen_llvm/src/abi.rs
+++ b/compiler/rustc_codegen_llvm/src/abi.rs
@@ -17,14 +17,13 @@ use rustc_target::callconv::{
 use rustc_target::spec::SanitizerSet;
 use smallvec::SmallVec;
 
-use crate::attributes::llfn_attrs_from_instance;
+use crate::attributes::{self, llfn_attrs_from_instance};
 use crate::builder::Builder;
 use crate::context::CodegenCx;
 use crate::llvm::{self, Attribute, AttributePlace};
 use crate::type_::Type;
 use crate::type_of::LayoutLlvmExt;
 use crate::value::Value;
-use crate::{attributes, llvm_util};
 
 trait ArgAttributesExt {
     fn apply_attrs_to_llfn(&self, idx: AttributePlace, cx: &CodegenCx<'_, '_>, llfn: &Value);
@@ -437,7 +436,6 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
 
         let apply_range_attr = |idx: AttributePlace, scalar: rustc_abi::Scalar| {
             if cx.sess().opts.optimize != config::OptLevel::No
-                && llvm_util::get_version() >= (19, 0, 0)
                 && matches!(scalar.primitive(), Primitive::Int(..))
                 // If the value is a boolean, the range is 0..2 and that ultimately
                 // become 0..0 when the type becomes i1, which would be rejected
@@ -571,19 +569,6 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
             }
             _ => {}
         }
-        if bx.cx.sess().opts.optimize != config::OptLevel::No
-                && llvm_util::get_version() < (19, 0, 0)
-                && let BackendRepr::Scalar(scalar) = self.ret.layout.backend_repr
-                && matches!(scalar.primitive(), Primitive::Int(..))
-                // If the value is a boolean, the range is 0..2 and that ultimately
-                // become 0..0 when the type becomes i1, which would be rejected
-                // by the LLVM verifier.
-                && !scalar.is_bool()
-                // LLVM also rejects full range.
-                && !scalar.is_always_valid(bx)
-        {
-            bx.range_metadata(callsite, scalar.valid_range(bx));
-        }
         for arg in self.args.iter() {
             match &arg.mode {
                 PassMode::Ignore => {}
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs
index 3d7afa17bdf..e8c42d16733 100644
--- a/compiler/rustc_codegen_llvm/src/attributes.rs
+++ b/compiler/rustc_codegen_llvm/src/attributes.rs
@@ -407,30 +407,28 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
         // Do not set sanitizer attributes for naked functions.
         to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize));
 
-        if llvm_util::get_version() >= (19, 0, 0) {
-            // For non-naked functions, set branch protection attributes on aarch64.
-            if let Some(BranchProtection { bti, pac_ret }) =
-                cx.sess().opts.unstable_opts.branch_protection
-            {
-                assert!(cx.sess().target.arch == "aarch64");
-                if bti {
-                    to_add.push(llvm::CreateAttrString(cx.llcx, "branch-target-enforcement"));
-                }
-                if let Some(PacRet { leaf, pc, key }) = pac_ret {
-                    if pc {
-                        to_add.push(llvm::CreateAttrString(cx.llcx, "branch-protection-pauth-lr"));
-                    }
-                    to_add.push(llvm::CreateAttrStringValue(
-                        cx.llcx,
-                        "sign-return-address",
-                        if leaf { "all" } else { "non-leaf" },
-                    ));
-                    to_add.push(llvm::CreateAttrStringValue(
-                        cx.llcx,
-                        "sign-return-address-key",
-                        if key == PAuthKey::A { "a_key" } else { "b_key" },
-                    ));
+        // For non-naked functions, set branch protection attributes on aarch64.
+        if let Some(BranchProtection { bti, pac_ret }) =
+            cx.sess().opts.unstable_opts.branch_protection
+        {
+            assert!(cx.sess().target.arch == "aarch64");
+            if bti {
+                to_add.push(llvm::CreateAttrString(cx.llcx, "branch-target-enforcement"));
+            }
+            if let Some(PacRet { leaf, pc, key }) = pac_ret {
+                if pc {
+                    to_add.push(llvm::CreateAttrString(cx.llcx, "branch-protection-pauth-lr"));
                 }
+                to_add.push(llvm::CreateAttrStringValue(
+                    cx.llcx,
+                    "sign-return-address",
+                    if leaf { "all" } else { "non-leaf" },
+                ));
+                to_add.push(llvm::CreateAttrStringValue(
+                    cx.llcx,
+                    "sign-return-address-key",
+                    if key == PAuthKey::A { "a_key" } else { "b_key" },
+                ));
             }
         }
     }
@@ -510,12 +508,6 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
             InstructionSetAttr::ArmA32 => "-thumb-mode".to_string(),
             InstructionSetAttr::ArmT32 => "+thumb-mode".to_string(),
         }))
-        // HACK: LLVM versions 19+ do not have the FPMR feature and treat it as always enabled
-        // It only exists as a feature in LLVM 18, cannot be passed down for any other version
-        .chain(match &*cx.tcx.sess.target.arch {
-            "aarch64" if llvm_util::get_version().0 == 18 => vec!["+fpmr".to_string()],
-            _ => vec![],
-        })
         .collect::<Vec<String>>();
 
     if cx.tcx.sess.target.is_like_wasm {
diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs
index f083cfbd7d3..a8b49e9552c 100644
--- a/compiler/rustc_codegen_llvm/src/back/lto.rs
+++ b/compiler/rustc_codegen_llvm/src/back/lto.rs
@@ -610,6 +610,8 @@ fn enable_autodiff_settings(ad: &[config::AutoDiff], module: &mut ModuleCodegen<
             }
             // We handle this below
             config::AutoDiff::PrintModAfter => {}
+            // We handle this below
+            config::AutoDiff::PrintModFinal => {}
             // This is required and already checked
             config::AutoDiff::Enable => {}
         }
@@ -657,14 +659,20 @@ pub(crate) fn run_pass_manager(
     }
 
     if cfg!(llvm_enzyme) && enable_ad {
+        // This is the post-autodiff IR, mainly used for testing and educational purposes.
+        if config.autodiff.contains(&config::AutoDiff::PrintModAfter) {
+            unsafe { llvm::LLVMDumpModule(module.module_llvm.llmod()) };
+        }
+
         let opt_stage = llvm::OptStage::FatLTO;
         let stage = write::AutodiffStage::PostAD;
         unsafe {
             write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage)?;
         }
 
-        // This is the final IR, so people should be able to inspect the optimized autodiff output.
-        if config.autodiff.contains(&config::AutoDiff::PrintModAfter) {
+        // This is the final IR, so people should be able to inspect the optimized autodiff output,
+        // for manual inspection.
+        if config.autodiff.contains(&config::AutoDiff::PrintModFinal) {
             unsafe { llvm::LLVMDumpModule(module.module_llvm.llmod()) };
         }
     }
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs
index bead4c82a81..bf6138142b6 100644
--- a/compiler/rustc_codegen_llvm/src/back/write.rs
+++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -1024,7 +1024,7 @@ fn create_section_with_flags_asm(section_name: &str, section_flags: &str, data:
 }
 
 pub(crate) fn bitcode_section_name(cgcx: &CodegenContext<LlvmCodegenBackend>) -> &'static CStr {
-    if cgcx.target_is_like_osx {
+    if cgcx.target_is_like_darwin {
         c"__LLVM,__bitcode"
     } else if cgcx.target_is_like_aix {
         c".ipa"
@@ -1077,7 +1077,7 @@ unsafe fn embed_bitcode(
     // and COFF we emit the sections using module level inline assembly for that
     // reason (see issue #90326 for historical background).
     unsafe {
-        if cgcx.target_is_like_osx
+        if cgcx.target_is_like_darwin
             || cgcx.target_is_like_aix
             || cgcx.target_arch == "wasm32"
             || cgcx.target_arch == "wasm64"
@@ -1096,7 +1096,7 @@ unsafe fn embed_bitcode(
             let llglobal =
                 llvm::add_global(llmod, common::val_ty(llconst), c"rustc.embedded.cmdline");
             llvm::set_initializer(llglobal, llconst);
-            let section = if cgcx.target_is_like_osx {
+            let section = if cgcx.target_is_like_darwin {
                 c"__LLVM,__cmdline"
             } else if cgcx.target_is_like_aix {
                 c".info"
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 297f104d124..35134e9f5a0 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -30,6 +30,7 @@ use smallvec::SmallVec;
 use tracing::{debug, instrument};
 
 use crate::abi::FnAbiLlvmExt;
+use crate::attributes;
 use crate::common::Funclet;
 use crate::context::{CodegenCx, FullCx, GenericCx, SCx};
 use crate::llvm::{
@@ -38,7 +39,6 @@ use crate::llvm::{
 use crate::type_::Type;
 use crate::type_of::LayoutLlvmExt;
 use crate::value::Value;
-use crate::{attributes, llvm_util};
 
 #[must_use]
 pub(crate) struct GenericBuilder<'a, 'll, CX: Borrow<SCx<'ll>>> {
@@ -927,11 +927,9 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
         debug_assert_ne!(self.val_ty(val), dest_ty);
 
         let trunc = self.trunc(val, dest_ty);
-        if llvm_util::get_version() >= (19, 0, 0) {
-            unsafe {
-                if llvm::LLVMIsAInstruction(trunc).is_some() {
-                    llvm::LLVMSetNUW(trunc, True);
-                }
+        unsafe {
+            if llvm::LLVMIsAInstruction(trunc).is_some() {
+                llvm::LLVMSetNUW(trunc, True);
             }
         }
         trunc
@@ -941,11 +939,9 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
         debug_assert_ne!(self.val_ty(val), dest_ty);
 
         let trunc = self.trunc(val, dest_ty);
-        if llvm_util::get_version() >= (19, 0, 0) {
-            unsafe {
-                if llvm::LLVMIsAInstruction(trunc).is_some() {
-                    llvm::LLVMSetNSW(trunc, True);
-                }
+        unsafe {
+            if llvm::LLVMIsAInstruction(trunc).is_some() {
+                llvm::LLVMSetNSW(trunc, True);
             }
         }
         trunc
@@ -1899,10 +1895,6 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
         hash: &'ll Value,
         bitmap_bits: &'ll Value,
     ) {
-        assert!(
-            crate::llvm_util::get_version() >= (19, 0, 0),
-            "MCDC intrinsics require LLVM 19 or later"
-        );
         self.call_intrinsic("llvm.instrprof.mcdc.parameters", &[fn_name, hash, bitmap_bits]);
     }
 
@@ -1914,10 +1906,6 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
         bitmap_index: &'ll Value,
         mcdc_temp: &'ll Value,
     ) {
-        assert!(
-            crate::llvm_util::get_version() >= (19, 0, 0),
-            "MCDC intrinsics require LLVM 19 or later"
-        );
         let args = &[fn_name, hash, bitmap_index, mcdc_temp];
         self.call_intrinsic("llvm.instrprof.mcdc.tvbitmap.update", args);
     }
@@ -1929,10 +1917,6 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
 
     #[instrument(level = "debug", skip(self))]
     pub(crate) fn mcdc_condbitmap_update(&mut self, cond_index: &'ll Value, mcdc_temp: &'ll Value) {
-        assert!(
-            crate::llvm_util::get_version() >= (19, 0, 0),
-            "MCDC intrinsics require LLVM 19 or later"
-        );
         let align = self.tcx.data_layout.i32_align.abi;
         let current_tv_index = self.load(self.cx.type_i32(), mcdc_temp, align);
         let new_tv_index = self.add(current_tv_index, cond_index);
diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs
index 7cd4ee539d8..7d264ba4d00 100644
--- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs
+++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs
@@ -3,8 +3,10 @@ use std::ptr;
 use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, AutoDiffItem, DiffActivity, DiffMode};
 use rustc_codegen_ssa::ModuleCodegen;
 use rustc_codegen_ssa::back::write::ModuleConfig;
-use rustc_codegen_ssa::traits::BaseTypeCodegenMethods as _;
+use rustc_codegen_ssa::common::TypeKind;
+use rustc_codegen_ssa::traits::BaseTypeCodegenMethods;
 use rustc_errors::FatalError;
+use rustc_middle::bug;
 use tracing::{debug, trace};
 
 use crate::back::write::llvm_err;
@@ -18,21 +20,42 @@ use crate::value::Value;
 use crate::{CodegenContext, LlvmCodegenBackend, ModuleLlvm, attributes, llvm};
 
 fn get_params(fnc: &Value) -> Vec<&Value> {
+    let param_num = llvm::LLVMCountParams(fnc) as usize;
+    let mut fnc_args: Vec<&Value> = vec![];
+    fnc_args.reserve(param_num);
     unsafe {
-        let param_num = llvm::LLVMCountParams(fnc) as usize;
-        let mut fnc_args: Vec<&Value> = vec![];
-        fnc_args.reserve(param_num);
         llvm::LLVMGetParams(fnc, fnc_args.as_mut_ptr());
         fnc_args.set_len(param_num);
-        fnc_args
     }
+    fnc_args
 }
 
+fn has_sret(fnc: &Value) -> bool {
+    let num_args = llvm::LLVMCountParams(fnc) as usize;
+    if num_args == 0 {
+        false
+    } else {
+        unsafe { llvm::LLVMRustHasAttributeAtIndex(fnc, 0, llvm::AttributeKind::StructRet) }
+    }
+}
+
+// When we call the `__enzyme_autodiff` or `__enzyme_fwddiff` function, we need to pass all the
+// original inputs, as well as metadata and the additional shadow arguments.
+// This function matches the arguments from the outer function to the inner enzyme call.
+//
+// This function also considers that Rust level arguments not always match the llvm-ir level
+// arguments. A slice, `&[f32]`, for example, is represented as a pointer and a length on
+// llvm-ir level. The number of activities matches the number of Rust level arguments, so we
+// need to match those.
+// FIXME(ZuseZ4): This logic is a bit more complicated than it should be, can we simplify it
+// using iterators and peek()?
 fn match_args_from_caller_to_enzyme<'ll>(
     cx: &SimpleCx<'ll>,
+    width: u32,
     args: &mut Vec<&'ll llvm::Value>,
     inputs: &[DiffActivity],
     outer_args: &[&'ll llvm::Value],
+    has_sret: bool,
 ) {
     debug!("matching autodiff arguments");
     // We now handle the issue that Rust level arguments not always match the llvm-ir level
@@ -44,6 +67,14 @@ fn match_args_from_caller_to_enzyme<'ll>(
     let mut outer_pos: usize = 0;
     let mut activity_pos = 0;
 
+    if has_sret {
+        // Then the first outer arg is the sret pointer. Enzyme doesn't know about sret, so the
+        // inner function will still return something. We increase our outer_pos by one,
+        // and once we're done with all other args we will take the return of the inner call and
+        // update the sret pointer with it
+        outer_pos = 1;
+    }
+
     let enzyme_const = cx.create_metadata("enzyme_const".to_string()).unwrap();
     let enzyme_out = cx.create_metadata("enzyme_out".to_string()).unwrap();
     let enzyme_dup = cx.create_metadata("enzyme_dup".to_string()).unwrap();
@@ -92,23 +123,20 @@ fn match_args_from_caller_to_enzyme<'ll>(
                 // (..., metadata! enzyme_dup, ptr, ptr, int1, ...).
                 // FIXME(ZuseZ4): We will upstream a safety check later which asserts that
                 // int2 >= int1, which means the shadow vector is large enough to store the gradient.
-                assert!(unsafe {
-                    llvm::LLVMRustGetTypeKind(next_outer_ty) == llvm::TypeKind::Integer
-                });
-                let next_outer_arg2 = outer_args[outer_pos + 2];
-                let next_outer_ty2 = cx.val_ty(next_outer_arg2);
-                assert!(unsafe {
-                    llvm::LLVMRustGetTypeKind(next_outer_ty2) == llvm::TypeKind::Pointer
-                });
-                let next_outer_arg3 = outer_args[outer_pos + 3];
-                let next_outer_ty3 = cx.val_ty(next_outer_arg3);
-                assert!(unsafe {
-                    llvm::LLVMRustGetTypeKind(next_outer_ty3) == llvm::TypeKind::Integer
-                });
-                args.push(next_outer_arg2);
+                assert_eq!(cx.type_kind(next_outer_ty), TypeKind::Integer);
+
+                for i in 0..(width as usize) {
+                    let next_outer_arg2 = outer_args[outer_pos + 2 * (i + 1)];
+                    let next_outer_ty2 = cx.val_ty(next_outer_arg2);
+                    assert_eq!(cx.type_kind(next_outer_ty2), TypeKind::Pointer);
+                    let next_outer_arg3 = outer_args[outer_pos + 2 * (i + 1) + 1];
+                    let next_outer_ty3 = cx.val_ty(next_outer_arg3);
+                    assert_eq!(cx.type_kind(next_outer_ty3), TypeKind::Integer);
+                    args.push(next_outer_arg2);
+                }
                 args.push(cx.get_metadata_value(enzyme_const));
                 args.push(next_outer_arg);
-                outer_pos += 4;
+                outer_pos += 2 + 2 * width as usize;
                 activity_pos += 2;
             } else {
                 // A duplicated pointer will have the following two outer_fn arguments:
@@ -116,15 +144,19 @@ fn match_args_from_caller_to_enzyme<'ll>(
                 // (..., metadata! enzyme_dup, ptr, ptr, ...).
                 if matches!(diff_activity, DiffActivity::Duplicated | DiffActivity::DuplicatedOnly)
                 {
-                    assert!(
-                        unsafe { llvm::LLVMRustGetTypeKind(next_outer_ty) }
-                            == llvm::TypeKind::Pointer
-                    );
+                    assert_eq!(cx.type_kind(next_outer_ty), TypeKind::Pointer);
                 }
                 // In the case of Dual we don't have assumptions, e.g. f32 would be valid.
                 args.push(next_outer_arg);
                 outer_pos += 2;
                 activity_pos += 1;
+
+                // Now, if width > 1, we need to account for that
+                for _ in 1..width {
+                    let next_outer_arg = outer_args[outer_pos];
+                    args.push(next_outer_arg);
+                    outer_pos += 1;
+                }
             }
         } else {
             // We do not differentiate with resprect to this argument.
@@ -135,6 +167,76 @@ fn match_args_from_caller_to_enzyme<'ll>(
     }
 }
 
+// On LLVM-IR, we can luckily declare __enzyme_ functions without specifying the input
+// arguments. We do however need to declare them with their correct return type.
+// We already figured the correct return type out in our frontend, when generating the outer_fn,
+// so we can now just go ahead and use that. This is not always trivial, e.g. because sret.
+// Beyond sret, this article describes our challenges nicely:
+// <https://yorickpeterse.com/articles/the-mess-that-is-handling-structure-arguments-and-returns-in-llvm/>
+// I.e. (i32, f32) will get merged into i64, but we don't handle that yet.
+fn compute_enzyme_fn_ty<'ll>(
+    cx: &SimpleCx<'ll>,
+    attrs: &AutoDiffAttrs,
+    fn_to_diff: &'ll Value,
+    outer_fn: &'ll Value,
+) -> &'ll llvm::Type {
+    let fn_ty = cx.get_type_of_global(outer_fn);
+    let mut ret_ty = cx.get_return_type(fn_ty);
+
+    let has_sret = has_sret(outer_fn);
+
+    if has_sret {
+        // Now we don't just forward the return type, so we have to figure it out based on the
+        // primal return type, in combination with the autodiff settings.
+        let fn_ty = cx.get_type_of_global(fn_to_diff);
+        let inner_ret_ty = cx.get_return_type(fn_ty);
+
+        let void_ty = unsafe { llvm::LLVMVoidTypeInContext(cx.llcx) };
+        if inner_ret_ty == void_ty {
+            // This indicates that even the inner function has an sret.
+            // Right now I only look for an sret in the outer function.
+            // This *probably* needs some extra handling, but I never ran
+            // into such a case. So I'll wait for user reports to have a test case.
+            bug!("sret in inner function");
+        }
+
+        if attrs.width == 1 {
+            todo!("Handle sret for scalar ad");
+        } else {
+            // First we check if we also have to deal with the primal return.
+            match attrs.mode {
+                DiffMode::Forward => match attrs.ret_activity {
+                    DiffActivity::Dual => {
+                        let arr_ty =
+                            unsafe { llvm::LLVMArrayType2(inner_ret_ty, attrs.width as u64 + 1) };
+                        ret_ty = arr_ty;
+                    }
+                    DiffActivity::DualOnly => {
+                        let arr_ty =
+                            unsafe { llvm::LLVMArrayType2(inner_ret_ty, attrs.width as u64) };
+                        ret_ty = arr_ty;
+                    }
+                    DiffActivity::Const => {
+                        todo!("Not sure, do we need to do something here?");
+                    }
+                    _ => {
+                        bug!("unreachable");
+                    }
+                },
+                DiffMode::Reverse => {
+                    todo!("Handle sret for reverse mode");
+                }
+                _ => {
+                    bug!("unreachable");
+                }
+            }
+        }
+    }
+
+    // LLVM can figure out the input types on it's own, so we take a shortcut here.
+    unsafe { llvm::LLVMFunctionType(ret_ty, ptr::null(), 0, True) }
+}
+
 /// When differentiating `fn_to_diff`, take a `outer_fn` and generate another
 /// function with expected naming and calling conventions[^1] which will be
 /// discovered by the enzyme LLVM pass and its body populated with the differentiated
@@ -197,17 +299,9 @@ fn generate_enzyme_call<'ll>(
     // }
     // ```
     unsafe {
-        // On LLVM-IR, we can luckily declare __enzyme_ functions without specifying the input
-        // arguments. We do however need to declare them with their correct return type.
-        // We already figured the correct return type out in our frontend, when generating the outer_fn,
-        // so we can now just go ahead and use that. FIXME(ZuseZ4): This doesn't handle sret yet.
-        let fn_ty = llvm::LLVMGlobalGetValueType(outer_fn);
-        let ret_ty = llvm::LLVMGetReturnType(fn_ty);
-
-        // LLVM can figure out the input types on it's own, so we take a shortcut here.
-        let enzyme_ty = llvm::LLVMFunctionType(ret_ty, ptr::null(), 0, True);
+        let enzyme_ty = compute_enzyme_fn_ty(cx, &attrs, fn_to_diff, outer_fn);
 
-        //FIXME(ZuseZ4): the CC/Addr/Vis values are best effort guesses, we should look at tests and
+        // FIXME(ZuseZ4): the CC/Addr/Vis values are best effort guesses, we should look at tests and
         // think a bit more about what should go here.
         let cc = llvm::LLVMGetFunctionCallConv(outer_fn);
         let ad_fn = declare_simple_fn(
@@ -240,14 +334,27 @@ fn generate_enzyme_call<'ll>(
         if matches!(attrs.ret_activity, DiffActivity::Dual | DiffActivity::Active) {
             args.push(cx.get_metadata_value(enzyme_primal_ret));
         }
+        if attrs.width > 1 {
+            let enzyme_width = cx.create_metadata("enzyme_width".to_string()).unwrap();
+            args.push(cx.get_metadata_value(enzyme_width));
+            args.push(cx.get_const_i64(attrs.width as u64));
+        }
 
+        let has_sret = has_sret(outer_fn);
         let outer_args: Vec<&llvm::Value> = get_params(outer_fn);
-        match_args_from_caller_to_enzyme(&cx, &mut args, &attrs.input_activity, &outer_args);
+        match_args_from_caller_to_enzyme(
+            &cx,
+            attrs.width,
+            &mut args,
+            &attrs.input_activity,
+            &outer_args,
+            has_sret,
+        );
 
         let call = builder.call(enzyme_ty, ad_fn, &args, None);
 
         // This part is a bit iffy. LLVM requires that a call to an inlineable function has some
-        // metadata attachted to it, but we just created this code oota. Given that the
+        // metadata attached to it, but we just created this code oota. Given that the
         // differentiated function already has partly confusing metadata, and given that this
         // affects nothing but the auttodiff IR, we take a shortcut and just steal metadata from the
         // dummy code which we inserted at a higher level.
@@ -268,7 +375,22 @@ fn generate_enzyme_call<'ll>(
         // Now that we copied the metadata, get rid of dummy code.
         llvm::LLVMRustEraseInstUntilInclusive(entry, last_inst);
 
-        if cx.val_ty(call) == cx.type_void() {
+        if cx.val_ty(call) == cx.type_void() || has_sret {
+            if has_sret {
+                // This is what we already have in our outer_fn (shortened):
+                // define void @_foo(ptr <..> sret([32 x i8]) initializes((0, 32)) %0, <...>) {
+                //   %7 = call [4 x double] (...) @__enzyme_fwddiff_foo(ptr @square, metadata !"enzyme_width", i64 4, <...>)
+                //   <Here we are, we want to add the following two lines>
+                //   store [4 x double] %7, ptr %0, align 8
+                //   ret void
+                // }
+
+                // now store the result of the enzyme call into the sret pointer.
+                let sret_ptr = outer_args[0];
+                let call_ty = cx.val_ty(call);
+                assert_eq!(cx.type_kind(call_ty), TypeKind::Array);
+                llvm::LLVMBuildStore(&builder.llbuilder, call, sret_ptr);
+            }
             builder.ret_void();
         } else {
             builder.ret(call);
@@ -300,8 +422,7 @@ pub(crate) fn differentiate<'ll>(
     if !diff_items.is_empty()
         && !cgcx.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::Enable)
     {
-        let dcx = cgcx.create_dcx();
-        return Err(dcx.handle().emit_almost_fatal(AutoDiffWithoutEnable));
+        return Err(diag_handler.handle().emit_almost_fatal(AutoDiffWithoutEnable));
     }
 
     // Before dumping the module, we want all the TypeTrees to become part of the module.
diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs
index 7675e75338a..bf81eb648f8 100644
--- a/compiler/rustc_codegen_llvm/src/consts.rs
+++ b/compiler/rustc_codegen_llvm/src/consts.rs
@@ -430,7 +430,7 @@ impl<'ll> CodegenCx<'ll, '_> {
             let val_llty = self.val_ty(v);
 
             let g = self.get_static_inner(def_id, val_llty);
-            let llty = llvm::LLVMGlobalGetValueType(g);
+            let llty = self.get_type_of_global(g);
 
             let g = if val_llty == llty {
                 g
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index f7b096ff976..4ec69995518 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -8,6 +8,7 @@ use std::str;
 use rustc_abi::{HasDataLayout, Size, TargetDataLayout, VariantIdx};
 use rustc_codegen_ssa::back::versioned_llvm_target;
 use rustc_codegen_ssa::base::{wants_msvc_seh, wants_wasm_eh};
+use rustc_codegen_ssa::common::TypeKind;
 use rustc_codegen_ssa::errors as ssa_errors;
 use rustc_codegen_ssa::traits::*;
 use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN};
@@ -38,7 +39,7 @@ use crate::debuginfo::metadata::apply_vcall_visibility_metadata;
 use crate::llvm::Metadata;
 use crate::type_::Type;
 use crate::value::Value;
-use crate::{attributes, coverageinfo, debuginfo, llvm, llvm_util};
+use crate::{attributes, common, coverageinfo, debuginfo, llvm, llvm_util};
 
 /// `TyCtxt` (and related cache datastructures) can't be move between threads.
 /// However, there are various cx related functions which we want to be available to the builder and
@@ -163,23 +164,6 @@ pub(crate) unsafe fn create_module<'ll>(
     let mut target_data_layout = sess.target.data_layout.to_string();
     let llvm_version = llvm_util::get_version();
 
-    if llvm_version < (19, 0, 0) {
-        if sess.target.arch == "aarch64" || sess.target.arch.starts_with("arm64") {
-            // LLVM 19 sets -Fn32 in its data layout string for 64-bit ARM
-            // Earlier LLVMs leave this default, so remove it.
-            // See https://github.com/llvm/llvm-project/pull/90702
-            target_data_layout = target_data_layout.replace("-Fn32", "");
-        }
-    }
-
-    if llvm_version < (19, 0, 0) {
-        if sess.target.arch == "loongarch64" {
-            // LLVM 19 updates the LoongArch64 data layout.
-            // See https://github.com/llvm/llvm-project/pull/93814
-            target_data_layout = target_data_layout.replace("-n32:64", "-n64");
-        }
-    }
-
     if llvm_version < (20, 0, 0) {
         if sess.target.arch == "aarch64" || sess.target.arch.starts_with("arm64") {
             // LLVM 20 defines three additional address spaces for alternate
@@ -327,6 +311,22 @@ pub(crate) unsafe fn create_module<'ll>(
                 pfe.prefix().into(),
             );
         }
+
+        // Add "kcfi-arity" module flag if KCFI arity indicator is enabled. (See
+        // https://github.com/llvm/llvm-project/pull/117121.)
+        if sess.is_sanitizer_kcfi_arity_enabled() {
+            // KCFI arity indicator requires LLVM 21.0.0 or later.
+            if llvm_version < (21, 0, 0) {
+                tcx.dcx().emit_err(crate::errors::SanitizerKcfiArityRequiresLLVM2100);
+            }
+
+            llvm::add_module_flag_u32(
+                llmod,
+                llvm::ModuleFlagMergeBehavior::Override,
+                "kcfi-arity",
+                1,
+            );
+        }
     }
 
     // Control Flow Guard is currently only supported by MSVC and LLVM on Windows.
@@ -643,7 +643,18 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
         llvm::set_section(g, c"llvm.metadata");
     }
 }
-
+impl<'ll> SimpleCx<'ll> {
+    pub(crate) fn get_return_type(&self, ty: &'ll Type) -> &'ll Type {
+        assert_eq!(self.type_kind(ty), TypeKind::Function);
+        unsafe { llvm::LLVMGetReturnType(ty) }
+    }
+    pub(crate) fn get_type_of_global(&self, val: &'ll Value) -> &'ll Type {
+        unsafe { llvm::LLVMGlobalGetValueType(val) }
+    }
+    pub(crate) fn val_ty(&self, v: &'ll Value) -> &'ll Type {
+        common::val_ty(v)
+    }
+}
 impl<'ll> SimpleCx<'ll> {
     pub(crate) fn new(
         llmod: &'ll llvm::Module,
@@ -660,6 +671,13 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
         llvm::LLVMMetadataAsValue(self.llcx(), metadata)
     }
 
+    // FIXME(autodiff): We should split `ConstCodegenMethods` to pull the reusable parts
+    // onto a trait that is also implemented for GenericCx.
+    pub(crate) fn get_const_i64(&self, n: u64) -> &'ll Value {
+        let ty = unsafe { llvm::LLVMInt64TypeInContext(self.llcx()) };
+        unsafe { llvm::LLVMConstInt(ty, n, llvm::False) }
+    }
+
     pub(crate) fn get_function(&self, name: &str) -> Option<&'ll Value> {
         let name = SmallCStr::new(name);
         unsafe { llvm::LLVMGetNamedFunction((**self).borrow().llmod, name.as_ptr()) }
@@ -1183,10 +1201,8 @@ impl<'ll> CodegenCx<'ll, '_> {
 
         if self.sess().instrument_coverage() {
             ifn!("llvm.instrprof.increment", fn(ptr, t_i64, t_i32, t_i32) -> void);
-            if crate::llvm_util::get_version() >= (19, 0, 0) {
-                ifn!("llvm.instrprof.mcdc.parameters", fn(ptr, t_i64, t_i32) -> void);
-                ifn!("llvm.instrprof.mcdc.tvbitmap.update", fn(ptr, t_i64, t_i32, ptr) -> void);
-            }
+            ifn!("llvm.instrprof.mcdc.parameters", fn(ptr, t_i64, t_i32) -> void);
+            ifn!("llvm.instrprof.mcdc.tvbitmap.update", fn(ptr, t_i64, t_i32, ptr) -> void);
         }
 
         ifn!("llvm.type.test", fn(ptr, t_metadata) -> i1);
diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs
index 4c5a78ca74f..ecf108f988f 100644
--- a/compiler/rustc_codegen_llvm/src/errors.rs
+++ b/compiler/rustc_codegen_llvm/src/errors.rs
@@ -217,3 +217,7 @@ pub(crate) struct MismatchedDataLayout<'a> {
 pub(crate) struct FixedX18InvalidArch<'a> {
     pub arch: &'a str,
 }
+
+#[derive(Diagnostic)]
+#[diag(codegen_llvm_sanitizer_kcfi_arity_requires_llvm_21_0_0)]
+pub(crate) struct SanitizerKcfiArityRequiresLLVM2100;
diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs
index 79e4cc8aa77..a9b3bdf7344 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs
@@ -4,7 +4,7 @@
 use libc::{c_char, c_uint};
 
 use super::MetadataKindId;
-use super::ffi::{BasicBlock, Metadata, Module, Type, Value};
+use super::ffi::{AttributeKind, BasicBlock, Metadata, Module, Type, Value};
 use crate::llvm::Bool;
 
 #[link(name = "llvm-wrapper", kind = "static")]
@@ -17,6 +17,8 @@ unsafe extern "C" {
     pub(crate) fn LLVMRustEraseInstFromParent(V: &Value);
     pub(crate) fn LLVMRustGetTerminator<'a>(B: &BasicBlock) -> &'a Value;
     pub(crate) fn LLVMRustVerifyFunction(V: &Value, action: LLVMRustVerifierFailureAction) -> Bool;
+    pub(crate) fn LLVMRustHasAttributeAtIndex(V: &Value, i: c_uint, Kind: AttributeKind) -> bool;
+    pub(crate) fn LLVMRustGetArrayNumElements(Ty: &Type) -> u64;
 }
 
 unsafe extern "C" {
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index 3ce3761944b..9ff04f72903 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -1180,7 +1180,7 @@ unsafe extern "C" {
 
     // Operations on parameters
     pub(crate) fn LLVMIsAArgument(Val: &Value) -> Option<&Value>;
-    pub(crate) fn LLVMCountParams(Fn: &Value) -> c_uint;
+    pub(crate) safe fn LLVMCountParams(Fn: &Value) -> c_uint;
     pub(crate) fn LLVMGetParam(Fn: &Value, Index: c_uint) -> &Value;
 
     // Operations on basic blocks
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index 4a166b0872d..36e35f81392 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -256,7 +256,6 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
         ("aarch64", "pmuv3") => Some(LLVMFeature::new("perfmon")),
         ("aarch64", "paca") => Some(LLVMFeature::new("pauth")),
         ("aarch64", "pacg") => Some(LLVMFeature::new("pauth")),
-        ("aarch64", "pauth-lr") if get_version().0 < 19 => None,
         // Before LLVM 20 those two features were packaged together as b16b16
         ("aarch64", "sve-b16b16") if get_version().0 < 20 => Some(LLVMFeature::new("b16b16")),
         ("aarch64", "sme-b16b16") if get_version().0 < 20 => Some(LLVMFeature::new("b16b16")),
@@ -270,18 +269,9 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
         ("aarch64", "fhm") => Some(LLVMFeature::new("fp16fml")),
         ("aarch64", "fp16") => Some(LLVMFeature::new("fullfp16")),
         // Filter out features that are not supported by the current LLVM version
-        ("aarch64", "fpmr") if get_version().0 != 18 => None,
+        ("aarch64", "fpmr") => None, // only existed in 18
         ("arm", "fp16") => Some(LLVMFeature::new("fullfp16")),
-        // In LLVM 18, `unaligned-scalar-mem` was merged with `unaligned-vector-mem` into a single
-        // feature called `fast-unaligned-access`. In LLVM 19, it was split back out.
-        ("riscv32" | "riscv64", "unaligned-scalar-mem") if get_version().0 == 18 => {
-            Some(LLVMFeature::new("fast-unaligned-access"))
-        }
         // Filter out features that are not supported by the current LLVM version
-        ("riscv32" | "riscv64", "zaamo") if get_version().0 < 19 => None,
-        ("riscv32" | "riscv64", "zabha") if get_version().0 < 19 => None,
-        ("riscv32" | "riscv64", "zalrsc") if get_version().0 < 19 => None,
-        ("riscv32" | "riscv64", "zama16b") if get_version().0 < 19 => None,
         ("riscv32" | "riscv64", "zacas") if get_version().0 < 20 => None,
         // Enable the evex512 target feature if an avx512 target feature is enabled.
         ("x86", s) if s.starts_with("avx512") => {
@@ -293,11 +283,17 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
         ("sparc", "leoncasa") => Some(LLVMFeature::new("hasleoncasa")),
         // In LLVM 19, there is no `v8plus` feature and `v9` means "SPARC-V9 instruction available and SPARC-V8+ ABI used".
         // https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/llvm/lib/Target/Sparc/MCTargetDesc/SparcELFObjectWriter.cpp#L27-L28
-        // Before LLVM 19, there is no `v8plus` feature and `v9` means "SPARC-V9 instruction available".
+        // Before LLVM 19, there was no `v8plus` feature and `v9` means "SPARC-V9 instruction available".
         // https://github.com/llvm/llvm-project/blob/llvmorg-18.1.0/llvm/lib/Target/Sparc/MCTargetDesc/SparcELFObjectWriter.cpp#L26
         ("sparc", "v8plus") if get_version().0 == 19 => Some(LLVMFeature::new("v9")),
-        ("sparc", "v8plus") if get_version().0 < 19 => None,
         ("powerpc", "power8-crypto") => Some(LLVMFeature::new("crypto")),
+        // These new `amx` variants and `movrs` were introduced in LLVM20
+        ("x86", "amx-avx512" | "amx-fp8" | "amx-movrs" | "amx-tf32" | "amx-transpose")
+            if get_version().0 < 20 =>
+        {
+            None
+        }
+        ("x86", "movrs") if get_version().0 < 20 => None,
         (_, s) => Some(LLVMFeature::new(s)),
     }
 }
diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs
index a64627eaf59..fdf62a08065 100644
--- a/compiler/rustc_codegen_llvm/src/mono_item.rs
+++ b/compiler/rustc_codegen_llvm/src/mono_item.rs
@@ -120,7 +120,7 @@ impl CodegenCx<'_, '_> {
         }
 
         // Match clang by only supporting COFF and ELF for now.
-        if self.tcx.sess.target.is_like_osx {
+        if self.tcx.sess.target.is_like_darwin {
             return false;
         }
 
diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs
index 8baa69cefe1..c216f0f4a09 100644
--- a/compiler/rustc_codegen_llvm/src/va_arg.rs
+++ b/compiler/rustc_codegen_llvm/src/va_arg.rs
@@ -399,7 +399,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
             emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(8).unwrap(), false)
         }
         // macOS / iOS AArch64
-        "aarch64" if target.is_like_osx => {
+        "aarch64" if target.is_like_darwin => {
             emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(8).unwrap(), true)
         }
         "aarch64" => emit_aapcs_va_arg(bx, addr, target_ty),
diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index 954a6014809..1dabf01ffd6 100644
--- a/compiler/rustc_codegen_ssa/messages.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
@@ -4,12 +4,6 @@ codegen_ssa_add_native_library = failed to add native library {$library_path}: {
 
 codegen_ssa_aix_strip_not_used = using host's `strip` binary to cross-compile to AIX which is not guaranteed to work
 
-codegen_ssa_apple_deployment_target_invalid =
-    failed to parse deployment target specified in {$env_var}: {$error}
-
-codegen_ssa_apple_deployment_target_too_low =
-    deployment target in {$env_var} was set to {$version}, but the minimum supported by `rustc` is {$os_min}
-
 codegen_ssa_archive_build_failure = failed to build archive at `{$path}`: {$error}
 
 codegen_ssa_atomic_compare_exchange = Atomic compare-exchange intrinsic missing failure memory ordering
diff --git a/compiler/rustc_codegen_ssa/src/back/apple.rs b/compiler/rustc_codegen_ssa/src/back/apple.rs
index 2c8b0ec418d..d242efaf4fd 100644
--- a/compiler/rustc_codegen_ssa/src/back/apple.rs
+++ b/compiler/rustc_codegen_ssa/src/back/apple.rs
@@ -1,7 +1,4 @@
-use std::env;
 use std::ffi::OsString;
-use std::fmt::{Display, from_fn};
-use std::num::ParseIntError;
 use std::path::PathBuf;
 use std::process::Command;
 
@@ -9,9 +6,10 @@ use itertools::Itertools;
 use rustc_middle::middle::exported_symbols::SymbolExportKind;
 use rustc_session::Session;
 use rustc_target::spec::Target;
+pub(super) use rustc_target::spec::apple::OSVersion;
 use tracing::debug;
 
-use crate::errors::{AppleDeploymentTarget, XcrunError, XcrunSdkPathWarning};
+use crate::errors::{XcrunError, XcrunSdkPathWarning};
 use crate::fluent_generated as fluent;
 
 #[cfg(test)]
@@ -134,124 +132,6 @@ pub(super) fn add_data_and_relocation(
     Ok(())
 }
 
-/// Deployment target or SDK version.
-///
-/// The size of the numbers in here are limited by Mach-O's `LC_BUILD_VERSION`.
-type OSVersion = (u16, u8, u8);
-
-/// Parse an OS version triple (SDK version or deployment target).
-fn parse_version(version: &str) -> Result<OSVersion, ParseIntError> {
-    if let Some((major, minor)) = version.split_once('.') {
-        let major = major.parse()?;
-        if let Some((minor, patch)) = minor.split_once('.') {
-            Ok((major, minor.parse()?, patch.parse()?))
-        } else {
-            Ok((major, minor.parse()?, 0))
-        }
-    } else {
-        Ok((version.parse()?, 0, 0))
-    }
-}
-
-pub fn pretty_version(version: OSVersion) -> impl Display {
-    let (major, minor, patch) = version;
-    from_fn(move |f| {
-        write!(f, "{major}.{minor}")?;
-        if patch != 0 {
-            write!(f, ".{patch}")?;
-        }
-        Ok(())
-    })
-}
-
-/// Minimum operating system versions currently supported by `rustc`.
-fn os_minimum_deployment_target(os: &str) -> OSVersion {
-    // When bumping a version in here, remember to update the platform-support docs too.
-    //
-    // NOTE: The defaults may change in future `rustc` versions, so if you are looking for the
-    // default deployment target, prefer:
-    // ```
-    // $ rustc --print deployment-target
-    // ```
-    match os {
-        "macos" => (10, 12, 0),
-        "ios" => (10, 0, 0),
-        "tvos" => (10, 0, 0),
-        "watchos" => (5, 0, 0),
-        "visionos" => (1, 0, 0),
-        _ => unreachable!("tried to get deployment target for non-Apple platform"),
-    }
-}
-
-/// The deployment target for the given target.
-///
-/// This is similar to `os_minimum_deployment_target`, except that on certain targets it makes sense
-/// to raise the minimum OS version.
-///
-/// This matches what LLVM does, see in part:
-/// <https://github.com/llvm/llvm-project/blob/llvmorg-18.1.8/llvm/lib/TargetParser/Triple.cpp#L1900-L1932>
-fn minimum_deployment_target(target: &Target) -> OSVersion {
-    match (&*target.os, &*target.arch, &*target.abi) {
-        ("macos", "aarch64", _) => (11, 0, 0),
-        ("ios", "aarch64", "macabi") => (14, 0, 0),
-        ("ios", "aarch64", "sim") => (14, 0, 0),
-        ("ios", _, _) if target.llvm_target.starts_with("arm64e") => (14, 0, 0),
-        // Mac Catalyst defaults to 13.1 in Clang.
-        ("ios", _, "macabi") => (13, 1, 0),
-        ("tvos", "aarch64", "sim") => (14, 0, 0),
-        ("watchos", "aarch64", "sim") => (7, 0, 0),
-        (os, _, _) => os_minimum_deployment_target(os),
-    }
-}
-
-/// Name of the environment variable used to fetch the deployment target on the given OS.
-pub fn deployment_target_env_var(os: &str) -> &'static str {
-    match os {
-        "macos" => "MACOSX_DEPLOYMENT_TARGET",
-        "ios" => "IPHONEOS_DEPLOYMENT_TARGET",
-        "watchos" => "WATCHOS_DEPLOYMENT_TARGET",
-        "tvos" => "TVOS_DEPLOYMENT_TARGET",
-        "visionos" => "XROS_DEPLOYMENT_TARGET",
-        _ => unreachable!("tried to get deployment target env var for non-Apple platform"),
-    }
-}
-
-/// Get the deployment target based on the standard environment variables, or fall back to the
-/// minimum version supported by `rustc`.
-pub fn deployment_target(sess: &Session) -> OSVersion {
-    let min = minimum_deployment_target(&sess.target);
-    let env_var = deployment_target_env_var(&sess.target.os);
-
-    if let Ok(deployment_target) = env::var(env_var) {
-        match parse_version(&deployment_target) {
-            Ok(version) => {
-                let os_min = os_minimum_deployment_target(&sess.target.os);
-                // It is common that the deployment target is set a bit too low, for example on
-                // macOS Aarch64 to also target older x86_64. So we only want to warn when variable
-                // is lower than the minimum OS supported by rustc, not when the variable is lower
-                // than the minimum for a specific target.
-                if version < os_min {
-                    sess.dcx().emit_warn(AppleDeploymentTarget::TooLow {
-                        env_var,
-                        version: pretty_version(version).to_string(),
-                        os_min: pretty_version(os_min).to_string(),
-                    });
-                }
-
-                // Raise the deployment target to the minimum supported.
-                version.max(min)
-            }
-            Err(error) => {
-                sess.dcx().emit_err(AppleDeploymentTarget::Invalid { env_var, error });
-                min
-            }
-        }
-    } else {
-        // If no deployment target variable is set, default to the minimum found above.
-        min
-    }
-}
-
 pub(super) fn add_version_to_llvm_target(
     llvm_target: &str,
     deployment_target: OSVersion,
@@ -263,18 +143,17 @@ pub(super) fn add_version_to_llvm_target(
     let environment = components.next();
     assert_eq!(components.next(), None, "too many LLVM triple components");
 
-    let (major, minor, patch) = deployment_target;
-
     assert!(
         !os.contains(|c: char| c.is_ascii_digit()),
         "LLVM target must not already be versioned"
     );
 
+    let version = deployment_target.fmt_full();
     if let Some(env) = environment {
         // Insert version into OS, before environment
-        format!("{arch}-{vendor}-{os}{major}.{minor}.{patch}-{env}")
+        format!("{arch}-{vendor}-{os}{version}-{env}")
     } else {
-        format!("{arch}-{vendor}-{os}{major}.{minor}.{patch}")
+        format!("{arch}-{vendor}-{os}{version}")
     }
 }
 
diff --git a/compiler/rustc_codegen_ssa/src/back/apple/tests.rs b/compiler/rustc_codegen_ssa/src/back/apple/tests.rs
index 8df740a4bcf..5afe79b7195 100644
--- a/compiler/rustc_codegen_ssa/src/back/apple/tests.rs
+++ b/compiler/rustc_codegen_ssa/src/back/apple/tests.rs
@@ -3,24 +3,16 @@ use super::*;
 #[test]
 fn test_add_version_to_llvm_target() {
     assert_eq!(
-        add_version_to_llvm_target("aarch64-apple-macosx", (10, 14, 1)),
+        add_version_to_llvm_target("aarch64-apple-macosx", OSVersion::new(10, 14, 1)),
         "aarch64-apple-macosx10.14.1"
     );
     assert_eq!(
-        add_version_to_llvm_target("aarch64-apple-ios-simulator", (16, 1, 0)),
+        add_version_to_llvm_target("aarch64-apple-ios-simulator", OSVersion::new(16, 1, 0)),
         "aarch64-apple-ios16.1.0-simulator"
     );
 }
 
 #[test]
-fn test_parse_version() {
-    assert_eq!(parse_version("10"), Ok((10, 0, 0)));
-    assert_eq!(parse_version("10.12"), Ok((10, 12, 0)));
-    assert_eq!(parse_version("10.12.6"), Ok((10, 12, 6)));
-    assert_eq!(parse_version("9999.99.99"), Ok((9999, 99, 99)));
-}
-
-#[test]
 #[cfg_attr(not(target_os = "macos"), ignore = "xcode-select is only available on macOS")]
 fn lookup_developer_dir() {
     let _developer_dir = xcode_select_developer_dir().unwrap();
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index b59d73a9aae..1d05c4eee4d 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -294,7 +294,7 @@ fn link_rlib<'a>(
             let (metadata, metadata_position) = create_wrapper_file(
                 sess,
                 ".rmeta".to_string(),
-                codegen_results.metadata.raw_data(),
+                codegen_results.metadata.stub_or_full(),
             );
             let metadata = emit_wrapper_file(sess, &metadata, tmpdir, METADATA_FILENAME);
             match metadata_position {
@@ -959,9 +959,9 @@ fn link_natively(
                 }
             }
 
-            let (level, src) = codegen_results.crate_info.lint_levels.linker_messages;
+            let level = codegen_results.crate_info.lint_levels.linker_messages;
             let lint = |msg| {
-                lint_level(sess, LINKER_MESSAGES, level, src, None, |diag| {
+                lint_level(sess, LINKER_MESSAGES, level, None, |diag| {
                     LinkerOutput { inner: msg }.decorate_lint(diag)
                 })
             };
@@ -1012,7 +1012,7 @@ fn link_natively(
         // On macOS the external `dsymutil` tool is used to create the packed
         // debug information. Note that this will read debug information from
         // the objects on the filesystem which we'll clean up later.
-        SplitDebuginfo::Packed if sess.target.is_like_osx => {
+        SplitDebuginfo::Packed if sess.target.is_like_darwin => {
             let prog = Command::new("dsymutil").arg(out_filename).output();
             match prog {
                 Ok(prog) => {
@@ -1043,7 +1043,7 @@ fn link_natively(
 
     let strip = sess.opts.cg.strip;
 
-    if sess.target.is_like_osx {
+    if sess.target.is_like_darwin {
         let stripcmd = "rust-objcopy";
         match (strip, crate_type) {
             (Strip::Debuginfo, _) => {
@@ -1241,7 +1241,7 @@ fn add_sanitizer_libraries(
     // Everywhere else the runtimes are currently distributed as static
     // libraries which should be linked to executables only.
     if matches!(crate_type, CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro)
-        && !(sess.target.is_like_osx || sess.target.is_like_msvc)
+        && !(sess.target.is_like_darwin || sess.target.is_like_msvc)
     {
         return;
     }
@@ -1294,7 +1294,7 @@ fn link_sanitizer_runtime(
     let channel =
         option_env!("CFG_RELEASE_CHANNEL").map(|channel| format!("-{channel}")).unwrap_or_default();
 
-    if sess.target.is_like_osx {
+    if sess.target.is_like_darwin {
         // On Apple platforms, the sanitizer is always built as a dylib, and
         // LLVM will link to `@rpath/*.dylib`, so we need to specify an
         // rpath to the library as well (the rpath should be absolute, see
@@ -2182,7 +2182,7 @@ fn add_rpath_args(
         let rpath_config = RPathConfig {
             libs: &*libs,
             out_filename: out_filename.to_path_buf(),
-            is_like_osx: sess.target.is_like_osx,
+            is_like_darwin: sess.target.is_like_darwin,
             linker_is_gnu: sess.target.linker_flavor.is_gnu(),
         };
         cmd.link_args(&rpath::get_rpath_linker_args(&rpath_config));
@@ -3044,7 +3044,7 @@ pub(crate) fn are_upstream_rust_objects_already_included(sess: &Session) -> bool
 /// - The deployment target.
 /// - The SDK version.
 fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
-    if !sess.target.is_like_osx {
+    if !sess.target.is_like_darwin {
         return;
     }
     let LinkerFlavor::Darwin(cc, _) = flavor else {
@@ -3115,8 +3115,7 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
             _ => bug!("invalid OS/ABI combination for Apple target: {target_os}, {target_abi}"),
         };
 
-        let (major, minor, patch) = apple::deployment_target(sess);
-        let min_version = format!("{major}.{minor}.{patch}");
+        let min_version = sess.apple_deployment_target().fmt_full().to_string();
 
         // The SDK version is used at runtime when compiling with a newer SDK / version of Xcode:
         // - By dyld to give extra warnings and errors, see e.g.:
@@ -3185,10 +3184,10 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
 
             // The presence of `-mmacosx-version-min` makes CC default to
             // macOS, and it sets the deployment target.
-            let (major, minor, patch) = apple::deployment_target(sess);
+            let version = sess.apple_deployment_target().fmt_full();
             // Intentionally pass this as a single argument, Clang doesn't
             // seem to like it otherwise.
-            cmd.cc_arg(&format!("-mmacosx-version-min={major}.{minor}.{patch}"));
+            cmd.cc_arg(&format!("-mmacosx-version-min={version}"));
 
             // macOS has no environment, so with these two, we've told CC the
             // four desired parameters.
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index bcf18cf57be..a09eec5dd74 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -373,7 +373,7 @@ impl<'a> GccLinker<'a> {
         // * On OSX they have their own linker, not binutils'
         // * For WebAssembly the only functional linker is LLD, which doesn't
         //   support hint flags
-        !self.sess.target.is_like_osx && !self.sess.target.is_like_wasm
+        !self.sess.target.is_like_darwin && !self.sess.target.is_like_wasm
     }
 
     // Some platforms take hints about whether a library is static or dynamic.
@@ -425,7 +425,7 @@ impl<'a> GccLinker<'a> {
 
     fn build_dylib(&mut self, crate_type: CrateType, out_filename: &Path) {
         // On mac we need to tell the linker to let this library be rpathed
-        if self.sess.target.is_like_osx {
+        if self.sess.target.is_like_darwin {
             if self.is_cc() {
                 // `-dynamiclib` makes `cc` pass `-dylib` to the linker.
                 self.cc_arg("-dynamiclib");
@@ -471,7 +471,7 @@ impl<'a> GccLinker<'a> {
 
     fn with_as_needed(&mut self, as_needed: bool, f: impl FnOnce(&mut Self)) {
         if !as_needed {
-            if self.sess.target.is_like_osx {
+            if self.sess.target.is_like_darwin {
                 // FIXME(81490): ld64 doesn't support these flags but macOS 11
                 // has -needed-l{} / -needed_library {}
                 // but we have no way to detect that here.
@@ -486,7 +486,7 @@ impl<'a> GccLinker<'a> {
         f(self);
 
         if !as_needed {
-            if self.sess.target.is_like_osx {
+            if self.sess.target.is_like_darwin {
                 // See above FIXME comment
             } else if self.is_gnu && !self.sess.target.is_like_windows {
                 self.link_arg("--as-needed");
@@ -619,7 +619,7 @@ impl<'a> Linker for GccLinker<'a> {
         let colon = if verbatim && self.is_gnu { ":" } else { "" };
         if !whole_archive {
             self.link_or_cc_arg(format!("-l{colon}{name}"));
-        } else if self.sess.target.is_like_osx {
+        } else if self.sess.target.is_like_darwin {
             // -force_load is the macOS equivalent of --whole-archive, but it
             // involves passing the full path to the library to link.
             self.link_arg("-force_load");
@@ -635,7 +635,7 @@ impl<'a> Linker for GccLinker<'a> {
         self.hint_static();
         if !whole_archive {
             self.link_or_cc_arg(path);
-        } else if self.sess.target.is_like_osx {
+        } else if self.sess.target.is_like_darwin {
             self.link_arg("-force_load").link_arg(path);
         } else {
             self.link_arg("--whole-archive").link_arg(path).link_arg("--no-whole-archive");
@@ -670,7 +670,7 @@ impl<'a> Linker for GccLinker<'a> {
         // -dead_strip can't be part of the pre_link_args because it's also used
         // for partial linking when using multiple codegen units (-r). So we
         // insert it here.
-        if self.sess.target.is_like_osx {
+        if self.sess.target.is_like_darwin {
             self.link_arg("-dead_strip");
 
         // If we're building a dylib, we don't use --gc-sections because LLVM
@@ -728,7 +728,7 @@ impl<'a> Linker for GccLinker<'a> {
 
     fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {
         // MacOS linker doesn't support stripping symbols directly anymore.
-        if self.sess.target.is_like_osx {
+        if self.sess.target.is_like_darwin {
             return;
         }
 
@@ -795,7 +795,7 @@ impl<'a> Linker for GccLinker<'a> {
 
         debug!("EXPORTED SYMBOLS:");
 
-        if self.sess.target.is_like_osx {
+        if self.sess.target.is_like_darwin {
             // Write a plain, newline-separated list of symbols
             let res: io::Result<()> = try {
                 let mut f = File::create_buffered(&path)?;
@@ -841,7 +841,7 @@ impl<'a> Linker for GccLinker<'a> {
             }
         }
 
-        if self.sess.target.is_like_osx {
+        if self.sess.target.is_like_darwin {
             self.link_arg("-exported_symbols_list").link_arg(path);
         } else if self.sess.target.is_like_solaris {
             self.link_arg("-M").link_arg(path);
diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs
index 68b453ff424..ebcccf1b97d 100644
--- a/compiler/rustc_codegen_ssa/src/back/metadata.rs
+++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs
@@ -214,7 +214,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
 
     let mut file = write::Object::new(binary_format, architecture, endianness);
     file.set_sub_architecture(sub_architecture);
-    if sess.target.is_like_osx {
+    if sess.target.is_like_darwin {
         if macho_is_arm64e(&sess.target) {
             file.set_macho_cpu_subtype(object::macho::CPU_SUBTYPE_ARM64E);
         }
@@ -388,13 +388,13 @@ pub(super) fn elf_e_flags(architecture: Architecture, sess: &Session) -> u32 {
 fn macho_object_build_version_for_target(sess: &Session) -> object::write::MachOBuildVersion {
     /// The `object` crate demands "X.Y.Z encoded in nibbles as xxxx.yy.zz"
     /// e.g. minOS 14.0 = 0x000E0000, or SDK 16.2 = 0x00100200
-    fn pack_version((major, minor, patch): (u16, u8, u8)) -> u32 {
+    fn pack_version(apple::OSVersion { major, minor, patch }: apple::OSVersion) -> u32 {
         let (major, minor, patch) = (major as u32, minor as u32, patch as u32);
         (major << 16) | (minor << 8) | patch
     }
 
     let platform = apple::macho_platform(&sess.target);
-    let min_os = apple::deployment_target(sess);
+    let min_os = sess.apple_deployment_target();
 
     let mut build_version = object::write::MachOBuildVersion::default();
     build_version.platform = platform;
@@ -540,8 +540,8 @@ pub fn create_compressed_metadata_file(
     symbol_name: &str,
 ) -> Vec<u8> {
     let mut packed_metadata = rustc_metadata::METADATA_HEADER.to_vec();
-    packed_metadata.write_all(&(metadata.raw_data().len() as u64).to_le_bytes()).unwrap();
-    packed_metadata.extend(metadata.raw_data());
+    packed_metadata.write_all(&(metadata.stub_or_full().len() as u64).to_le_bytes()).unwrap();
+    packed_metadata.extend(metadata.stub_or_full());
 
     let Some(mut file) = create_object_file(sess) else {
         if sess.target.is_like_wasm {
diff --git a/compiler/rustc_codegen_ssa/src/back/mod.rs b/compiler/rustc_codegen_ssa/src/back/mod.rs
index 64b5d4569ec..8d1adb99930 100644
--- a/compiler/rustc_codegen_ssa/src/back/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/back/mod.rs
@@ -19,8 +19,8 @@ pub mod write;
 ///
 /// Certain optimizations also depend on the deployment target.
 pub fn versioned_llvm_target(sess: &Session) -> Cow<'_, str> {
-    if sess.target.is_like_osx {
-        apple::add_version_to_llvm_target(&sess.target.llvm_target, apple::deployment_target(sess))
+    if sess.target.is_like_darwin {
+        apple::add_version_to_llvm_target(&sess.target.llvm_target, sess.apple_deployment_target())
             .into()
     } else {
         // FIXME(madsmtm): Certain other targets also include a version,
diff --git a/compiler/rustc_codegen_ssa/src/back/rpath.rs b/compiler/rustc_codegen_ssa/src/back/rpath.rs
index d633cc98ac8..7bb8979e882 100644
--- a/compiler/rustc_codegen_ssa/src/back/rpath.rs
+++ b/compiler/rustc_codegen_ssa/src/back/rpath.rs
@@ -9,7 +9,7 @@ use tracing::debug;
 pub(super) struct RPathConfig<'a> {
     pub libs: &'a [&'a Path],
     pub out_filename: PathBuf,
-    pub is_like_osx: bool,
+    pub is_like_darwin: bool,
     pub linker_is_gnu: bool,
 }
 
@@ -63,7 +63,7 @@ fn get_rpaths_relative_to_output(config: &RPathConfig<'_>) -> Vec<OsString> {
 
 fn get_rpath_relative_to_output(config: &RPathConfig<'_>, lib: &Path) -> OsString {
     // Mac doesn't appear to support $ORIGIN
-    let prefix = if config.is_like_osx { "@loader_path" } else { "$ORIGIN" };
+    let prefix = if config.is_like_darwin { "@loader_path" } else { "$ORIGIN" };
 
     // Strip filenames
     let lib = lib.parent().unwrap();
diff --git a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs
index f1a30105c59..ab8fbedb812 100644
--- a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs
+++ b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs
@@ -28,7 +28,7 @@ fn test_rpath_relative() {
     if cfg!(target_os = "macos") {
         let config = &mut RPathConfig {
             libs: &[],
-            is_like_osx: true,
+            is_like_darwin: true,
             linker_is_gnu: false,
             out_filename: PathBuf::from("bin/rustc"),
         };
@@ -38,7 +38,7 @@ fn test_rpath_relative() {
         let config = &mut RPathConfig {
             libs: &[],
             out_filename: PathBuf::from("bin/rustc"),
-            is_like_osx: false,
+            is_like_darwin: false,
             linker_is_gnu: true,
         };
         let res = get_rpath_relative_to_output(config, Path::new("lib/libstd.so"));
@@ -51,7 +51,7 @@ fn test_rpath_relative_issue_119571() {
     let config = &mut RPathConfig {
         libs: &[],
         out_filename: PathBuf::from("rustc"),
-        is_like_osx: false,
+        is_like_darwin: false,
         linker_is_gnu: true,
     };
     // Should not panic when out_filename only contains filename.
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index 216a18e72ed..e816e71379a 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -352,7 +352,7 @@ pub struct CodegenContext<B: WriteBackendMethods> {
     pub is_pe_coff: bool,
     pub target_can_use_split_dwarf: bool,
     pub target_arch: String,
-    pub target_is_like_osx: bool,
+    pub target_is_like_darwin: bool,
     pub target_is_like_aix: bool,
     pub split_debuginfo: rustc_target::spec::SplitDebuginfo,
     pub split_dwarf_kind: rustc_session::config::SplitDwarfKind,
@@ -1216,7 +1216,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
         is_pe_coff: tcx.sess.target.is_like_windows,
         target_can_use_split_dwarf: tcx.sess.target_can_use_split_dwarf(),
         target_arch: tcx.sess.target.arch.to_string(),
-        target_is_like_osx: tcx.sess.target.is_like_osx,
+        target_is_like_darwin: tcx.sess.target.is_like_darwin,
         target_is_like_aix: tcx.sess.target.is_like_aix,
         split_debuginfo: tcx.sess.split_debuginfo(),
         split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind,
@@ -2186,7 +2186,7 @@ fn msvc_imps_needed(tcx: TyCtxt<'_>) -> bool {
     // indirectly from ThinLTO. In theory these are not needed as ThinLTO could resolve
     // these, but it currently does not do so.
     let can_have_static_objects =
-        tcx.sess.lto() == Lto::Thin || tcx.crate_types().iter().any(|ct| *ct == CrateType::Rlib);
+        tcx.sess.lto() == Lto::Thin || tcx.crate_types().contains(&CrateType::Rlib);
 
     tcx.sess.target.is_like_windows &&
     can_have_static_objects   &&
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index d16d4ed2283..ddb61188983 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -2,7 +2,7 @@ use std::str::FromStr;
 
 use rustc_abi::ExternAbi;
 use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
-use rustc_ast::{MetaItem, MetaItemInner, attr};
+use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr};
 use rustc_attr_parsing::ReprAttr::ReprAlign;
 use rustc_attr_parsing::{AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr};
 use rustc_data_structures::fx::FxHashMap;
@@ -213,7 +213,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                         // somewhat, and is subject to change in the future (which
                         // is a good thing, because this would ideally be a bit
                         // more firmed up).
-                        let is_like_elf = !(tcx.sess.target.is_like_osx
+                        let is_like_elf = !(tcx.sess.target.is_like_darwin
                             || tcx.sess.target.is_like_windows
                             || tcx.sess.target.is_like_wasm);
                         codegen_fn_attrs.flags |= if is_like_elf {
@@ -604,7 +604,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
     if let Some((name, _)) = lang_items::extract(attrs)
         && let Some(lang_item) = LangItem::from_name(name)
     {
-        if WEAK_LANG_ITEMS.iter().any(|&l| l == lang_item) {
+        if WEAK_LANG_ITEMS.contains(&lang_item) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
         }
         if let Some(link_name) = lang_item.link_name() {
@@ -805,8 +805,8 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
         return Some(AutoDiffAttrs::source());
     }
 
-    let [mode, input_activities @ .., ret_activity] = &list[..] else {
-        span_bug!(attr.span(), "rustc_autodiff attribute must contain mode and activities");
+    let [mode, width_meta, input_activities @ .., ret_activity] = &list[..] else {
+        span_bug!(attr.span(), "rustc_autodiff attribute must contain mode, width and activities");
     };
     let mode = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = mode {
         p1.segments.first().unwrap().ident
@@ -823,6 +823,30 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
         }
     };
 
+    let width: u32 = match width_meta {
+        MetaItemInner::MetaItem(MetaItem { path: p1, .. }) => {
+            let w = p1.segments.first().unwrap().ident;
+            match w.as_str().parse() {
+                Ok(val) => val,
+                Err(_) => {
+                    span_bug!(w.span, "rustc_autodiff width should fit u32");
+                }
+            }
+        }
+        MetaItemInner::Lit(lit) => {
+            if let LitKind::Int(val, _) = lit.kind {
+                match val.get().try_into() {
+                    Ok(val) => val,
+                    Err(_) => {
+                        span_bug!(lit.span, "rustc_autodiff width should fit u32");
+                    }
+                }
+            } else {
+                span_bug!(lit.span, "rustc_autodiff width should be an integer");
+            }
+        }
+    };
+
     // First read the ret symbol from the attribute
     let ret_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = ret_activity {
         p1.segments.first().unwrap().ident
@@ -860,7 +884,7 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
         }
     }
 
-    Some(AutoDiffAttrs { mode, ret_activity, input_activity: arg_activities })
+    Some(AutoDiffAttrs { mode, width, ret_activity, input_activity: arg_activities })
 }
 
 pub(crate) fn provide(providers: &mut Providers) {
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index f52d29baf9d..b33de8802d8 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -3,7 +3,6 @@
 use std::borrow::Cow;
 use std::ffi::OsString;
 use std::io::Error;
-use std::num::ParseIntError;
 use std::path::{Path, PathBuf};
 use std::process::ExitStatus;
 
@@ -739,14 +738,6 @@ pub enum ExtractBundledLibsError<'a> {
 }
 
 #[derive(Diagnostic)]
-pub(crate) enum AppleDeploymentTarget {
-    #[diag(codegen_ssa_apple_deployment_target_invalid)]
-    Invalid { env_var: &'static str, error: ParseIntError },
-    #[diag(codegen_ssa_apple_deployment_target_too_low)]
-    TooLow { env_var: &'static str, version: String, os_min: String },
-}
-
-#[derive(Diagnostic)]
 #[diag(codegen_ssa_read_file)]
 pub(crate) struct ReadFileError {
     pub message: std::io::Error,
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index d26d6edf314..67217927a87 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -7,7 +7,6 @@
 #![doc(rust_logo)]
 #![feature(assert_matches)]
 #![feature(box_patterns)]
-#![feature(debug_closure_helpers)]
 #![feature(file_buffered)]
 #![feature(if_let_guard)]
 #![feature(let_chains)]
@@ -34,7 +33,7 @@ use rustc_hir::CRATE_HIR_ID;
 use rustc_hir::def_id::CrateNum;
 use rustc_macros::{Decodable, Encodable, HashStable};
 use rustc_middle::dep_graph::WorkProduct;
-use rustc_middle::lint::LintLevelSource;
+use rustc_middle::lint::LevelAndSource;
 use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
 use rustc_middle::middle::dependency_format::Dependencies;
 use rustc_middle::middle::exported_symbols::SymbolExportKind;
@@ -45,7 +44,6 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 use rustc_session::Session;
 use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT};
 use rustc_session::cstore::{self, CrateSource};
-use rustc_session::lint::Level;
 use rustc_session::lint::builtin::LINKER_MESSAGES;
 use rustc_session::utils::NativeLibKind;
 use rustc_span::Symbol;
@@ -341,7 +339,7 @@ impl CodegenResults {
 /// Instead, encode exactly the information we need.
 #[derive(Copy, Clone, Debug, Encodable, Decodable)]
 pub struct CodegenLintLevels {
-    linker_messages: (Level, LintLevelSource),
+    linker_messages: LevelAndSource,
 }
 
 impl CodegenLintLevels {
diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index 90002d3f109..2b74c849f1a 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -335,7 +335,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
             self.tcx.dcx().span_bug(span, "tls access is checked in `Rvalue::ThreadLocalRef`");
         }
         if let Some(def_id) = def_id.as_local()
-            && let Err(guar) = self.tcx.at(span).check_well_formed(hir::OwnerId { def_id })
+            && let Err(guar) = self.tcx.ensure_ok().check_well_formed(hir::OwnerId { def_id })
         {
             self.error_emitted = Some(guar);
         }
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 496f6c86f71..61a7ec13511 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -546,7 +546,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
                         rustc_session::lint::builtin::LONG_RUNNING_CONST_EVAL,
                         hir_id,
                     )
-                    .0
+                    .level
                     .is_error();
                 let span = ecx.cur_span();
                 ecx.tcx.emit_node_span_lint(
diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
index 3776fb55c2e..34239ae1d15 100644
--- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs
+++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
@@ -256,7 +256,7 @@ pub(crate) fn eval_to_valtree<'tcx>(
         Err(err) => {
             let did = cid.instance.def_id();
             let global_const_id = cid.display(tcx);
-            let span = tcx.hir().span_if_local(did);
+            let span = tcx.hir_span_if_local(did);
             match err {
                 ValTreeCreationError::NodesOverflow => {
                     let handled =
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 8f286971e63..d077900587e 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -8,8 +8,9 @@
 
 use std::assert_matches::assert_matches;
 use std::borrow::{Borrow, Cow};
+use std::cell::Cell;
 use std::collections::VecDeque;
-use std::{fmt, mem, ptr};
+use std::{fmt, ptr};
 
 use rustc_abi::{Align, HasDataLayout, Size};
 use rustc_ast::Mutability;
@@ -131,7 +132,7 @@ pub struct Memory<'tcx, M: Machine<'tcx>> {
     /// This stores whether we are currently doing reads purely for the purpose of validation.
     /// Those reads do not trigger the machine's hooks for memory reads.
     /// Needless to say, this must only be set with great care!
-    validation_in_progress: bool,
+    validation_in_progress: Cell<bool>,
 }
 
 /// A reference to some allocation that was already bounds-checked for the given region
@@ -158,7 +159,7 @@ impl<'tcx, M: Machine<'tcx>> Memory<'tcx, M> {
             alloc_map: M::MemoryMap::default(),
             extra_fn_ptr_map: FxIndexMap::default(),
             dead_alloc_map: FxIndexMap::default(),
-            validation_in_progress: false,
+            validation_in_progress: Cell::new(false),
         }
     }
 
@@ -715,7 +716,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         // We want to call the hook on *all* accesses that involve an AllocId, including zero-sized
         // accesses. That means we cannot rely on the closure above or the `Some` branch below. We
         // do this after `check_and_deref_ptr` to ensure some basic sanity has already been checked.
-        if !self.memory.validation_in_progress {
+        if !self.memory.validation_in_progress.get() {
             if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(ptr, size_i64) {
                 M::before_alloc_read(self, alloc_id)?;
             }
@@ -723,7 +724,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
 
         if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc {
             let range = alloc_range(offset, size);
-            if !self.memory.validation_in_progress {
+            if !self.memory.validation_in_progress.get() {
                 M::before_memory_read(
                     self.tcx,
                     &self.machine,
@@ -801,7 +802,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     ) -> InterpResult<'tcx, Option<AllocRefMut<'a, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>>
     {
         let tcx = self.tcx;
-        let validation_in_progress = self.memory.validation_in_progress;
+        let validation_in_progress = self.memory.validation_in_progress.get();
 
         let size_i64 = i64::try_from(size.bytes()).unwrap(); // it would be an error to even ask for more than isize::MAX bytes
         let ptr_and_alloc = Self::check_and_deref_ptr(
@@ -1087,23 +1088,43 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     ///
     /// We do this so Miri's allocation access tracking does not show the validation
     /// reads as spurious accesses.
-    pub fn run_for_validation<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
+    pub fn run_for_validation_mut<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
         // This deliberately uses `==` on `bool` to follow the pattern
         // `assert!(val.replace(new) == old)`.
         assert!(
-            mem::replace(&mut self.memory.validation_in_progress, true) == false,
+            self.memory.validation_in_progress.replace(true) == false,
             "`validation_in_progress` was already set"
         );
         let res = f(self);
         assert!(
-            mem::replace(&mut self.memory.validation_in_progress, false) == true,
+            self.memory.validation_in_progress.replace(false) == true,
+            "`validation_in_progress` was unset by someone else"
+        );
+        res
+    }
+
+    /// Runs the closure in "validation" mode, which means the machine's memory read hooks will be
+    /// suppressed. Needless to say, this must only be set with great care! Cannot be nested.
+    ///
+    /// We do this so Miri's allocation access tracking does not show the validation
+    /// reads as spurious accesses.
+    pub fn run_for_validation_ref<R>(&self, f: impl FnOnce(&Self) -> R) -> R {
+        // This deliberately uses `==` on `bool` to follow the pattern
+        // `assert!(val.replace(new) == old)`.
+        assert!(
+            self.memory.validation_in_progress.replace(true) == false,
+            "`validation_in_progress` was already set"
+        );
+        let res = f(self);
+        assert!(
+            self.memory.validation_in_progress.replace(false) == true,
             "`validation_in_progress` was unset by someone else"
         );
         res
     }
 
     pub(super) fn validation_in_progress(&self) -> bool {
-        self.memory.validation_in_progress
+        self.memory.validation_in_progress.get()
     }
 }
 
@@ -1375,7 +1396,7 @@ 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, "we can't be copying during validation");
+        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
         // before the write hook -- the aliasing model cares about the order.
         M::before_memory_read(
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index eb3f552cd27..fb7ba6d7ef5 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -1322,7 +1322,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         trace!("validate_operand_internal: {:?}, {:?}", *val, val.layout.ty);
 
         // Run the visitor.
-        self.run_for_validation(|ecx| {
+        self.run_for_validation_mut(|ecx| {
             let reset_padding = reset_provenance_and_padding && {
                 // Check if `val` is actually stored in memory. If not, padding is not even
                 // represented and we need not reset it.
diff --git a/compiler/rustc_data_structures/src/fx.rs b/compiler/rustc_data_structures/src/fx.rs
index 80e72250470..f0db9623b67 100644
--- a/compiler/rustc_data_structures/src/fx.rs
+++ b/compiler/rustc_data_structures/src/fx.rs
@@ -9,6 +9,8 @@ pub type FxIndexSet<V> = indexmap::IndexSet<V, BuildHasherDefault<FxHasher>>;
 pub type IndexEntry<'a, K, V> = indexmap::map::Entry<'a, K, V>;
 pub type IndexOccupiedEntry<'a, K, V> = indexmap::map::OccupiedEntry<'a, K, V>;
 
+pub use indexmap::set::MutableValues;
+
 #[macro_export]
 macro_rules! define_id_collections {
     ($map_name:ident, $set_name:ident, $entry_name:ident, $key:ty) => {
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 37755e7d61d..595ac1edd28 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -34,7 +34,6 @@ use std::time::{Instant, SystemTime};
 use std::{env, str};
 
 use rustc_ast as ast;
-use rustc_codegen_ssa::back::apple;
 use rustc_codegen_ssa::traits::CodegenBackend;
 use rustc_codegen_ssa::{CodegenErrors, CodegenResults};
 use rustc_data_structures::profiling::{
@@ -64,6 +63,7 @@ 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_span::FileName;
+use rustc_span::def_id::LOCAL_CRATE;
 use rustc_target::json::ToJson;
 use rustc_target::spec::{Target, TargetTuple};
 use time::OffsetDateTime;
@@ -392,14 +392,10 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send))
 }
 
 fn dump_feature_usage_metrics(tcxt: TyCtxt<'_>, metrics_dir: &Path) {
-    let output_filenames = tcxt.output_filenames(());
-    let mut metrics_file_name = std::ffi::OsString::from("unstable_feature_usage_metrics-");
-    let mut metrics_path = output_filenames.with_directory_and_extension(metrics_dir, "json");
-    let metrics_file_stem =
-        metrics_path.file_name().expect("there should be a valid default output filename");
-    metrics_file_name.push(metrics_file_stem);
-    metrics_path.pop();
-    metrics_path.push(metrics_file_name);
+    let hash = tcxt.crate_hash(LOCAL_CRATE);
+    let crate_name = tcxt.crate_name(LOCAL_CRATE);
+    let metrics_file_name = format!("unstable_feature_usage_metrics-{crate_name}-{hash}.json");
+    let metrics_path = metrics_dir.join(metrics_file_name);
     if let Err(error) = tcxt.features().dump_feature_usage_metrics(metrics_path) {
         // FIXME(yaahc): once metrics can be enabled by default we will want "failure to emit
         // default metrics" to only produce a warning when metrics are enabled by default and emit
@@ -691,6 +687,34 @@ fn print_crate_info(
                 };
                 println_info!("{}", passes::get_crate_name(sess, attrs));
             }
+            CrateRootLintLevels => {
+                let Some(attrs) = attrs.as_ref() else {
+                    // no crate attributes, print out an error and exit
+                    return Compilation::Continue;
+                };
+                let crate_name = passes::get_crate_name(sess, attrs);
+                let lint_store = crate::unerased_lint_store(sess);
+                let registered_tools = rustc_resolve::registered_tools_ast(sess.dcx(), attrs);
+                let features = rustc_expand::config::features(sess, attrs, crate_name);
+                let lint_levels = rustc_lint::LintLevelsBuilder::crate_root(
+                    sess,
+                    &features,
+                    true,
+                    lint_store,
+                    &registered_tools,
+                    attrs,
+                );
+                for lint in lint_store.get_lints() {
+                    if let Some(feature_symbol) = lint.feature_gate
+                        && !features.enabled(feature_symbol)
+                    {
+                        // lint is unstable and feature gate isn't active, don't print
+                        continue;
+                    }
+                    let level = lint_levels.lint_level(lint).level;
+                    println_info!("{}={}", lint.name_lower(), level.as_str());
+                }
+            }
             Cfg => {
                 let mut cfgs = sess
                     .psess
@@ -779,11 +803,11 @@ fn print_crate_info(
                 }
             }
             DeploymentTarget => {
-                if sess.target.is_like_osx {
+                if sess.target.is_like_darwin {
                     println_info!(
                         "{}={}",
-                        apple::deployment_target_env_var(&sess.target.os),
-                        apple::pretty_version(apple::deployment_target(sess)),
+                        rustc_target::spec::apple::deployment_target_env_var(&sess.target.os),
+                        sess.apple_deployment_target().fmt_pretty(),
                     )
                 } else {
                     #[allow(rustc::diagnostic_outside_of_impl)]
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
index f0636b600b7..f3aeb8d224b 100644
--- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
+++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -91,13 +91,13 @@ fn annotation_level_for_level(level: Level) -> annotate_snippets::Level {
         Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => {
             annotate_snippets::Level::Error
         }
-        Level::ForceWarning(_) | Level::Warning => annotate_snippets::Level::Warning,
+        Level::ForceWarning | Level::Warning => annotate_snippets::Level::Warning,
         Level::Note | Level::OnceNote => annotate_snippets::Level::Note,
         Level::Help | Level::OnceHelp => annotate_snippets::Level::Help,
         // FIXME(#59346): Not sure how to map this level
         Level::FailureNote => annotate_snippets::Level::Error,
         Level::Allow => panic!("Should not call with Allow"),
-        Level::Expect(_) => panic!("Should not call with Expect"),
+        Level::Expect => panic!("Should not call with Expect"),
     }
 }
 
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 9f4d2ea5c1a..794502d7aae 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -9,7 +9,7 @@ use std::thread::panicking;
 
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_error_messages::{FluentValue, fluent_value_from_str_list_sep_by_and};
-use rustc_lint_defs::Applicability;
+use rustc_lint_defs::{Applicability, LintExpectationId};
 use rustc_macros::{Decodable, Encodable};
 use rustc_span::source_map::Spanned;
 use rustc_span::{DUMMY_SP, Span, Symbol};
@@ -296,6 +296,7 @@ pub struct DiagInner {
 
     pub messages: Vec<(DiagMessage, Style)>,
     pub code: Option<ErrCode>,
+    pub lint_id: Option<LintExpectationId>,
     pub span: MultiSpan,
     pub children: Vec<Subdiag>,
     pub suggestions: Suggestions,
@@ -324,6 +325,7 @@ impl DiagInner {
     pub fn new_with_messages(level: Level, messages: Vec<(DiagMessage, Style)>) -> Self {
         DiagInner {
             level,
+            lint_id: None,
             messages,
             code: None,
             span: MultiSpan::new(),
@@ -346,7 +348,7 @@ impl DiagInner {
         match self.level {
             Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => true,
 
-            Level::ForceWarning(_)
+            Level::ForceWarning
             | Level::Warning
             | Level::Note
             | Level::OnceNote
@@ -354,7 +356,7 @@ impl DiagInner {
             | Level::OnceHelp
             | Level::FailureNote
             | Level::Allow
-            | Level::Expect(_) => false,
+            | Level::Expect => false,
         }
     }
 
@@ -365,7 +367,7 @@ impl DiagInner {
 
     pub(crate) fn is_force_warn(&self) -> bool {
         match self.level {
-            Level::ForceWarning(_) => {
+            Level::ForceWarning => {
                 assert!(self.is_lint.is_some());
                 true
             }
@@ -1259,6 +1261,17 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
         self
     } }
 
+    with_fn! { with_lint_id,
+    /// Add an argument.
+    #[rustc_lint_diagnostics]
+    pub fn lint_id(
+        &mut self,
+        id: LintExpectationId,
+    ) -> &mut Self {
+        self.lint_id = Some(id);
+        self
+    } }
+
     with_fn! { with_primary_message,
     /// Add a primary message.
     #[rustc_lint_diagnostics]
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index 7d7f364fec2..a6583407b7e 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -144,7 +144,7 @@ impl Emitter for JsonEmitter {
                 //
                 // So to avoid ICEs and confused users we "upgrade" the lint level for
                 // those `FutureBreakageItem` to warn.
-                if matches!(diag.level, crate::Level::Allow | crate::Level::Expect(..)) {
+                if matches!(diag.level, crate::Level::Allow | crate::Level::Expect) {
                     diag.level = crate::Level::Warning;
                 }
                 FutureBreakageItem {
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 80e43ede445..f5f7618285e 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -905,8 +905,8 @@ impl<'a> DiagCtxtHandle<'a> {
             DelayedBug => {
                 return self.inner.borrow_mut().emit_diagnostic(diag, self.tainted_with_errors);
             }
-            ForceWarning(_) | Warning | Note | OnceNote | Help | OnceHelp | FailureNote | Allow
-            | Expect(_) => None,
+            ForceWarning | Warning | Note | OnceNote | Help | OnceHelp | FailureNote | Allow
+            | Expect => None,
         };
 
         // FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic
@@ -1045,7 +1045,7 @@ impl<'a> DiagCtxtHandle<'a> {
                 // Use `ForceWarning` rather than `Warning` to guarantee emission, e.g. with a
                 // configuration like `--cap-lints allow --force-warn bare_trait_objects`.
                 inner.emit_diagnostic(
-                    DiagInner::new(ForceWarning(None), DiagMessage::Str(warnings)),
+                    DiagInner::new(ForceWarning, DiagMessage::Str(warnings)),
                     None,
                 );
             }
@@ -1450,7 +1450,7 @@ impl<'a> DiagCtxtHandle<'a> {
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn struct_expect(self, msg: impl Into<DiagMessage>, id: LintExpectationId) -> Diag<'a, ()> {
-        Diag::new(self, Expect(id), msg)
+        Diag::new(self, Expect, msg).with_lint_id(id)
     }
 }
 
@@ -1510,7 +1510,7 @@ impl DiagCtxtInner {
             // Future breakages aren't emitted if they're `Level::Allow` or
             // `Level::Expect`, but they still need to be constructed and
             // stashed below, so they'll trigger the must_produce_diag check.
-            assert_matches!(diagnostic.level, Error | Warning | Allow | Expect(_));
+            assert_matches!(diagnostic.level, Error | Warning | Allow | Expect);
             self.future_breakage_diagnostics.push(diagnostic.clone());
         }
 
@@ -1558,7 +1558,7 @@ impl DiagCtxtInner {
                     };
                 }
             }
-            ForceWarning(None) => {} // `ForceWarning(Some(...))` is below, with `Expect`
+            ForceWarning if diagnostic.lint_id.is_none() => {} // `ForceWarning(Some(...))` is below, with `Expect`
             Warning => {
                 if !self.flags.can_emit_warnings {
                     // We are not emitting warnings.
@@ -1580,9 +1580,9 @@ impl DiagCtxtInner {
                 }
                 return None;
             }
-            Expect(expect_id) | ForceWarning(Some(expect_id)) => {
-                self.fulfilled_expectations.insert(expect_id);
-                if let Expect(_) = diagnostic.level {
+            Expect | ForceWarning => {
+                self.fulfilled_expectations.insert(diagnostic.lint_id.unwrap());
+                if let Expect = diagnostic.level {
                     // Nothing emitted here for expected lints.
                     TRACK_DIAGNOSTIC(diagnostic, &mut |_| None);
                     self.suppressed_expected_diag = true;
@@ -1631,7 +1631,7 @@ impl DiagCtxtInner {
 
                 if is_error {
                     self.deduplicated_err_count += 1;
-                } else if matches!(diagnostic.level, ForceWarning(_) | Warning) {
+                } else if matches!(diagnostic.level, ForceWarning | Warning) {
                     self.deduplicated_warn_count += 1;
                 }
                 self.has_printed = true;
@@ -1899,9 +1899,9 @@ pub enum Level {
     /// A `force-warn` lint warning about the code being compiled. Does not prevent compilation
     /// from finishing.
     ///
-    /// The [`LintExpectationId`] is used for expected lint diagnostics. In all other cases this
+    /// Requires a [`LintExpectationId`] for expected lint diagnostics. In all other cases this
     /// should be `None`.
-    ForceWarning(Option<LintExpectationId>),
+    ForceWarning,
 
     /// A warning about the code being compiled. Does not prevent compilation from finishing.
     /// Will be skipped if `can_emit_warnings` is false.
@@ -1926,8 +1926,8 @@ pub enum Level {
     /// Only used for lints.
     Allow,
 
-    /// Only used for lints.
-    Expect(LintExpectationId),
+    /// Only used for lints. Requires a [`LintExpectationId`] for silencing the lints.
+    Expect,
 }
 
 impl fmt::Display for Level {
@@ -1943,7 +1943,7 @@ impl Level {
             Bug | Fatal | Error | DelayedBug => {
                 spec.set_fg(Some(Color::Red)).set_intense(true);
             }
-            ForceWarning(_) | Warning => {
+            ForceWarning | Warning => {
                 spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows));
             }
             Note | OnceNote => {
@@ -1953,7 +1953,7 @@ impl Level {
                 spec.set_fg(Some(Color::Cyan)).set_intense(true);
             }
             FailureNote => {}
-            Allow | Expect(_) => unreachable!(),
+            Allow | Expect => unreachable!(),
         }
         spec
     }
@@ -1962,11 +1962,11 @@ impl Level {
         match self {
             Bug | DelayedBug => "error: internal compiler error",
             Fatal | Error => "error",
-            ForceWarning(_) | Warning => "warning",
+            ForceWarning | Warning => "warning",
             Note | OnceNote => "note",
             Help | OnceHelp => "help",
             FailureNote => "failure-note",
-            Allow | Expect(_) => unreachable!(),
+            Allow | Expect => unreachable!(),
         }
     }
 
@@ -1977,8 +1977,7 @@ impl Level {
     // Can this level be used in a subdiagnostic message?
     fn can_be_subdiag(&self) -> bool {
         match self {
-            Bug | DelayedBug | Fatal | Error | ForceWarning(_) | FailureNote | Allow
-            | Expect(_) => false,
+            Bug | DelayedBug | Fatal | Error | ForceWarning | FailureNote | Allow | Expect => false,
 
             Warning | Note | Help | OnceNote | OnceHelp => true,
         }
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 990d0f2e028..d14e476ba32 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -1424,12 +1424,11 @@ pub fn parse_macro_name_and_helper_attrs(
 /// See #73345 and #83125 for more details.
 /// FIXME(#73933): Remove this eventually.
 fn pretty_printing_compatibility_hack(item: &Item, psess: &ParseSess) {
-    let name = item.ident.name;
-    if name == sym::ProceduralMasqueradeDummyType
-        && let ast::ItemKind::Enum(enum_def, _) = &item.kind
+    if let ast::ItemKind::Enum(ident, enum_def, _) = &item.kind
+        && ident.name == sym::ProceduralMasqueradeDummyType
         && let [variant] = &*enum_def.variants
         && variant.ident.name == sym::Input
-        && let FileName::Real(real) = psess.source_map().span_to_filename(item.ident.span)
+        && let FileName::Real(real) = psess.source_map().span_to_filename(ident.span)
         && let Some(c) = real
             .local_path()
             .unwrap_or(Path::new(""))
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index 89a750bb39f..f68172c1f67 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -662,15 +662,8 @@ impl<'a> ExtCtxt<'a> {
         P(ast::FnDecl { inputs, output })
     }
 
-    pub fn item(
-        &self,
-        span: Span,
-        name: Ident,
-        attrs: ast::AttrVec,
-        kind: ast::ItemKind,
-    ) -> P<ast::Item> {
+    pub fn item(&self, span: Span, attrs: ast::AttrVec, kind: ast::ItemKind) -> P<ast::Item> {
         P(ast::Item {
-            ident: name,
             attrs,
             id: ast::DUMMY_NODE_ID,
             kind,
@@ -687,17 +680,17 @@ impl<'a> ExtCtxt<'a> {
     pub fn item_static(
         &self,
         span: Span,
-        name: Ident,
+        ident: Ident,
         ty: P<ast::Ty>,
         mutability: ast::Mutability,
         expr: P<ast::Expr>,
     ) -> P<ast::Item> {
         self.item(
             span,
-            name,
             AttrVec::new(),
             ast::ItemKind::Static(
                 ast::StaticItem {
+                    ident,
                     ty,
                     safety: ast::Safety::Default,
                     mutability,
@@ -712,18 +705,18 @@ impl<'a> ExtCtxt<'a> {
     pub fn item_const(
         &self,
         span: Span,
-        name: Ident,
+        ident: Ident,
         ty: P<ast::Ty>,
         expr: P<ast::Expr>,
     ) -> P<ast::Item> {
         let defaultness = ast::Defaultness::Final;
         self.item(
             span,
-            name,
             AttrVec::new(),
             ast::ItemKind::Const(
                 ast::ConstItem {
                     defaultness,
+                    ident,
                     // FIXME(generic_const_items): Pass the generics as a parameter.
                     generics: ast::Generics::default(),
                     ty,
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 22da1179feb..bca846d2ec4 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -744,6 +744,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                                     item_inner.kind,
                                     ItemKind::Mod(
                                         _,
+                                        _,
                                         ModKind::Unloaded | ModKind::Loaded(_, Inline::No, _, _),
                                     )
                                 ) =>
@@ -911,7 +912,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         impl<'ast, 'a> Visitor<'ast> for GateProcMacroInput<'a> {
             fn visit_item(&mut self, item: &'ast ast::Item) {
                 match &item.kind {
-                    ItemKind::Mod(_, mod_kind)
+                    ItemKind::Mod(_, _, mod_kind)
                         if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _, _)) =>
                     {
                         feature_err(
@@ -1221,9 +1222,8 @@ impl InvocationCollectorNode for P<ast::Item> {
         }
 
         // Work around borrow checker not seeing through `P`'s deref.
-        let (ident, span, mut attrs) = (node.ident, node.span, mem::take(&mut node.attrs));
-        let ItemKind::Mod(_, mod_kind) = &mut node.kind else { unreachable!() };
-
+        let (span, mut attrs) = (node.span, mem::take(&mut node.attrs));
+        let ItemKind::Mod(_, ident, ref mut mod_kind) = node.kind else { unreachable!() };
         let ecx = &mut collector.cx;
         let (file_path, dir_path, dir_ownership) = match mod_kind {
             ModKind::Loaded(_, inline, _, _) => {
@@ -1305,6 +1305,7 @@ impl InvocationCollectorNode for P<ast::Item> {
         collector.cx.current_expansion.module = orig_module;
         res
     }
+
     fn declared_names(&self) -> Vec<Ident> {
         if let ItemKind::Use(ut) = &self.kind {
             fn collect_use_tree_leaves(ut: &ast::UseTree, idents: &mut Vec<Ident>) {
@@ -1324,7 +1325,7 @@ impl InvocationCollectorNode for P<ast::Item> {
             return idents;
         }
 
-        vec![self.ident]
+        if let Some(ident) = self.kind.ident() { vec![ident] } else { vec![] }
     }
 }
 
@@ -1844,11 +1845,11 @@ fn build_single_delegations<'a, Node: InvocationCollectorNode>(
             id: ast::DUMMY_NODE_ID,
             span: if from_glob { item_span } else { ident.span },
             vis: item.vis.clone(),
-            ident: rename.unwrap_or(ident),
             kind: Node::delegation_item_kind(Box::new(ast::Delegation {
                 id: ast::DUMMY_NODE_ID,
                 qself: deleg.qself.clone(),
                 path,
+                ident: rename.unwrap_or(ident),
                 rename,
                 body: deleg.body.clone(),
                 from_glob,
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs
index cffa4af6ac3..6e47ed6eb67 100644
--- a/compiler/rustc_expand/src/mbe/transcribe.rs
+++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -3,11 +3,10 @@ use std::sync::Arc;
 
 use rustc_ast::mut_visit::{self, MutVisitor};
 use rustc_ast::token::{
-    self, Delimiter, IdentIsRaw, InvisibleOrigin, Lit, LitKind, MetaVarKind, Nonterminal, Token,
-    TokenKind,
+    self, Delimiter, IdentIsRaw, InvisibleOrigin, Lit, LitKind, MetaVarKind, Token, TokenKind,
 };
 use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
-use rustc_ast::{ExprKind, StmtKind, TyKind};
+use rustc_ast::{ExprKind, StmtKind, TyKind, UnOp};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{Diag, DiagCtxtHandle, PResult, pluralize};
 use rustc_parse::lexer::nfc_normalize;
@@ -340,6 +339,30 @@ pub(super) fn transcribe<'a>(
                             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(
@@ -869,10 +892,8 @@ fn extract_symbol_from_pnr<'a>(
             },
             _,
         )) => Ok(*symbol),
-        ParseNtResult::Nt(nt)
-            if let Nonterminal::NtLiteral(expr) = &**nt
-                && let ExprKind::Lit(Lit { kind: LitKind::Str, symbol, suffix: None }) =
-                    &expr.kind =>
+        ParseNtResult::Literal(expr)
+            if let ExprKind::Lit(Lit { kind: LitKind::Str, symbol, suffix: None }) = &expr.kind =>
         {
             Ok(*symbol)
         }
diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs
index a60a87244cc..0136292decb 100644
--- a/compiler/rustc_expand/src/placeholders.rs
+++ b/compiler/rustc_expand/src/placeholders.rs
@@ -26,7 +26,7 @@ pub(crate) fn placeholder(
         })
     }
 
-    let ident = Ident::empty();
+    let ident = Ident::dummy();
     let attrs = ast::AttrVec::new();
     let vis = vis.unwrap_or(ast::Visibility {
         span: DUMMY_SP,
@@ -62,7 +62,6 @@ pub(crate) fn placeholder(
         AstFragmentKind::Items => AstFragment::Items(smallvec![P(ast::Item {
             id,
             span,
-            ident,
             vis,
             attrs,
             kind: ast::ItemKind::MacCall(mac_placeholder()),
@@ -71,7 +70,6 @@ pub(crate) fn placeholder(
         AstFragmentKind::TraitItems => AstFragment::TraitItems(smallvec![P(ast::AssocItem {
             id,
             span,
-            ident,
             vis,
             attrs,
             kind: ast::AssocItemKind::MacCall(mac_placeholder()),
@@ -80,7 +78,6 @@ pub(crate) fn placeholder(
         AstFragmentKind::ImplItems => AstFragment::ImplItems(smallvec![P(ast::AssocItem {
             id,
             span,
-            ident,
             vis,
             attrs,
             kind: ast::AssocItemKind::MacCall(mac_placeholder()),
@@ -90,7 +87,6 @@ pub(crate) fn placeholder(
             AstFragment::TraitImplItems(smallvec![P(ast::AssocItem {
                 id,
                 span,
-                ident,
                 vis,
                 attrs,
                 kind: ast::AssocItemKind::MacCall(mac_placeholder()),
@@ -101,7 +97,6 @@ pub(crate) fn placeholder(
             AstFragment::ForeignItems(smallvec![P(ast::ForeignItem {
                 id,
                 span,
-                ident,
                 vis,
                 attrs,
                 kind: ast::ForeignItemKind::MacCall(mac_placeholder()),
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 6fe65c88f71..1e33e2e9393 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -1087,9 +1087,9 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         WarnFollowing, EncodeCrossCrate::No
     ),
     rustc_attr!(
-        TEST, rustc_error, Normal,
-        template!(Word, List: "delayed_bug_from_inside_query"),
-        WarnFollowingWordOnly, EncodeCrossCrate::Yes
+        TEST, rustc_delayed_bug_from_inside_query, Normal,
+        template!(Word),
+        WarnFollowing, EncodeCrossCrate::No
     ),
     rustc_attr!(
         TEST, rustc_dump_user_args, Normal, template!(Word),
diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs
index 47e4f9a2fe8..6ba7b22a0df 100644
--- a/compiler/rustc_feature/src/removed.rs
+++ b/compiler/rustc_feature/src/removed.rs
@@ -142,6 +142,9 @@ declare_features! (
     /// Allows inferring `'static` outlives requirements (RFC 2093).
     (removed, infer_static_outlives_requirements, "1.63.0", Some(54185),
      Some("removed as it caused some confusion and discussion was inactive for years")),
+    /// Allow anonymous constants from an inline `const` block in pattern position
+    (removed, inline_const_pat, "CURRENT_RUSTC_VERSION", Some(76001),
+     Some("removed due to implementation concerns as it requires significant refactorings")),
     /// Lazily evaluate constants. This allows constants to depend on type parameters.
     (removed, lazy_normalization_consts, "1.46.0", Some(72219), Some("superseded by `generic_const_exprs`")),
     /// Changes `impl Trait` to capture all lifetimes in scope.
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 72468dd4714..710e129b609 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -324,6 +324,7 @@ declare_features! (
     (unstable, loongarch_target_feature, "1.73.0", Some(44839)),
     (unstable, m68k_target_feature, "1.85.0", Some(134328)),
     (unstable, mips_target_feature, "1.27.0", Some(44839)),
+    (unstable, movrs_target_feature, "CURRENT_RUSTC_VERSION", Some(137976)),
     (unstable, powerpc_target_feature, "1.27.0", Some(44839)),
     (unstable, prfchw_target_feature, "1.78.0", Some(44839)),
     (unstable, riscv_target_feature, "1.45.0", Some(44839)),
@@ -531,8 +532,6 @@ declare_features! (
     (unstable, import_trait_associated_functions, "1.86.0", Some(134691)),
     /// Allows associated types in inherent impls.
     (incomplete, inherent_associated_types, "1.52.0", Some(8995)),
-    /// Allow anonymous constants from an inline `const` block in pattern position
-    (unstable, inline_const_pat, "1.58.0", Some(76001)),
     /// Allows using `pointer` and `reference` in intra-doc links
     (unstable, intra_doc_pointers, "1.51.0", Some(80896)),
     // Allows using the `kl` and `widekl` target features and the associated intrinsics
@@ -630,6 +629,8 @@ declare_features! (
     (unstable, strict_provenance_lints, "1.61.0", Some(130351)),
     /// Allows string patterns to dereference values to match them.
     (unstable, string_deref_patterns, "1.67.0", Some(87121)),
+    /// Allows `super let` statements.
+    (incomplete, super_let, "CURRENT_RUSTC_VERSION", Some(139076)),
     /// Allows subtrait items to shadow supertrait items.
     (unstable, supertrait_item_shadowing, "1.86.0", Some(89151)),
     /// Allows using `#[thread_local]` on `static` items.
diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs
index 5f8941d4754..dc00b52a593 100644
--- a/compiler/rustc_hir/src/def.rs
+++ b/compiler/rustc_hir/src/def.rs
@@ -294,7 +294,7 @@ impl DefKind {
             DefKind::GlobalAsm => DefPathData::GlobalAsm,
             DefKind::Impl { .. } => DefPathData::Impl,
             DefKind::Closure => DefPathData::Closure,
-            DefKind::SyntheticCoroutineBody => DefPathData::Closure,
+            DefKind::SyntheticCoroutineBody => DefPathData::SyntheticCoroutineBody,
         }
     }
 
diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs
index 61f5efd9978..c52954aa96f 100644
--- a/compiler/rustc_hir/src/definitions.rs
+++ b/compiler/rustc_hir/src/definitions.rs
@@ -291,6 +291,8 @@ pub enum DefPathData {
     /// An existential `impl Trait` type node.
     /// Argument position `impl Trait` have a `TypeNs` with their pretty-printed name.
     OpaqueTy,
+    /// A synthetic body for a coroutine's by-move body.
+    SyntheticCoroutineBody,
 }
 
 impl Definitions {
@@ -415,8 +417,16 @@ impl DefPathData {
 
             ValueNs(name) | MacroNs(name) | LifetimeNs(name) => Some(name),
 
-            Impl | ForeignMod | CrateRoot | Use | GlobalAsm | Closure | Ctor | AnonConst
-            | OpaqueTy => None,
+            Impl
+            | ForeignMod
+            | CrateRoot
+            | Use
+            | GlobalAsm
+            | Closure
+            | Ctor
+            | AnonConst
+            | OpaqueTy
+            | SyntheticCoroutineBody => None,
         }
     }
 
@@ -441,6 +451,7 @@ impl DefPathData {
             Ctor => DefPathDataName::Anon { namespace: sym::constructor },
             AnonConst => DefPathDataName::Anon { namespace: sym::constant },
             OpaqueTy => DefPathDataName::Anon { namespace: sym::opaque },
+            SyntheticCoroutineBody => DefPathDataName::Anon { namespace: sym::synthetic },
         }
     }
 }
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index e3e96894ed1..1a6c15b66a4 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -10,9 +10,9 @@ use rustc_ast::{
     LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind,
 };
 pub use rustc_ast::{
-    AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity,
-    ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto, MetaItemInner, MetaItemLit, Movability,
-    Mutability, UnOp,
+    AssignOp, AssignOpKind, AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind,
+    BoundConstness, BoundPolarity, ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto,
+    MetaItemInner, MetaItemLit, Movability, Mutability, UnOp,
 };
 use rustc_attr_data_structures::AttributeKind;
 use rustc_data_structures::fingerprint::Fingerprint;
@@ -2123,7 +2123,7 @@ pub type Lit = Spanned<LitKind>;
 /// explicit discriminant values for enum variants.
 ///
 /// You can check if this anon const is a default in a const param
-/// `const N: usize = { ... }` with `tcx.hir().opt_const_param_default_param_def_id(..)`
+/// `const N: usize = { ... }` with `tcx.hir_opt_const_param_default_param_def_id(..)`
 #[derive(Copy, Clone, Debug, HashStable_Generic)]
 pub struct AnonConst {
     #[stable_hasher(ignore)]
@@ -2648,7 +2648,7 @@ pub enum ExprKind<'hir> {
     /// An assignment with an operator.
     ///
     /// E.g., `a += 1`.
-    AssignOp(BinOp, &'hir Expr<'hir>, &'hir Expr<'hir>),
+    AssignOp(AssignOp, &'hir Expr<'hir>, &'hir Expr<'hir>),
     /// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct or tuple field.
     Field(&'hir Expr<'hir>, Ident),
     /// An indexing operation (`foo[2]`).
diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir/src/hir_id.rs
index 3fa06620ea8..b48a081d371 100644
--- a/compiler/rustc_hir/src/hir_id.rs
+++ b/compiler/rustc_hir/src/hir_id.rs
@@ -83,6 +83,12 @@ pub struct HirId {
     pub local_id: ItemLocalId,
 }
 
+// To ensure correctness of incremental compilation,
+// `HirId` must not implement `Ord` or `PartialOrd`.
+// See https://github.com/rust-lang/rust/issues/90317.
+impl !Ord for HirId {}
+impl !PartialOrd for HirId {}
+
 impl Debug for HirId {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         // Example: HirId(DefId(0:1 ~ aa[7697]::{use#0}).10)
@@ -116,10 +122,6 @@ impl HirId {
     pub fn make_owner(owner: LocalDefId) -> Self {
         Self { owner: OwnerId { def_id: owner }, local_id: ItemLocalId::ZERO }
     }
-
-    pub fn index(self) -> (usize, usize) {
-        (rustc_index::Idx::index(self.owner.def_id), rustc_index::Idx::index(self.local_id))
-    }
 }
 
 impl fmt::Display for HirId {
@@ -128,18 +130,6 @@ impl fmt::Display for HirId {
     }
 }
 
-impl Ord for HirId {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        (self.index()).cmp(&(other.index()))
-    }
-}
-
-impl PartialOrd for HirId {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
 rustc_data_structures::define_stable_id_collections!(HirIdMap, HirIdSet, HirIdMapEntry, HirId);
 rustc_data_structures::define_id_collections!(
     ItemLocalMap,
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index e625514e9ff..90fab01ba2d 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -433,6 +433,12 @@ language_item_table! {
     // Experimental lang items for implementing contract pre- and post-condition checking.
     ContractBuildCheckEnsures, sym::contract_build_check_ensures, contract_build_check_ensures_fn, Target::Fn, GenericRequirement::None;
     ContractCheckRequires,     sym::contract_check_requires,      contract_check_requires_fn,      Target::Fn, GenericRequirement::None;
+
+    // Experimental lang items for `MCP: Low level components for async drop`(https://github.com/rust-lang/compiler-team/issues/727)
+    DefaultTrait4,           sym::default_trait4,      default_trait4_trait,       Target::Trait,          GenericRequirement::None;
+    DefaultTrait3,           sym::default_trait3,      default_trait3_trait,       Target::Trait,          GenericRequirement::None;
+    DefaultTrait2,           sym::default_trait2,      default_trait2_trait,       Target::Trait,          GenericRequirement::None;
+    DefaultTrait1,           sym::default_trait1,      default_trait1_trait,       Target::Trait,          GenericRequirement::None;
 }
 
 /// The requirement imposed on the generics of a lang item
diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs
index 4a839d40571..6bc0f797cca 100644
--- a/compiler/rustc_hir/src/lib.rs
+++ b/compiler/rustc_hir/src/lib.rs
@@ -11,6 +11,7 @@
 #![feature(debug_closure_helpers)]
 #![feature(exhaustive_patterns)]
 #![feature(let_chains)]
+#![feature(negative_impls)]
 #![feature(never_type)]
 #![feature(rustc_attrs)]
 #![feature(variant_count)]
diff --git a/compiler/rustc_hir_analysis/src/check/always_applicable.rs b/compiler/rustc_hir_analysis/src/check/always_applicable.rs
index 8a841a11556..58c3020f60e 100644
--- a/compiler/rustc_hir_analysis/src/check/always_applicable.rs
+++ b/compiler/rustc_hir_analysis/src/check/always_applicable.rs
@@ -36,10 +36,8 @@ use crate::hir::def_id::{DefId, LocalDefId};
 ///    cannot do `struct S<T>; impl<T:Clone> Drop for S<T> { ... }`).
 pub(crate) fn check_drop_impl(
     tcx: TyCtxt<'_>,
-    drop_impl_did: DefId,
+    drop_impl_did: LocalDefId,
 ) -> Result<(), ErrorGuaranteed> {
-    let drop_impl_did = drop_impl_did.expect_local();
-
     match tcx.impl_polarity(drop_impl_did) {
         ty::ImplPolarity::Positive => {}
         ty::ImplPolarity::Negative => {
@@ -56,9 +54,9 @@ pub(crate) fn check_drop_impl(
 
     tcx.ensure_ok().orphan_check_impl(drop_impl_did)?;
 
-    let dtor_impl_trait_ref = tcx.impl_trait_ref(drop_impl_did).unwrap().instantiate_identity();
+    let self_ty = tcx.type_of(drop_impl_did).instantiate_identity();
 
-    match dtor_impl_trait_ref.self_ty().kind() {
+    match self_ty.kind() {
         ty::Adt(adt_def, adt_to_impl_args) => {
             ensure_impl_params_and_item_params_correspond(
                 tcx,
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 07b5837bd87..a0798656763 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -578,10 +578,8 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe
                 }
             }
             _ => {
-                tcx.dcx().span_delayed_bug(
-                    tcx.hir().span(hir_id),
-                    "parameter should have been resolved",
-                );
+                tcx.dcx()
+                    .span_delayed_bug(tcx.hir_span(hir_id), "parameter should have been resolved");
             }
         }
     }
@@ -1049,7 +1047,7 @@ fn check_impl_items_against_trait<'tcx>(
                 leaf_def.as_ref().is_some_and(|node_item| !node_item.defining_node.is_from_trait());
 
             if !is_implemented_here {
-                let full_impl_span = tcx.hir().span_with_body(tcx.local_def_id_to_hir_id(impl_id));
+                let full_impl_span = tcx.hir_span_with_body(tcx.local_def_id_to_hir_id(impl_id));
                 match tcx.eval_default_body_stability(trait_item_id, full_impl_span) {
                     EvalResult::Deny { feature, reason, issue, .. } => default_body_is_unstable(
                         tcx,
@@ -1105,7 +1103,7 @@ fn check_impl_items_against_trait<'tcx>(
         }
 
         if !missing_items.is_empty() {
-            let full_impl_span = tcx.hir().span_with_body(tcx.local_def_id_to_hir_id(impl_id));
+            let full_impl_span = tcx.hir_span_with_body(tcx.local_def_id_to_hir_id(impl_id));
             missing_items_err(tcx, impl_id, &missing_items, full_impl_span);
         }
 
@@ -1321,7 +1319,7 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
         let typing_env = ty::TypingEnv::non_body_analysis(tcx, field.did);
         let layout = tcx.layout_of(typing_env.as_query_input(ty));
         // We are currently checking the type this field came from, so it must be local
-        let span = tcx.hir().span_if_local(field.did).unwrap();
+        let span = tcx.hir_span_if_local(field.did).unwrap();
         let trivial = layout.is_ok_and(|layout| layout.is_1zst());
         if !trivial {
             return (span, trivial, None);
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 84d07c711fa..5e68bb31001 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -12,7 +12,6 @@ use rustc_hir::{self as hir, AmbigArg, GenericParamKind, ImplItemKind, intravisi
 use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
 use rustc_infer::traits::util;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
-use rustc_middle::ty::util::ExplicitSelf;
 use rustc_middle::ty::{
     self, BottomUpFolder, GenericArgs, GenericParamDefKind, Ty, TyCtxt, TypeFoldable, TypeFolder,
     TypeSuperFoldable, TypeVisitableExt, TypingMode, Upcast,
@@ -995,6 +994,26 @@ impl<'tcx> ty::FallibleTypeFolder<TyCtxt<'tcx>> for RemapHiddenTyRegions<'tcx> {
     }
 }
 
+/// Gets the string for an explicit self declaration, e.g. "self", "&self",
+/// etc.
+fn get_self_string<'tcx, P>(self_arg_ty: Ty<'tcx>, is_self_ty: P) -> String
+where
+    P: Fn(Ty<'tcx>) -> bool,
+{
+    if is_self_ty(self_arg_ty) {
+        "self".to_owned()
+    } else if let ty::Ref(_, ty, mutbl) = self_arg_ty.kind()
+        && is_self_ty(*ty)
+    {
+        match mutbl {
+            hir::Mutability::Not => "&self".to_owned(),
+            hir::Mutability::Mut => "&mut self".to_owned(),
+        }
+    } else {
+        format!("self: {self_arg_ty}")
+    }
+}
+
 fn report_trait_method_mismatch<'tcx>(
     infcx: &InferCtxt<'tcx>,
     mut cause: ObligationCause<'tcx>,
@@ -1020,12 +1039,7 @@ fn report_trait_method_mismatch<'tcx>(
             if trait_m.fn_has_self_parameter =>
         {
             let ty = trait_sig.inputs()[0];
-            let sugg = match ExplicitSelf::determine(ty, |ty| ty == impl_trait_ref.self_ty()) {
-                ExplicitSelf::ByValue => "self".to_owned(),
-                ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(),
-                ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(),
-                _ => format!("self: {ty}"),
-            };
+            let sugg = get_self_string(ty, |ty| ty == impl_trait_ref.self_ty());
 
             // When the `impl` receiver is an arbitrary self type, like `self: Box<Self>`, the
             // span points only at the type `Box<Self`>, but we want to cover the whole
@@ -1208,7 +1222,7 @@ fn extract_spans_for_error_reporting<'tcx>(
         TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(ExpectedFound { .. }, i) => {
             (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i)))
         }
-        _ => (cause.span, tcx.hir().span_if_local(trait_m.def_id)),
+        _ => (cause.span, tcx.hir_span_if_local(trait_m.def_id)),
     }
 }
 
@@ -1238,12 +1252,7 @@ fn compare_self_type<'tcx>(
             .build_with_typing_env(ty::TypingEnv::non_body_analysis(tcx, method.def_id));
         let self_arg_ty = tcx.liberate_late_bound_regions(method.def_id, self_arg_ty);
         let can_eq_self = |ty| infcx.can_eq(param_env, untransformed_self_ty, ty);
-        match ExplicitSelf::determine(self_arg_ty, can_eq_self) {
-            ExplicitSelf::ByValue => "self".to_owned(),
-            ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(),
-            ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(),
-            _ => format!("self: {self_arg_ty}"),
-        }
+        get_self_string(self_arg_ty, can_eq_self)
     };
 
     match (trait_m.fn_has_self_parameter, impl_m.fn_has_self_parameter) {
@@ -1261,7 +1270,7 @@ fn compare_self_type<'tcx>(
                 self_descr
             );
             err.span_label(impl_m_span, format!("`{self_descr}` used in impl"));
-            if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) {
+            if let Some(span) = tcx.hir_span_if_local(trait_m.def_id) {
                 err.span_label(span, format!("trait method declared without `{self_descr}`"));
             } else {
                 err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
@@ -1281,7 +1290,7 @@ fn compare_self_type<'tcx>(
                 self_descr
             );
             err.span_label(impl_m_span, format!("expected `{self_descr}` in impl"));
-            if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) {
+            if let Some(span) = tcx.hir_span_if_local(trait_m.def_id) {
                 err.span_label(span, format!("`{self_descr}` used in trait"));
             } else {
                 err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
@@ -1389,7 +1398,7 @@ fn compare_number_of_generics<'tcx>(
                     .collect();
                 (Some(arg_spans), impl_trait_spans)
             } else {
-                let trait_span = tcx.hir().span_if_local(trait_.def_id);
+                let trait_span = tcx.hir_span_if_local(trait_.def_id);
                 (trait_span.map(|s| vec![s]), vec![])
             };
 
@@ -1481,7 +1490,7 @@ fn compare_number_of_method_arguments<'tcx>(
                     }
                 })
             })
-            .or_else(|| tcx.hir().span_if_local(trait_m.def_id));
+            .or_else(|| tcx.hir_span_if_local(trait_m.def_id));
 
         let (impl_m_sig, _) = &tcx.hir_expect_impl_item(impl_m.def_id.expect_local()).expect_fn();
         let pos = impl_number_args.saturating_sub(1);
@@ -2366,7 +2375,7 @@ fn try_report_async_mismatch<'tcx>(
             return Err(tcx.sess.dcx().emit_err(MethodShouldReturnFuture {
                 span: tcx.def_span(impl_m.def_id),
                 method_name: tcx.item_ident(impl_m.def_id),
-                trait_item_span: tcx.hir().span_if_local(trait_m.def_id),
+                trait_item_span: tcx.hir_span_if_local(trait_m.def_id),
             }));
         }
     }
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
index d63165f0f16..32a582aadc1 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
@@ -29,6 +29,7 @@ enum NonAsmTypeReason<'tcx> {
     Invalid(Ty<'tcx>),
     InvalidElement(DefId, Ty<'tcx>),
     NotSizedPtr(Ty<'tcx>),
+    EmptySIMDArray(Ty<'tcx>),
 }
 
 impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
@@ -102,6 +103,9 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
             }
             ty::Adt(adt, args) if adt.repr().simd() => {
                 let fields = &adt.non_enum_variant().fields;
+                if fields.is_empty() {
+                    return Err(NonAsmTypeReason::EmptySIMDArray(ty));
+                }
                 let field = &fields[FieldIdx::ZERO];
                 let elem_ty = field.ty(self.tcx(), args);
 
@@ -226,6 +230,10 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                             can be used as arguments for inline assembly",
                         ).emit();
                     }
+                    NonAsmTypeReason::EmptySIMDArray(ty) => {
+                        let msg = format!("use of empty SIMD vector `{ty}`");
+                        self.infcx.dcx().struct_span_err(expr.span, msg).emit();
+                    }
                 }
                 return None;
             }
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index d8ae4214527..7c5d7b33a34 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -114,11 +114,11 @@ pub fn provide(providers: &mut Providers) {
 }
 
 fn adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::Destructor> {
-    tcx.calculate_dtor(def_id.to_def_id(), always_applicable::check_drop_impl)
+    tcx.calculate_dtor(def_id, always_applicable::check_drop_impl)
 }
 
 fn adt_async_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::AsyncDestructor> {
-    tcx.calculate_async_dtor(def_id.to_def_id(), always_applicable::check_drop_impl)
+    tcx.calculate_async_dtor(def_id, always_applicable::check_drop_impl)
 }
 
 /// Given a `DefId` for an opaque type in return position, find its parent item's return
@@ -243,7 +243,7 @@ fn missing_items_err(
             tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(),
         ));
         let code = format!("{padding}{snippet}\n{padding}");
-        if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) {
+        if let Some(span) = tcx.hir_span_if_local(trait_item.def_id) {
             missing_trait_item_label
                 .push(errors::MissingTraitItemLabel { span, item: trait_item.name });
             missing_trait_item.push(errors::MissingTraitItemSuggestion {
@@ -341,9 +341,8 @@ fn bounds_from_generic_predicates<'tcx>(
             ty::ClauseKind::Trait(trait_predicate) => {
                 let entry = types.entry(trait_predicate.self_ty()).or_default();
                 let def_id = trait_predicate.def_id();
-                if Some(def_id) != tcx.lang_items().sized_trait() {
-                    // Type params are `Sized` by default, do not add that restriction to the list
-                    // if it is a positive requirement.
+                if !tcx.is_default_trait(def_id) {
+                    // Do not add that restriction to the list if it is a positive requirement.
                     entry.push(trait_predicate.def_id());
                 }
             }
@@ -534,7 +533,7 @@ fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>, sp: Span, d
     let variant_spans: Vec<_> = adt
         .variants()
         .iter()
-        .map(|variant| tcx.hir().span_if_local(variant.def_id).unwrap())
+        .map(|variant| tcx.hir_span_if_local(variant.def_id).unwrap())
         .collect();
     let (mut spans, mut many) = (Vec::new(), None);
     if let [start @ .., end] = &*variant_spans {
diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs
index ba8124b11fc..cf66ab708bb 100644
--- a/compiler/rustc_hir_analysis/src/check/region.rs
+++ b/compiler/rustc_hir_analysis/src/check/region.rs
@@ -8,7 +8,6 @@
 
 use std::mem;
 
-use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{self, Visitor};
@@ -23,18 +22,11 @@ use tracing::debug;
 
 #[derive(Debug, Copy, Clone)]
 struct Context {
-    /// The scope that contains any new variables declared, plus its depth in
-    /// the scope tree.
+    /// The scope that contains any new variables declared.
     var_parent: Option<Scope>,
 
-    /// Region parent of expressions, etc., plus its depth in the scope tree.
-    parent: Option<(Scope, ScopeDepth)>,
-}
-
-impl Context {
-    fn set_var_parent(&mut self) {
-        self.var_parent = self.parent.map(|(p, _)| p);
-    }
+    /// Region parent of expressions, etc.
+    parent: Option<Scope>,
 }
 
 struct ScopeResolutionVisitor<'tcx> {
@@ -52,28 +44,6 @@ struct ScopeResolutionVisitor<'tcx> {
     scope_tree: ScopeTree,
 
     cx: Context,
-
-    /// `terminating_scopes` is a set containing the ids of each
-    /// statement, or conditional/repeating expression. These scopes
-    /// are calling "terminating scopes" because, when attempting to
-    /// find the scope of a temporary, by default we search up the
-    /// enclosing scopes until we encounter the terminating scope. A
-    /// conditional/repeating expression is one which is not
-    /// guaranteed to execute exactly once upon entering the parent
-    /// scope. This could be because the expression only executes
-    /// conditionally, such as the expression `b` in `a && b`, or
-    /// because the expression may execute many times, such as a loop
-    /// body. The reason that we distinguish such expressions is that,
-    /// upon exiting the parent scope, we cannot statically know how
-    /// many times the expression executed, and thus if the expression
-    /// creates temporaries we cannot know statically how many such
-    /// temporaries we would have to cleanup. Therefore, we ensure that
-    /// the temporaries never outlast the conditional/repeating
-    /// expression, preventing the need for dynamic checks and/or
-    /// arbitrary amounts of stack space. Terminating scopes end
-    /// up being contained in a DestructionScope that contains the
-    /// destructor's execution.
-    terminating_scopes: FxHashSet<hir::ItemLocalId>,
 }
 
 /// Records the lifetime of a local variable as `cx.var_parent`
@@ -88,7 +58,11 @@ fn record_var_lifetime(visitor: &mut ScopeResolutionVisitor<'_>, var_id: hir::It
     }
 }
 
-fn resolve_block<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, blk: &'tcx hir::Block<'tcx>) {
+fn resolve_block<'tcx>(
+    visitor: &mut ScopeResolutionVisitor<'tcx>,
+    blk: &'tcx hir::Block<'tcx>,
+    terminating: bool,
+) {
     debug!("resolve_block(blk.hir_id={:?})", blk.hir_id);
 
     let prev_cx = visitor.cx;
@@ -118,8 +92,8 @@ fn resolve_block<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, blk: &'tcx hi
     // `other_argument()` has run and also the call to `quux(..)`
     // itself has returned.
 
-    visitor.enter_node_scope_with_dtor(blk.hir_id.local_id);
-    visitor.cx.set_var_parent();
+    visitor.enter_node_scope_with_dtor(blk.hir_id.local_id, terminating);
+    visitor.cx.var_parent = visitor.cx.parent;
 
     {
         // This block should be kept approximately in sync with
@@ -138,7 +112,7 @@ fn resolve_block<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, blk: &'tcx hi
                         local_id: blk.hir_id.local_id,
                         data: ScopeData::Remainder(FirstStatementIndex::new(i)),
                     });
-                    visitor.cx.set_var_parent();
+                    visitor.cx.var_parent = visitor.cx.parent;
                     visitor.visit_stmt(statement);
                     // We need to back out temporarily to the last enclosing scope
                     // for the `else` block, so that even the temporaries receiving
@@ -147,8 +121,7 @@ fn resolve_block<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, blk: &'tcx hi
                     // the sequence of visits agree with the order in the default
                     // `hir::intravisit` visitor.
                     mem::swap(&mut prev_cx, &mut visitor.cx);
-                    visitor.terminating_scopes.insert(els.hir_id.local_id);
-                    visitor.visit_block(els);
+                    resolve_block(visitor, els, true);
                     // From now on, we continue normally.
                     visitor.cx = prev_cx;
                 }
@@ -163,7 +136,7 @@ fn resolve_block<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, blk: &'tcx hi
                         local_id: blk.hir_id.local_id,
                         data: ScopeData::Remainder(FirstStatementIndex::new(i)),
                     });
-                    visitor.cx.set_var_parent();
+                    visitor.cx.var_parent = visitor.cx.parent;
                     visitor.visit_stmt(statement)
                 }
                 hir::StmtKind::Item(..) => {
@@ -176,12 +149,12 @@ fn resolve_block<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, blk: &'tcx hi
         if let Some(tail_expr) = blk.expr {
             let local_id = tail_expr.hir_id.local_id;
             let edition = blk.span.edition();
-            if edition.at_least_rust_2024() {
-                visitor.terminating_scopes.insert(local_id);
-            } else if !visitor
-                .tcx
-                .lints_that_dont_need_to_run(())
-                .contains(&lint::LintId::of(lint::builtin::TAIL_EXPR_DROP_ORDER))
+            let terminating = edition.at_least_rust_2024();
+            if !terminating
+                && !visitor
+                    .tcx
+                    .lints_that_dont_need_to_run(())
+                    .contains(&lint::LintId::of(lint::builtin::TAIL_EXPR_DROP_ORDER))
             {
                 // If this temporary scope will be changing once the codebase adopts Rust 2024,
                 // and we are linting about possible semantic changes that would result,
@@ -192,7 +165,7 @@ fn resolve_block<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, blk: &'tcx hi
                     .backwards_incompatible_scope
                     .insert(local_id, Scope { local_id, data: ScopeData::Node });
             }
-            visitor.visit_expr(tail_expr);
+            resolve_expr(visitor, tail_expr, terminating);
         }
     }
 
@@ -210,18 +183,14 @@ fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir:
 
     let prev_cx = visitor.cx;
 
-    visitor.terminating_scopes.insert(arm.hir_id.local_id);
+    visitor.enter_node_scope_with_dtor(arm.hir_id.local_id, true);
+    visitor.cx.var_parent = visitor.cx.parent;
 
-    visitor.enter_node_scope_with_dtor(arm.hir_id.local_id);
-    visitor.cx.set_var_parent();
-
-    if let Some(expr) = arm.guard
-        && !has_let_expr(expr)
-    {
-        visitor.terminating_scopes.insert(expr.hir_id.local_id);
+    resolve_pat(visitor, arm.pat);
+    if let Some(guard) = arm.guard {
+        resolve_expr(visitor, guard, !has_let_expr(guard));
     }
-
-    intravisit::walk_arm(visitor, arm);
+    resolve_expr(visitor, arm.body, false);
 
     visitor.cx = prev_cx;
 }
@@ -250,126 +219,24 @@ fn resolve_stmt<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, stmt: &'tcx hi
     // associated destruction scope that represents the scope of the
     // statement plus its destructors, and thus the scope for which
     // regions referenced by the destructors need to survive.
-    visitor.terminating_scopes.insert(stmt_id);
 
     let prev_parent = visitor.cx.parent;
-    visitor.enter_node_scope_with_dtor(stmt_id);
+    visitor.enter_node_scope_with_dtor(stmt_id, true);
 
     intravisit::walk_stmt(visitor, stmt);
 
     visitor.cx.parent = prev_parent;
 }
 
-fn resolve_expr<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
+fn resolve_expr<'tcx>(
+    visitor: &mut ScopeResolutionVisitor<'tcx>,
+    expr: &'tcx hir::Expr<'tcx>,
+    terminating: bool,
+) {
     debug!("resolve_expr - pre-increment {} expr = {:?}", visitor.expr_and_pat_count, expr);
 
     let prev_cx = visitor.cx;
-    visitor.enter_node_scope_with_dtor(expr.hir_id.local_id);
-
-    {
-        let terminating_scopes = &mut visitor.terminating_scopes;
-        let mut terminating = |id: hir::ItemLocalId| {
-            terminating_scopes.insert(id);
-        };
-        match expr.kind {
-            // Conditional or repeating scopes are always terminating
-            // scopes, meaning that temporaries cannot outlive them.
-            // This ensures fixed size stacks.
-            hir::ExprKind::Binary(
-                source_map::Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. },
-                l,
-                r,
-            ) => {
-                // expr is a short circuiting operator (|| or &&). As its
-                // functionality can't be overridden by traits, it always
-                // processes bool sub-expressions. bools are Copy and thus we
-                // can drop any temporaries in evaluation (read) order
-                // (with the exception of potentially failing let expressions).
-                // We achieve this by enclosing the operands in a terminating
-                // scope, both the LHS and the RHS.
-
-                // We optimize this a little in the presence of chains.
-                // Chains like a && b && c get lowered to AND(AND(a, b), c).
-                // In here, b and c are RHS, while a is the only LHS operand in
-                // that chain. This holds true for longer chains as well: the
-                // leading operand is always the only LHS operand that is not a
-                // binop itself. Putting a binop like AND(a, b) into a
-                // terminating scope is not useful, thus we only put the LHS
-                // into a terminating scope if it is not a binop.
-
-                let terminate_lhs = match l.kind {
-                    // let expressions can create temporaries that live on
-                    hir::ExprKind::Let(_) => false,
-                    // binops already drop their temporaries, so there is no
-                    // need to put them into a terminating scope.
-                    // This is purely an optimization to reduce the number of
-                    // terminating scopes.
-                    hir::ExprKind::Binary(
-                        source_map::Spanned {
-                            node: hir::BinOpKind::And | hir::BinOpKind::Or, ..
-                        },
-                        ..,
-                    ) => false,
-                    // otherwise: mark it as terminating
-                    _ => true,
-                };
-                if terminate_lhs {
-                    terminating(l.hir_id.local_id);
-                }
-
-                // `Let` expressions (in a let-chain) shouldn't be terminating, as their temporaries
-                // should live beyond the immediate expression
-                if !matches!(r.kind, hir::ExprKind::Let(_)) {
-                    terminating(r.hir_id.local_id);
-                }
-            }
-            hir::ExprKind::If(_, then, Some(otherwise)) => {
-                terminating(then.hir_id.local_id);
-                terminating(otherwise.hir_id.local_id);
-            }
-
-            hir::ExprKind::If(_, then, None) => {
-                terminating(then.hir_id.local_id);
-            }
-
-            hir::ExprKind::Loop(body, _, _, _) => {
-                terminating(body.hir_id.local_id);
-            }
-
-            hir::ExprKind::DropTemps(expr) => {
-                // `DropTemps(expr)` does not denote a conditional scope.
-                // Rather, we want to achieve the same behavior as `{ let _t = expr; _t }`.
-                terminating(expr.hir_id.local_id);
-            }
-
-            hir::ExprKind::AssignOp(..)
-            | hir::ExprKind::Index(..)
-            | hir::ExprKind::Unary(..)
-            | hir::ExprKind::Call(..)
-            | hir::ExprKind::MethodCall(..) => {
-                // FIXME(https://github.com/rust-lang/rfcs/issues/811) Nested method calls
-                //
-                // The lifetimes for a call or method call look as follows:
-                //
-                // call.id
-                // - arg0.id
-                // - ...
-                // - argN.id
-                // - call.callee_id
-                //
-                // The idea is that call.callee_id represents *the time when
-                // the invoked function is actually running* and call.id
-                // represents *the time to prepare the arguments and make the
-                // call*. See the section "Borrows in Calls" borrowck/README.md
-                // for an extended explanation of why this distinction is
-                // important.
-                //
-                // record_superlifetime(new_cx, expr.callee_id);
-            }
-
-            _ => {}
-        }
-    }
+    visitor.enter_node_scope_with_dtor(expr.hir_id.local_id, terminating);
 
     let prev_pessimistic = visitor.pessimistic_yield;
 
@@ -424,6 +291,53 @@ fn resolve_expr<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, expr: &'tcx hi
     // properly, we can't miss any types.
 
     match expr.kind {
+        // Conditional or repeating scopes are always terminating
+        // scopes, meaning that temporaries cannot outlive them.
+        // This ensures fixed size stacks.
+        hir::ExprKind::Binary(
+            source_map::Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. },
+            left,
+            right,
+        ) => {
+            // expr is a short circuiting operator (|| or &&). As its
+            // functionality can't be overridden by traits, it always
+            // processes bool sub-expressions. bools are Copy and thus we
+            // can drop any temporaries in evaluation (read) order
+            // (with the exception of potentially failing let expressions).
+            // We achieve this by enclosing the operands in a terminating
+            // scope, both the LHS and the RHS.
+
+            // We optimize this a little in the presence of chains.
+            // Chains like a && b && c get lowered to AND(AND(a, b), c).
+            // In here, b and c are RHS, while a is the only LHS operand in
+            // that chain. This holds true for longer chains as well: the
+            // leading operand is always the only LHS operand that is not a
+            // binop itself. Putting a binop like AND(a, b) into a
+            // terminating scope is not useful, thus we only put the LHS
+            // into a terminating scope if it is not a binop.
+
+            let terminate_lhs = match left.kind {
+                // let expressions can create temporaries that live on
+                hir::ExprKind::Let(_) => false,
+                // binops already drop their temporaries, so there is no
+                // need to put them into a terminating scope.
+                // This is purely an optimization to reduce the number of
+                // terminating scopes.
+                hir::ExprKind::Binary(
+                    source_map::Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. },
+                    ..,
+                ) => false,
+                // otherwise: mark it as terminating
+                _ => true,
+            };
+
+            // `Let` expressions (in a let-chain) shouldn't be terminating, as their temporaries
+            // should live beyond the immediate expression
+            let terminate_rhs = !matches!(right.kind, hir::ExprKind::Let(_));
+
+            resolve_expr(visitor, left, terminate_lhs);
+            resolve_expr(visitor, right, terminate_rhs);
+        }
         // Manually recurse over closures, because they are nested bodies
         // that share the parent environment. We handle const blocks in
         // `visit_inline_const`.
@@ -490,11 +404,11 @@ fn resolve_expr<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, expr: &'tcx hi
                 ScopeData::IfThen
             };
             visitor.enter_scope(Scope { local_id: then.hir_id.local_id, data });
-            visitor.cx.set_var_parent();
+            visitor.cx.var_parent = visitor.cx.parent;
             visitor.visit_expr(cond);
-            visitor.visit_expr(then);
+            resolve_expr(visitor, then, true);
             visitor.cx = expr_cx;
-            visitor.visit_expr(otherwise);
+            resolve_expr(visitor, otherwise, true);
         }
 
         hir::ExprKind::If(cond, then, None) => {
@@ -505,12 +419,22 @@ fn resolve_expr<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, expr: &'tcx hi
                 ScopeData::IfThen
             };
             visitor.enter_scope(Scope { local_id: then.hir_id.local_id, data });
-            visitor.cx.set_var_parent();
+            visitor.cx.var_parent = visitor.cx.parent;
             visitor.visit_expr(cond);
-            visitor.visit_expr(then);
+            resolve_expr(visitor, then, true);
             visitor.cx = expr_cx;
         }
 
+        hir::ExprKind::Loop(body, _, _, _) => {
+            resolve_block(visitor, body, true);
+        }
+
+        hir::ExprKind::DropTemps(expr) => {
+            // `DropTemps(expr)` does not denote a conditional scope.
+            // Rather, we want to achieve the same behavior as `{ let _t = expr; _t }`.
+            resolve_expr(visitor, expr, true);
+        }
+
         _ => intravisit::walk_expr(visitor, expr),
     }
 
@@ -545,7 +469,7 @@ fn resolve_expr<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, expr: &'tcx hi
             // Keep traversing up while we can.
             match visitor.scope_tree.parent_map.get(&scope) {
                 // Don't cross from closure bodies to their parent.
-                Some(&(superscope, _)) => match superscope.data {
+                Some(&superscope) => match superscope.data {
                     ScopeData::CallSite => break,
                     _ => scope = superscope,
                 },
@@ -781,28 +705,24 @@ fn resolve_local<'tcx>(
 
 impl<'tcx> ScopeResolutionVisitor<'tcx> {
     /// Records the current parent (if any) as the parent of `child_scope`.
-    /// Returns the depth of `child_scope`.
-    fn record_child_scope(&mut self, child_scope: Scope) -> ScopeDepth {
+    fn record_child_scope(&mut self, child_scope: Scope) {
         let parent = self.cx.parent;
         self.scope_tree.record_scope_parent(child_scope, parent);
-        // If `child_scope` has no parent, it must be the root node, and so has
-        // a depth of 1. Otherwise, its depth is one more than its parent's.
-        parent.map_or(1, |(_p, d)| d + 1)
     }
 
     /// Records the current parent (if any) as the parent of `child_scope`,
     /// and sets `child_scope` as the new current parent.
     fn enter_scope(&mut self, child_scope: Scope) {
-        let child_depth = self.record_child_scope(child_scope);
-        self.cx.parent = Some((child_scope, child_depth));
+        self.record_child_scope(child_scope);
+        self.cx.parent = Some(child_scope);
     }
 
-    fn enter_node_scope_with_dtor(&mut self, id: hir::ItemLocalId) {
+    fn enter_node_scope_with_dtor(&mut self, id: hir::ItemLocalId, terminating: bool) {
         // If node was previously marked as a terminating scope during the
         // recursive visit of its parent node in the HIR, then we need to
         // account for the destruction scope representing the scope of
         // the destructors that run immediately after it completes.
-        if self.terminating_scopes.contains(&id) {
+        if terminating {
             self.enter_scope(Scope { local_id: id, data: ScopeData::Destruction });
         }
         self.enter_scope(Scope { local_id: id, data: ScopeData::Node });
@@ -814,13 +734,11 @@ impl<'tcx> ScopeResolutionVisitor<'tcx> {
         // visited the body.
         let outer_ec = mem::replace(&mut self.expr_and_pat_count, 0);
         let outer_cx = self.cx;
-        let outer_ts = mem::take(&mut self.terminating_scopes);
         // The 'pessimistic yield' flag is set to true when we are
         // processing a `+=` statement and have to make pessimistic
         // control flow assumptions. This doesn't apply to nested
         // bodies within the `+=` statements. See #69307.
         let outer_pessimistic_yield = mem::replace(&mut self.pessimistic_yield, false);
-        self.terminating_scopes.insert(hir_id.local_id);
 
         self.enter_scope(Scope { local_id: hir_id.local_id, data: ScopeData::CallSite });
         self.enter_scope(Scope { local_id: hir_id.local_id, data: ScopeData::Arguments });
@@ -830,14 +748,13 @@ impl<'tcx> ScopeResolutionVisitor<'tcx> {
         // Restore context we had at the start.
         self.expr_and_pat_count = outer_ec;
         self.cx = outer_cx;
-        self.terminating_scopes = outer_ts;
         self.pessimistic_yield = outer_pessimistic_yield;
     }
 }
 
 impl<'tcx> Visitor<'tcx> for ScopeResolutionVisitor<'tcx> {
     fn visit_block(&mut self, b: &'tcx Block<'tcx>) {
-        resolve_block(self, b);
+        resolve_block(self, b, false);
     }
 
     fn visit_body(&mut self, body: &hir::Body<'tcx>) {
@@ -855,13 +772,13 @@ impl<'tcx> Visitor<'tcx> for ScopeResolutionVisitor<'tcx> {
         self.enter_body(body.value.hir_id, |this| {
             if this.tcx.hir_body_owner_kind(owner_id).is_fn_or_closure() {
                 // The arguments and `self` are parented to the fn.
-                this.cx.set_var_parent();
+                this.cx.var_parent = this.cx.parent;
                 for param in body.params {
                     this.visit_pat(param.pat);
                 }
 
                 // The body of the every fn is a root scope.
-                this.visit_expr(body.value)
+                resolve_expr(this, body.value, true);
             } else {
                 // Only functions have an outer terminating (drop) scope, while
                 // temporaries in constant initializers may be 'static, but only
@@ -882,6 +799,10 @@ impl<'tcx> Visitor<'tcx> for ScopeResolutionVisitor<'tcx> {
                 // (i.e., `'static`), which means that after `g` returns, it drops,
                 // and all the associated destruction scope rules apply.
                 this.cx.var_parent = None;
+                this.enter_scope(Scope {
+                    local_id: body.value.hir_id.local_id,
+                    data: ScopeData::Destruction,
+                });
                 resolve_local(this, None, Some(body.value));
             }
         })
@@ -897,7 +818,7 @@ impl<'tcx> Visitor<'tcx> for ScopeResolutionVisitor<'tcx> {
         resolve_stmt(self, s);
     }
     fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
-        resolve_expr(self, ex);
+        resolve_expr(self, ex, false);
     }
     fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) {
         resolve_local(self, Some(l.pat), l.init)
@@ -927,7 +848,6 @@ pub(crate) fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree {
             scope_tree: ScopeTree::default(),
             expr_and_pat_count: 0,
             cx: Context { parent: None, var_parent: None },
-            terminating_scopes: Default::default(),
             pessimistic_yield: false,
             fixup_scopes: vec![],
         };
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 952af592311..83d095ab72e 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -188,10 +188,10 @@ fn check_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGua
 /// definition itself. For example, this definition would be illegal:
 ///
 /// ```rust
-/// struct Ref<'a, T> { x: &'a T }
+/// struct StaticRef<T> { x: &'static T }
 /// ```
 ///
-/// because the type did not declare that `T:'a`.
+/// because the type did not declare that `T: 'static`.
 ///
 /// We do this check as a pre-pass before checking fn bodies because if these constraints are
 /// not included it frequently leads to confusing errors in fn bodies. So it's better to check
diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs
index 15e0a72fdcb..16bac430491 100644
--- a/compiler/rustc_hir_analysis/src/coherence/mod.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs
@@ -153,9 +153,12 @@ pub(crate) fn provide(providers: &mut Providers) {
 }
 
 fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) -> Result<(), ErrorGuaranteed> {
+    let impls = tcx.local_trait_impls(def_id);
     // If there are no impls for the trait, then "all impls" are trivially coherent and we won't check anything
     // anyway. Thus we bail out even before the specialization graph, avoiding the dep_graph edge.
-    let Some(impls) = tcx.all_local_trait_impls(()).get(&def_id) else { return Ok(()) };
+    if impls.is_empty() {
+        return Ok(());
+    }
     // Trigger building the specialization graph for the trait. This will detect and report any
     // overlap errors.
     let mut res = tcx.ensure_ok().specialization_graph_of(def_id);
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index 075abc32594..625f51dd29e 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -61,6 +61,7 @@ pub(crate) fn provide(providers: &mut Providers) {
     *providers = Providers {
         type_of: type_of::type_of,
         type_of_opaque: type_of::type_of_opaque,
+        type_of_opaque_hir_typeck: type_of::type_of_opaque_hir_typeck,
         type_alias_is_lazy: type_of::type_alias_is_lazy,
         item_bounds: item_bounds::item_bounds,
         explicit_item_bounds: item_bounds::explicit_item_bounds,
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index a153ce8ea90..2bed28d7b71 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -116,8 +116,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
                 {
                     // enum variant discriminants are not allowed to use any kind of generics
                     None
-                } else if let Some(param_id) =
-                    tcx.hir().opt_const_param_default_param_def_id(hir_id)
+                } else if let Some(param_id) = tcx.hir_opt_const_param_default_param_def_id(hir_id)
                 {
                     // If the def_id we are calling generics_of on is an anon ct default i.e:
                     //
diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
index 6e07f0ff53c..279b1e82a71 100644
--- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
+++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
@@ -38,13 +38,13 @@ fn associated_type_bounds<'tcx>(
         let icx = ItemCtxt::new(tcx, assoc_item_def_id);
         let mut bounds = Vec::new();
         icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter);
-        // Associated types are implicitly sized unless a `?Sized` bound is found
+        // Implicit bounds are added to associated types unless a `?Trait` bound is found
         match filter {
             PredicateFilter::All
             | PredicateFilter::SelfOnly
             | PredicateFilter::SelfTraitThatDefines(_)
             | PredicateFilter::SelfAndAssociatedTypeBounds => {
-                icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span);
+                icx.lowerer().add_default_traits(&mut bounds, item_ty, hir_bounds, None, span);
             }
             // `ConstIfConst` is only interested in `~const` bounds.
             PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {}
@@ -327,14 +327,13 @@ fn opaque_type_bounds<'tcx>(
         let icx = ItemCtxt::new(tcx, opaque_def_id);
         let mut bounds = Vec::new();
         icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter);
-        // Opaque types are implicitly sized unless a `?Sized` bound is found
+        // Implicit bounds are added to opaque types unless a `?Trait` bound is found
         match filter {
             PredicateFilter::All
             | PredicateFilter::SelfOnly
             | PredicateFilter::SelfTraitThatDefines(_)
             | PredicateFilter::SelfAndAssociatedTypeBounds => {
-                // Associated types are implicitly sized unless a `?Sized` bound is found
-                icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span);
+                icx.lowerer().add_default_traits(&mut bounds, item_ty, hir_bounds, None, span);
             }
             //`ConstIfConst` is only interested in `~const` bounds.
             PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {}
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index 4bd89861a9e..776b23bea8e 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -165,12 +165,42 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
 
             ItemKind::Trait(_, _, _, _, self_bounds, ..)
             | ItemKind::TraitAlias(_, _, self_bounds) => {
-                is_trait = Some(self_bounds);
+                is_trait = Some((self_bounds, item.span));
             }
             _ => {}
         }
     };
 
+    if let Node::TraitItem(item) = node {
+        let parent = tcx.local_parent(item.hir_id().owner.def_id);
+        let Node::Item(parent_trait) = tcx.hir_node_by_def_id(parent) else {
+            unreachable!();
+        };
+
+        let (trait_generics, trait_bounds) = match parent_trait.kind {
+            hir::ItemKind::Trait(_, _, _, generics, supertraits, _) => (generics, supertraits),
+            hir::ItemKind::TraitAlias(_, generics, supertraits) => (generics, supertraits),
+            _ => unreachable!(),
+        };
+
+        // Implicitly add `Self: DefaultAutoTrait` clauses on trait associated items if
+        // they are not added as super trait bounds to the trait itself. See comment on
+        // `requires_default_supertraits` for more details.
+        if !icx.lowerer().requires_default_supertraits(trait_bounds, trait_generics) {
+            let mut bounds = Vec::new();
+            let self_ty_where_predicates = (parent, item.generics.predicates);
+            icx.lowerer().add_default_traits_with_filter(
+                &mut bounds,
+                tcx.types.self_param,
+                &[],
+                Some(self_ty_where_predicates),
+                item.span,
+                |tr| tr != hir::LangItem::Sized,
+            );
+            predicates.extend(bounds);
+        }
+    }
+
     let generics = tcx.generics_of(def_id);
 
     // Below we'll consider the bounds on the type parameters (including `Self`)
@@ -181,11 +211,18 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
         let mut bounds = Vec::new();
         icx.lowerer().lower_bounds(
             tcx.types.self_param,
-            self_bounds,
+            self_bounds.0,
             &mut bounds,
             ty::List::empty(),
             PredicateFilter::All,
         );
+        icx.lowerer().add_default_super_traits(
+            def_id,
+            &mut bounds,
+            self_bounds.0,
+            hir_generics,
+            self_bounds.1,
+        );
         predicates.extend(bounds);
     }
 
@@ -210,8 +247,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
             GenericParamKind::Type { .. } => {
                 let param_ty = icx.lowerer().lower_ty_param(param.hir_id);
                 let mut bounds = Vec::new();
-                // Params are implicitly sized unless a `?Sized` bound is found
-                icx.lowerer().add_sized_bound(
+                // Implicit bounds are added to type params unless a `?Trait` bound is found
+                icx.lowerer().add_default_traits(
                     &mut bounds,
                     param_ty,
                     &[],
@@ -508,7 +545,7 @@ pub(super) fn explicit_predicates_of<'tcx>(
         if matches!(def_kind, DefKind::AnonConst)
             && tcx.features().generic_const_exprs()
             && let Some(defaulted_param_def_id) =
-                tcx.hir().opt_const_param_default_param_def_id(tcx.local_def_id_to_hir_id(def_id))
+                tcx.hir_opt_const_param_default_param_def_id(tcx.local_def_id_to_hir_id(def_id))
         {
             // In `generics_of` we set the generics' parent to be our parent's parent which means that
             // we lose out on the predicates of our actual parent if we dont return those predicates here.
@@ -625,6 +662,22 @@ pub(super) fn implied_predicates_with_filter<'tcx>(
     let self_param_ty = tcx.types.self_param;
     let mut bounds = Vec::new();
     icx.lowerer().lower_bounds(self_param_ty, superbounds, &mut bounds, ty::List::empty(), filter);
+    match filter {
+        PredicateFilter::All
+        | PredicateFilter::SelfOnly
+        | PredicateFilter::SelfTraitThatDefines(_)
+        | PredicateFilter::SelfAndAssociatedTypeBounds => {
+            icx.lowerer().add_default_super_traits(
+                trait_def_id,
+                &mut bounds,
+                superbounds,
+                generics,
+                item.span,
+            );
+        }
+        //`ConstIfConst` is only interested in `~const` bounds.
+        PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {}
+    }
 
     let where_bounds_that_match =
         icx.probe_ty_param_bounds_in_generics(generics, item.owner_id.def_id, filter);
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 9b0d57bd75b..404753875ee 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -1529,7 +1529,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
             if let ResolvedArg::LateBound(..) = def
                 && let Some(what) = crossed_late_boundary
             {
-                let use_span = self.tcx.hir().span(hir_id);
+                let use_span = self.tcx.hir_span(hir_id);
                 let def_span = self.tcx.def_span(param_def_id);
                 let guar = match self.tcx.def_kind(param_def_id) {
                     DefKind::ConstParam => {
@@ -1576,11 +1576,11 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                 } => {
                     let guar = self.tcx.dcx().emit_err(match self.tcx.def_kind(param_def_id) {
                         DefKind::TyParam => errors::LateBoundInApit::Type {
-                            span: self.tcx.hir().span(hir_id),
+                            span: self.tcx.hir_span(hir_id),
                             param_span: self.tcx.def_span(param_def_id),
                         },
                         DefKind::ConstParam => errors::LateBoundInApit::Const {
-                            span: self.tcx.hir().span(hir_id),
+                            span: self.tcx.hir_span(hir_id),
                             param_span: self.tcx.def_span(param_def_id),
                         },
                         kind => {
@@ -1605,7 +1605,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
 
         self.tcx
             .dcx()
-            .span_bug(self.tcx.hir().span(hir_id), format!("could not resolve {param_def_id:?}"));
+            .span_bug(self.tcx.hir_span(hir_id), format!("could not resolve {param_def_id:?}"));
     }
 
     #[instrument(level = "debug", skip(self))]
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index afda2c142e2..694c1228859 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -7,7 +7,9 @@ use rustc_hir::{self as hir, AmbigArg, HirId};
 use rustc_middle::query::plumbing::CyclePlaceholder;
 use rustc_middle::ty::print::with_forced_trimmed_paths;
 use rustc_middle::ty::util::IntTypeExt;
-use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, fold_regions};
+use rustc_middle::ty::{
+    self, DefiningScopeKind, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, fold_regions,
+};
 use rustc_middle::{bug, span_bug};
 use rustc_span::{DUMMY_SP, Ident, Span};
 
@@ -324,10 +326,18 @@ pub(super) fn type_of_opaque(
     if let Some(def_id) = def_id.as_local() {
         Ok(ty::EarlyBinder::bind(match tcx.hir_node_by_def_id(def_id).expect_opaque_ty().origin {
             hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false, .. } => {
-                opaque::find_opaque_ty_constraints_for_tait(tcx, def_id)
+                opaque::find_opaque_ty_constraints_for_tait(
+                    tcx,
+                    def_id,
+                    DefiningScopeKind::MirBorrowck,
+                )
             }
             hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true, .. } => {
-                opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(tcx, def_id)
+                opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
+                    tcx,
+                    def_id,
+                    DefiningScopeKind::MirBorrowck,
+                )
             }
             // Opaque types desugared from `impl Trait`.
             hir::OpaqueTyOrigin::FnReturn { parent: owner, in_trait_or_impl }
@@ -340,7 +350,12 @@ pub(super) fn type_of_opaque(
                         "tried to get type of this RPITIT with no definition"
                     );
                 }
-                opaque::find_opaque_ty_constraints_for_rpit(tcx, def_id, owner)
+                opaque::find_opaque_ty_constraints_for_rpit(
+                    tcx,
+                    def_id,
+                    owner,
+                    DefiningScopeKind::MirBorrowck,
+                )
             }
         }))
     } else {
@@ -350,6 +365,42 @@ pub(super) fn type_of_opaque(
     }
 }
 
+pub(super) fn type_of_opaque_hir_typeck(
+    tcx: TyCtxt<'_>,
+    def_id: LocalDefId,
+) -> ty::EarlyBinder<'_, Ty<'_>> {
+    ty::EarlyBinder::bind(match tcx.hir_node_by_def_id(def_id).expect_opaque_ty().origin {
+        hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false, .. } => {
+            opaque::find_opaque_ty_constraints_for_tait(tcx, def_id, DefiningScopeKind::HirTypeck)
+        }
+        hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true, .. } => {
+            opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
+                tcx,
+                def_id,
+                DefiningScopeKind::HirTypeck,
+            )
+        }
+        // Opaque types desugared from `impl Trait`.
+        hir::OpaqueTyOrigin::FnReturn { parent: owner, in_trait_or_impl }
+        | hir::OpaqueTyOrigin::AsyncFn { parent: owner, in_trait_or_impl } => {
+            if in_trait_or_impl == Some(hir::RpitContext::Trait)
+                && !tcx.defaultness(owner).has_value()
+            {
+                span_bug!(
+                    tcx.def_span(def_id),
+                    "tried to get type of this RPITIT with no definition"
+                );
+            }
+            opaque::find_opaque_ty_constraints_for_rpit(
+                tcx,
+                def_id,
+                owner,
+                DefiningScopeKind::HirTypeck,
+            )
+        }
+    })
+}
+
 fn infer_placeholder_type<'tcx>(
     cx: &dyn HirTyLowerer<'tcx>,
     def_id: LocalDefId,
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
index 3dec1e286b4..3fe3d71b32d 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
@@ -3,8 +3,7 @@ use rustc_hir::def_id::LocalDefId;
 use rustc_hir::{self as hir, Expr, ImplItem, Item, Node, TraitItem, def, intravisit};
 use rustc_middle::bug;
 use rustc_middle::hir::nested_filter;
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
-use rustc_span::DUMMY_SP;
+use rustc_middle::ty::{self, DefiningScopeKind, Ty, TyCtxt, TypeVisitableExt};
 use tracing::{debug, instrument, trace};
 
 use crate::errors::{TaitForwardCompat2, UnconstrainedOpaqueType};
@@ -15,6 +14,7 @@ use crate::errors::{TaitForwardCompat2, UnconstrainedOpaqueType};
 pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
     tcx: TyCtxt<'_>,
     def_id: LocalDefId,
+    opaque_types_from: DefiningScopeKind,
 ) -> Ty<'_> {
     let mut parent_def_id = def_id;
     while tcx.def_kind(parent_def_id) == def::DefKind::OpaqueTy {
@@ -27,7 +27,7 @@ pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
         other => bug!("invalid impl trait in assoc type parent: {other:?}"),
     }
 
-    let mut locator = TaitConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] };
+    let mut locator = TaitConstraintLocator { def_id, tcx, found: None, opaque_types_from };
 
     for &assoc_id in tcx.associated_item_def_ids(impl_def_id) {
         let assoc = tcx.associated_item(assoc_id);
@@ -39,25 +39,14 @@ pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
     }
 
     if let Some(hidden) = locator.found {
-        // Only check against typeck if we didn't already error
-        if !hidden.ty.references_error() {
-            for concrete_type in locator.typeck_types {
-                if concrete_type.ty != tcx.erase_regions(hidden.ty) {
-                    if let Ok(d) = hidden.build_mismatch_error(&concrete_type, tcx) {
-                        d.emit();
-                    }
-                }
-            }
-        }
-
         hidden.ty
     } else {
-        let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType {
+        let guar = tcx.dcx().emit_err(UnconstrainedOpaqueType {
             span: tcx.def_span(def_id),
             name: tcx.item_ident(parent_def_id.to_def_id()),
             what: "impl",
         });
-        Ty::new_error(tcx, reported)
+        Ty::new_error(tcx, guar)
     }
 }
 
@@ -80,23 +69,16 @@ pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
 /// fn b<T>() -> Foo<T, u32> { .. }
 /// ```
 #[instrument(skip(tcx), level = "debug")]
-pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
-    let mut locator = TaitConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] };
+pub(super) fn find_opaque_ty_constraints_for_tait(
+    tcx: TyCtxt<'_>,
+    def_id: LocalDefId,
+    opaque_types_from: DefiningScopeKind,
+) -> Ty<'_> {
+    let mut locator = TaitConstraintLocator { def_id, tcx, found: None, opaque_types_from };
 
     tcx.hir_walk_toplevel_module(&mut locator);
 
     if let Some(hidden) = locator.found {
-        // Only check against typeck if we didn't already error
-        if !hidden.ty.references_error() {
-            for concrete_type in locator.typeck_types {
-                if concrete_type.ty != tcx.erase_regions(hidden.ty) {
-                    if let Ok(d) = hidden.build_mismatch_error(&concrete_type, tcx) {
-                        d.emit();
-                    }
-                }
-            }
-        }
-
         hidden.ty
     } else {
         let mut parent_def_id = def_id;
@@ -104,12 +86,12 @@ pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: Local
             // Account for `type Alias = impl Trait<Foo = impl Trait>;` (#116031)
             parent_def_id = tcx.local_parent(parent_def_id);
         }
-        let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType {
+        let guar = tcx.dcx().emit_err(UnconstrainedOpaqueType {
             span: tcx.def_span(def_id),
             name: tcx.item_ident(parent_def_id.to_def_id()),
             what: "crate",
         });
-        Ty::new_error(tcx, reported)
+        Ty::new_error(tcx, guar)
     }
 }
 
@@ -126,22 +108,44 @@ struct TaitConstraintLocator<'tcx> {
     /// type).
     found: Option<ty::OpaqueHiddenType<'tcx>>,
 
-    /// In the presence of dead code, typeck may figure out a hidden type
-    /// while borrowck will not. We collect these cases here and check at
-    /// the end that we actually found a type that matches (modulo regions).
-    typeck_types: Vec<ty::OpaqueHiddenType<'tcx>>,
+    opaque_types_from: DefiningScopeKind,
 }
 
-impl TaitConstraintLocator<'_> {
+impl<'tcx> TaitConstraintLocator<'tcx> {
+    fn insert_found(&mut self, hidden_ty: ty::OpaqueHiddenType<'tcx>) {
+        if let Some(prev) = &mut self.found {
+            if hidden_ty.ty != prev.ty {
+                let (Ok(guar) | Err(guar)) =
+                    prev.build_mismatch_error(&hidden_ty, self.tcx).map(|d| d.emit());
+                prev.ty = Ty::new_error(self.tcx, guar);
+            }
+        } else {
+            self.found = Some(hidden_ty);
+        }
+    }
+
+    fn non_defining_use_in_defining_scope(&mut self, item_def_id: LocalDefId) {
+        let guar = self.tcx.dcx().emit_err(TaitForwardCompat2 {
+            span: self
+                .tcx
+                .def_ident_span(item_def_id)
+                .unwrap_or_else(|| self.tcx.def_span(item_def_id)),
+            opaque_type_span: self.tcx.def_span(self.def_id),
+            opaque_type: self.tcx.def_path_str(self.def_id),
+        });
+        self.insert_found(ty::OpaqueHiddenType::new_error(self.tcx, guar));
+    }
+
     #[instrument(skip(self), level = "debug")]
     fn check(&mut self, item_def_id: LocalDefId) {
         // Don't try to check items that cannot possibly constrain the type.
-        if !self.tcx.has_typeck_results(item_def_id) {
+        let tcx = self.tcx;
+        if !tcx.has_typeck_results(item_def_id) {
             debug!("no constraint: no typeck results");
             return;
         }
 
-        let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id);
+        let opaque_types_defined_by = tcx.opaque_types_defined_by(item_def_id);
         // Don't try to check items that cannot possibly constrain the type.
         if !opaque_types_defined_by.contains(&self.def_id) {
             debug!("no constraint: no opaque types defined");
@@ -152,7 +156,7 @@ impl TaitConstraintLocator<'_> {
         // "non-defining use" errors for them.
         // Note that we use `Node::fn_sig` instead of `Node::fn_decl` here, because the former
         // excludes closures, which are allowed to have `_` in their return type.
-        let hir_node = self.tcx.hir_node_by_def_id(item_def_id);
+        let hir_node = tcx.hir_node_by_def_id(item_def_id);
         debug_assert!(
             !matches!(hir_node, Node::ForeignItem(..)),
             "foreign items cannot constrain opaque types",
@@ -164,88 +168,39 @@ impl TaitConstraintLocator<'_> {
                 hir_sig.decl.output.span(),
                 "inferring return types and opaque types do not mix well",
             );
-            self.found =
-                Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: Ty::new_error(self.tcx, guar) });
+            self.found = Some(ty::OpaqueHiddenType::new_error(tcx, guar));
             return;
         }
 
-        // Calling `mir_borrowck` can lead to cycle errors through
-        // const-checking, avoid calling it if we don't have to.
-        // ```rust
-        // type Foo = impl Fn() -> usize; // when computing type for this
-        // const fn bar() -> Foo {
-        //     || 0usize
-        // }
-        // const BAZR: Foo = bar(); // we would mir-borrowck this, causing cycles
-        // // because we again need to reveal `Foo` so we can check whether the
-        // // constant does not contain interior mutability.
-        // ```
-        let tables = self.tcx.typeck(item_def_id);
-        if let Some(guar) = tables.tainted_by_errors {
-            self.found =
-                Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: Ty::new_error(self.tcx, guar) });
-            return;
-        }
-
-        let mut constrained = false;
-        for (&opaque_type_key, &hidden_type) in &tables.concrete_opaque_types {
-            if opaque_type_key.def_id != self.def_id {
-                continue;
-            }
-            constrained = true;
-
-            let concrete_type =
-                self.tcx.erase_regions(hidden_type.remap_generic_params_to_declaration_params(
-                    opaque_type_key,
-                    self.tcx,
-                    true,
-                ));
-            if self.typeck_types.iter().all(|prev| prev.ty != concrete_type.ty) {
-                self.typeck_types.push(concrete_type);
-            }
-        }
-
-        if !constrained {
-            debug!("no constraints in typeck results");
-            if opaque_types_defined_by.contains(&self.def_id) {
-                let guar = self.tcx.dcx().emit_err(TaitForwardCompat2 {
-                    span: self
-                        .tcx
-                        .def_ident_span(item_def_id)
-                        .unwrap_or_else(|| self.tcx.def_span(item_def_id)),
-                    opaque_type_span: self.tcx.def_span(self.def_id),
-                    opaque_type: self.tcx.def_path_str(self.def_id),
-                });
-                // Avoid "opaque type not constrained" errors on the opaque itself.
-                self.found = Some(ty::OpaqueHiddenType {
-                    span: DUMMY_SP,
-                    ty: Ty::new_error(self.tcx, guar),
-                });
+        match self.opaque_types_from {
+            DefiningScopeKind::HirTypeck => {
+                let tables = tcx.typeck(item_def_id);
+                if let Some(guar) = tables.tainted_by_errors {
+                    self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar));
+                } else if let Some(&hidden_type) = tables.concrete_opaque_types.get(&self.def_id) {
+                    self.insert_found(hidden_type);
+                } else {
+                    self.non_defining_use_in_defining_scope(item_def_id);
+                }
             }
-            return;
-        };
-
-        // Use borrowck to get the type with unerased regions.
-        let borrowck_results = &self.tcx.mir_borrowck(item_def_id);
-
-        // If the body was tainted, then assume the opaque may have been constrained and just set it to error.
-        if let Some(guar) = borrowck_results.tainted_by_errors {
-            self.found =
-                Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: Ty::new_error(self.tcx, guar) });
-            return;
-        }
-
-        debug!(?borrowck_results.concrete_opaque_types);
-        if let Some(&concrete_type) = borrowck_results.concrete_opaque_types.get(&self.def_id) {
-            debug!(?concrete_type, "found constraint");
-            if let Some(prev) = &mut self.found {
-                if concrete_type.ty != prev.ty {
-                    let (Ok(guar) | Err(guar)) =
-                        prev.build_mismatch_error(&concrete_type, self.tcx).map(|d| d.emit());
-                    prev.ty = Ty::new_error(self.tcx, guar);
+            DefiningScopeKind::MirBorrowck => {
+                let borrowck_result = tcx.mir_borrowck(item_def_id);
+                if let Some(guar) = borrowck_result.tainted_by_errors {
+                    self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar));
+                } else if let Some(&hidden_type) =
+                    borrowck_result.concrete_opaque_types.get(&self.def_id)
+                {
+                    debug!(?hidden_type, "found constraint");
+                    self.insert_found(hidden_type);
+                } else if let Err(guar) = tcx
+                    .type_of_opaque_hir_typeck(self.def_id)
+                    .instantiate_identity()
+                    .error_reported()
+                {
+                    self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar));
+                } else {
+                    self.non_defining_use_in_defining_scope(item_def_id);
                 }
-            } else {
-                self.found = Some(concrete_type);
             }
         }
     }
@@ -287,126 +242,42 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>(
     tcx: TyCtxt<'tcx>,
     def_id: LocalDefId,
     owner_def_id: LocalDefId,
+    opaque_types_from: DefiningScopeKind,
 ) -> Ty<'tcx> {
-    let tables = tcx.typeck(owner_def_id);
-
-    // Check that all of the opaques we inferred during HIR are compatible.
-    // FIXME: We explicitly don't check that the types inferred during HIR
-    // typeck are compatible with the one that we infer during borrowck,
-    // because that one actually sometimes has consts evaluated eagerly so
-    // using strict type equality will fail.
-    let mut hir_opaque_ty: Option<ty::OpaqueHiddenType<'tcx>> = None;
-    if tables.tainted_by_errors.is_none() {
-        for (&opaque_type_key, &hidden_type) in &tables.concrete_opaque_types {
-            if opaque_type_key.def_id != def_id {
-                continue;
-            }
-            let concrete_type = tcx.erase_regions(
-                hidden_type.remap_generic_params_to_declaration_params(opaque_type_key, tcx, true),
-            );
-            if let Some(prev) = &mut hir_opaque_ty {
-                if concrete_type.ty != prev.ty {
-                    if let Ok(d) = prev.build_mismatch_error(&concrete_type, tcx) {
-                        d.emit();
-                    }
-                }
+    match opaque_types_from {
+        DefiningScopeKind::HirTypeck => {
+            let tables = tcx.typeck(owner_def_id);
+            if let Some(guar) = tables.tainted_by_errors {
+                Ty::new_error(tcx, guar)
+            } else if let Some(hidden_ty) = tables.concrete_opaque_types.get(&def_id) {
+                hidden_ty.ty
             } else {
-                hir_opaque_ty = Some(concrete_type);
+                // FIXME(-Znext-solver): This should not be necessary and we should
+                // instead rely on inference variable fallback inside of typeck itself.
+
+                // We failed to resolve the opaque type or it
+                // resolves to itself. We interpret this as the
+                // no values of the hidden type ever being constructed,
+                // so we can just make the hidden type be `!`.
+                // For backwards compatibility reasons, we fall back to
+                // `()` until we the diverging default is changed.
+                Ty::new_diverging_default(tcx)
             }
         }
-    }
-
-    let mir_opaque_ty = tcx.mir_borrowck(owner_def_id).concrete_opaque_types.get(&def_id).copied();
-    if let Some(mir_opaque_ty) = mir_opaque_ty {
-        if mir_opaque_ty.references_error() {
-            return mir_opaque_ty.ty;
-        }
-
-        debug!(?owner_def_id);
-        let mut locator = RpitConstraintChecker { def_id, tcx, found: mir_opaque_ty };
-
-        match tcx.hir_node_by_def_id(owner_def_id) {
-            Node::Item(it) => intravisit::walk_item(&mut locator, it),
-            Node::ImplItem(it) => intravisit::walk_impl_item(&mut locator, it),
-            Node::TraitItem(it) => intravisit::walk_trait_item(&mut locator, it),
-            other => bug!("{:?} is not a valid scope for an opaque type item", other),
-        }
-
-        mir_opaque_ty.ty
-    } else if let Some(guar) = tables.tainted_by_errors {
-        // Some error in the owner fn prevented us from populating
-        // the `concrete_opaque_types` table.
-        Ty::new_error(tcx, guar)
-    } else {
-        // Fall back to the RPIT we inferred during HIR typeck
-        if let Some(hir_opaque_ty) = hir_opaque_ty {
-            hir_opaque_ty.ty
-        } else {
-            // We failed to resolve the opaque type or it
-            // resolves to itself. We interpret this as the
-            // no values of the hidden type ever being constructed,
-            // so we can just make the hidden type be `!`.
-            // For backwards compatibility reasons, we fall back to
-            // `()` until we the diverging default is changed.
-            Ty::new_diverging_default(tcx)
-        }
-    }
-}
-
-struct RpitConstraintChecker<'tcx> {
-    tcx: TyCtxt<'tcx>,
-
-    /// def_id of the opaque type whose defining uses are being checked
-    def_id: LocalDefId,
-
-    found: ty::OpaqueHiddenType<'tcx>,
-}
-
-impl RpitConstraintChecker<'_> {
-    #[instrument(skip(self), level = "debug")]
-    fn check(&self, def_id: LocalDefId) {
-        // Use borrowck to get the type with unerased regions.
-        let concrete_opaque_types = &self.tcx.mir_borrowck(def_id).concrete_opaque_types;
-        debug!(?concrete_opaque_types);
-        if let Some(&concrete_type) = concrete_opaque_types.get(&self.def_id) {
-            debug!(?concrete_type, "found constraint");
-            if concrete_type.ty != self.found.ty {
-                if let Ok(d) = self.found.build_mismatch_error(&concrete_type, self.tcx) {
-                    d.emit();
+        DefiningScopeKind::MirBorrowck => {
+            let borrowck_result = tcx.mir_borrowck(owner_def_id);
+            if let Some(guar) = borrowck_result.tainted_by_errors {
+                Ty::new_error(tcx, guar)
+            } else if let Some(hidden_ty) = borrowck_result.concrete_opaque_types.get(&def_id) {
+                hidden_ty.ty
+            } else {
+                let hir_ty = tcx.type_of_opaque_hir_typeck(def_id).instantiate_identity();
+                if let Err(guar) = hir_ty.error_reported() {
+                    Ty::new_error(tcx, guar)
+                } else {
+                    hir_ty
                 }
             }
         }
     }
 }
-
-impl<'tcx> intravisit::Visitor<'tcx> for RpitConstraintChecker<'tcx> {
-    type NestedFilter = nested_filter::OnlyBodies;
-
-    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
-        self.tcx
-    }
-    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
-        intravisit::walk_expr(self, ex);
-    }
-    fn visit_item(&mut self, it: &'tcx Item<'tcx>) {
-        trace!(?it.owner_id);
-        // The opaque type itself or its children are not within its reveal scope.
-        if it.owner_id.def_id != self.def_id {
-            self.check(it.owner_id.def_id);
-            intravisit::walk_item(self, it);
-        }
-    }
-    fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) {
-        trace!(?it.owner_id);
-        // The opaque type itself or its children are not within its reveal scope.
-        if it.owner_id.def_id != self.def_id {
-            self.check(it.owner_id.def_id);
-            intravisit::walk_impl_item(self, it);
-        }
-    }
-    fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) {
-        trace!(?it.owner_id);
-        self.check(it.owner_id.def_id);
-        intravisit::walk_trait_item(self, it);
-    }
-}
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
index f0dffd780bc..c3bb860538e 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -4,9 +4,9 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_errors::codes::*;
 use rustc_errors::struct_span_code_err;
 use rustc_hir as hir;
-use rustc_hir::HirId;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::{AmbigArg, HirId};
 use rustc_middle::bug;
 use rustc_middle::ty::{
     self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
@@ -24,25 +24,190 @@ use crate::hir_ty_lowering::{
 };
 
 impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
-    /// Add a `Sized` bound to the `bounds` if appropriate.
-    ///
-    /// Doesn't add the bound if the HIR bounds contain any of `Sized`, `?Sized` or `!Sized`.
-    pub(crate) fn add_sized_bound(
+    pub(crate) fn add_default_traits(
         &self,
         bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
         self_ty: Ty<'tcx>,
+        hir_bounds: &[hir::GenericBound<'tcx>],
+        self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
+        span: Span,
+    ) {
+        self.add_default_traits_with_filter(
+            bounds,
+            self_ty,
+            hir_bounds,
+            self_ty_where_predicates,
+            span,
+            |_| true,
+        );
+    }
+
+    /// Checks whether `Self: DefaultAutoTrait` bounds should be added on trait super bounds
+    /// or associative items.
+    ///
+    /// To keep backward compatibility with existing code, `experimental_default_bounds` bounds
+    /// should be added everywhere, including super bounds. However this causes a huge performance
+    /// costs. For optimization purposes instead of adding default supertraits, bounds
+    /// are added to the associative items:
+    ///
+    /// ```ignore(illustrative)
+    /// // Default bounds are generated in the following way:
+    /// trait Trait {
+    ///     fn foo(&self) where Self: Leak {}
+    /// }
+    ///
+    /// // instead of this:
+    /// trait Trait: Leak {
+    ///     fn foo(&self) {}
+    /// }
+    /// ```
+    /// It is not always possible to do this because of backward compatibility:
+    ///
+    /// ```ignore(illustrative)
+    /// pub trait Trait<Rhs = Self> {}
+    /// pub trait Trait1 : Trait {}
+    /// //~^ ERROR: `Rhs` requires `DefaultAutoTrait`, but `Self` is not `DefaultAutoTrait`
+    /// ```
+    ///
+    /// or:
+    ///
+    /// ```ignore(illustrative)
+    /// trait Trait {
+    ///     type Type where Self: Sized;
+    /// }
+    /// trait Trait2<T> : Trait<Type = T> {}
+    /// //~^ ERROR: `DefaultAutoTrait` required for `Trait2`, by implicit  `Self: DefaultAutoTrait` in `Trait::Type`
+    /// ```
+    ///
+    /// Therefore, `experimental_default_bounds` are still being added to supertraits if
+    /// the `SelfTyParam` or `AssocItemConstraint` were found in a trait header.
+    pub(crate) fn requires_default_supertraits(
+        &self,
         hir_bounds: &'tcx [hir::GenericBound<'tcx>],
+        hir_generics: &'tcx hir::Generics<'tcx>,
+    ) -> bool {
+        struct TraitInfoCollector;
+
+        impl<'tcx> hir::intravisit::Visitor<'tcx> for TraitInfoCollector {
+            type Result = ControlFlow<()>;
+
+            fn visit_assoc_item_constraint(
+                &mut self,
+                _constraint: &'tcx hir::AssocItemConstraint<'tcx>,
+            ) -> Self::Result {
+                ControlFlow::Break(())
+            }
+
+            fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result {
+                if matches!(
+                    &t.kind,
+                    hir::TyKind::Path(hir::QPath::Resolved(
+                        _,
+                        hir::Path { res: hir::def::Res::SelfTyParam { .. }, .. },
+                    ))
+                ) {
+                    return ControlFlow::Break(());
+                }
+                hir::intravisit::walk_ty(self, t)
+            }
+        }
+
+        let mut found = false;
+        for bound in hir_bounds {
+            found |= hir::intravisit::walk_param_bound(&mut TraitInfoCollector, bound).is_break();
+        }
+        found |= hir::intravisit::walk_generics(&mut TraitInfoCollector, hir_generics).is_break();
+        found
+    }
+
+    /// Lazily sets `experimental_default_bounds` to true on trait super bounds.
+    /// See `requires_default_supertraits` for more information.
+    pub(crate) fn add_default_super_traits(
+        &self,
+        trait_def_id: LocalDefId,
+        bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
+        hir_bounds: &'tcx [hir::GenericBound<'tcx>],
+        hir_generics: &'tcx hir::Generics<'tcx>,
+        span: Span,
+    ) {
+        assert!(matches!(self.tcx().def_kind(trait_def_id), DefKind::Trait | DefKind::TraitAlias));
+        if self.requires_default_supertraits(hir_bounds, hir_generics) {
+            let self_ty_where_predicates = (trait_def_id, hir_generics.predicates);
+            self.add_default_traits_with_filter(
+                bounds,
+                self.tcx().types.self_param,
+                hir_bounds,
+                Some(self_ty_where_predicates),
+                span,
+                |default_trait| default_trait != hir::LangItem::Sized,
+            );
+        }
+    }
+
+    pub(crate) fn add_default_traits_with_filter(
+        &self,
+        bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
+        self_ty: Ty<'tcx>,
+        hir_bounds: &[hir::GenericBound<'tcx>],
+        self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
+        span: Span,
+        f: impl Fn(hir::LangItem) -> bool,
+    ) {
+        self.tcx().default_traits().iter().filter(|&&default_trait| f(default_trait)).for_each(
+            |default_trait| {
+                self.add_default_trait(
+                    *default_trait,
+                    bounds,
+                    self_ty,
+                    hir_bounds,
+                    self_ty_where_predicates,
+                    span,
+                );
+            },
+        );
+    }
+
+    /// Add a `Sized` or `experimental_default_bounds` bounds to the `bounds` if appropriate.
+    ///
+    /// Doesn't add the bound if the HIR bounds contain any of `Trait`, `?Trait` or `!Trait`.
+    pub(crate) fn add_default_trait(
+        &self,
+        trait_: hir::LangItem,
+        bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
+        self_ty: Ty<'tcx>,
+        hir_bounds: &[hir::GenericBound<'tcx>],
         self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
         span: Span,
     ) {
+        let trait_id = self.tcx().lang_items().get(trait_);
+        if let Some(trait_id) = trait_id
+            && self.do_not_provide_default_trait_bound(
+                trait_id,
+                hir_bounds,
+                self_ty_where_predicates,
+            )
+        {
+            // There was no `?Trait` or `!Trait` bound;
+            // add `Trait` if it's available.
+            let trait_ref = ty::TraitRef::new(self.tcx(), trait_id, [self_ty]);
+            // Preferable to put this obligation first, since we report better errors for sized ambiguity.
+            bounds.insert(0, (trait_ref.upcast(self.tcx()), span));
+        }
+    }
+
+    fn do_not_provide_default_trait_bound<'a>(
+        &self,
+        trait_def_id: DefId,
+        hir_bounds: &'a [hir::GenericBound<'tcx>],
+        self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
+    ) -> bool {
         let tcx = self.tcx();
-        let sized_def_id = tcx.lang_items().sized_trait();
-        let mut seen_negative_sized_bound = false;
-        let mut seen_positive_sized_bound = false;
+        let mut seen_negative_bound = false;
+        let mut seen_positive_bound = false;
 
         // Try to find an unbound in bounds.
         let mut unbounds: SmallVec<[_; 1]> = SmallVec::new();
-        let mut search_bounds = |hir_bounds: &'tcx [hir::GenericBound<'tcx>]| {
+        let mut search_bounds = |hir_bounds: &'a [hir::GenericBound<'tcx>]| {
             for hir_bound in hir_bounds {
                 let hir::GenericBound::Trait(ptr) = hir_bound else {
                     continue;
@@ -50,17 +215,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 match ptr.modifiers.polarity {
                     hir::BoundPolarity::Maybe(_) => unbounds.push(ptr),
                     hir::BoundPolarity::Negative(_) => {
-                        if let Some(sized_def_id) = sized_def_id
-                            && ptr.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id)
-                        {
-                            seen_negative_sized_bound = true;
+                        if ptr.trait_ref.path.res == Res::Def(DefKind::Trait, trait_def_id) {
+                            seen_negative_bound = true;
                         }
                     }
                     hir::BoundPolarity::Positive => {
-                        if let Some(sized_def_id) = sized_def_id
-                            && ptr.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id)
-                        {
-                            seen_positive_sized_bound = true;
+                        if ptr.trait_ref.path.res == Res::Def(DefKind::Trait, trait_def_id) {
+                            seen_positive_bound = true;
                         }
                     }
                 }
@@ -95,32 +256,36 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             };
         }
 
-        let mut seen_sized_unbound = false;
+        let mut seen_unbound = false;
         for unbound in unbounds {
-            if let Some(sized_def_id) = sized_def_id
-                && unbound.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id)
-            {
-                seen_sized_unbound = true;
-                continue;
+            let unbound_def_id = unbound.trait_ref.trait_def_id();
+            if unbound_def_id == Some(trait_def_id) {
+                seen_unbound = true;
+            }
+            let emit_relax_err = || {
+                let unbound_traits =
+                    match self.tcx().sess.opts.unstable_opts.experimental_default_bounds {
+                        true => "`?Sized` and `experimental_default_bounds`",
+                        false => "`?Sized`",
+                    };
+                // There was a `?Trait` bound, but it was neither `?Sized` nor `experimental_default_bounds`.
+                tcx.dcx().span_err(
+                    unbound.span,
+                    format!(
+                        "relaxing a default bound only does something for {}; \
+                    all other traits are not bound by default",
+                        unbound_traits
+                    ),
+                );
+            };
+            match unbound_def_id {
+                Some(def_id) if !tcx.is_default_trait(def_id) => emit_relax_err(),
+                None => emit_relax_err(),
+                _ => {}
             }
-            // There was a `?Trait` bound, but it was not `?Sized`
-            self.dcx().span_err(
-                unbound.span,
-                "relaxing a default bound only does something for `?Sized`; \
-                all other traits are not bound by default",
-            );
         }
 
-        if seen_sized_unbound || seen_negative_sized_bound || seen_positive_sized_bound {
-            // There was in fact a `?Sized`, `!Sized` or explicit `Sized` bound;
-            // we don't need to do anything.
-        } else if let Some(sized_def_id) = sized_def_id {
-            // There was no `?Sized`, `!Sized` or explicit `Sized` bound;
-            // add `Sized` if it's available.
-            let trait_ref = ty::TraitRef::new(tcx, sized_def_id, [self_ty]);
-            // Preferable to put this obligation first, since we report better errors for sized ambiguity.
-            bounds.insert(0, (trait_ref.upcast(tcx), span));
-        }
+        !(seen_unbound || seen_negative_bound || seen_positive_bound)
     }
 
     /// Lower HIR bounds into `bounds` given the self type `param_ty` and the overarching late-bound vars if any.
@@ -684,7 +849,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             return Err(self.dcx().emit_err(crate::errors::ReturnTypeNotationOnNonRpitit {
                 span: path_span,
                 ty: tcx.liberate_late_bound_regions(item_def_id, output),
-                fn_span: tcx.hir().span_if_local(item_def_id),
+                fn_span: tcx.hir_span_if_local(item_def_id),
                 note: (),
             }));
         };
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 170500c7a16..ecb453bced0 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
@@ -32,7 +32,7 @@ pub(crate) fn validate_cmse_abi<'tcx>(
                         span,
                         ..
                     }) => *span,
-                    _ => tcx.hir().span(hir_id),
+                    _ => tcx.hir_span(hir_id),
                 };
                 struct_span_code_err!(
                     tcx.dcx(),
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs
index 27643e715e6..aeebe45f881 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs
@@ -57,6 +57,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             }
         }
 
+        let ast_bounds: Vec<_> =
+            hir_bounds.iter().map(|&trait_ref| hir::GenericBound::Trait(trait_ref)).collect();
+
+        self.add_default_traits_with_filter(
+            &mut user_written_bounds,
+            dummy_self,
+            &ast_bounds,
+            None,
+            span,
+            |tr| tr != hir::LangItem::Sized,
+        );
+
         let (elaborated_trait_bounds, elaborated_projection_bounds) =
             traits::expand_trait_aliases(tcx, user_written_bounds.iter().copied());
         let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = elaborated_trait_bounds
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 3b007c77198..6e9c178d33a 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -882,7 +882,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
             let rename_message = if is_shadowed { ", consider renaming it" } else { "" };
 
-            if let Some(sp) = tcx.hir().span_if_local(item.def_id) {
+            if let Some(sp) = tcx.hir_span_if_local(item.def_id) {
                 err.span_label(
                     sp,
                     format!("`{}{}` defined here{}", prefix, item.name, rename_message),
@@ -1381,7 +1381,7 @@ pub(crate) fn fn_trait_to_string(
         .find_map(|c| {
             if c.ident.name == sym::Output
                 && let Some(ty) = c.ty()
-                && ty.span != tcx.hir().span(trait_segment.hir_id)
+                && ty.span != tcx.hir_span(trait_segment.hir_id)
             {
                 tcx.sess.source_map().span_to_snippet(ty.span).ok()
             } else {
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
index 60a60f6415a..21f0f9648ea 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
@@ -92,7 +92,7 @@ fn generic_arg_mismatch_err(
             GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }),
             GenericParamDefKind::Const { .. },
         ) if tcx.type_of(param.def_id).skip_binder() == tcx.types.usize => {
-            let snippet = sess.source_map().span_to_snippet(tcx.hir().span(len.hir_id));
+            let snippet = sess.source_map().span_to_snippet(tcx.hir_span(len.hir_id));
             if let Ok(snippet) = snippet {
                 err.span_suggestion(
                     arg.span(),
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
index 55886312284..8e62dce2191 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
@@ -86,6 +86,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 "expected a type, found a trait"
             );
             if self_ty.span.can_be_used_for_suggestions()
+                && poly_trait_ref.trait_ref.trait_def_id().is_some()
                 && !self.maybe_suggest_impl_trait(self_ty, &mut diag)
                 && !self.maybe_suggest_dyn_trait(self_ty, sugg, &mut diag)
             {
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 76a880da418..b4a71edc118 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -838,7 +838,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 | PredicateFilter::SelfOnly
                 | PredicateFilter::SelfAndAssociatedTypeBounds => {
                     match constness {
-                        hir::BoundConstness::Always(span) => {
+                        hir::BoundConstness::Always(_) => {
                             if polarity == ty::PredicatePolarity::Positive {
                                 bounds.push((
                                     poly_trait_ref
@@ -864,7 +864,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 // in `lower_assoc_item_constraint`.
                 PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {
                     match constness {
-                        hir::BoundConstness::Maybe(span) => {
+                        hir::BoundConstness::Maybe(_) => {
                             if polarity == ty::PredicatePolarity::Positive {
                                 bounds.push((
                                     poly_trait_ref
@@ -1066,7 +1066,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 let bound_span = tcx
                     .associated_items(bound_id)
                     .find_by_name_and_kind(tcx, assoc_name, assoc_kind, bound_id)
-                    .and_then(|item| tcx.hir().span_if_local(item.def_id));
+                    .and_then(|item| tcx.hir_span_if_local(item.def_id));
 
                 if let Some(bound_span) = bound_span {
                     err.span_label(
@@ -1400,7 +1400,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         );
                     }
 
-                    if let Some(sp) = tcx.hir().span_if_local(adt_def.did()) {
+                    if let Some(sp) = tcx.hir_span_if_local(adt_def.did()) {
                         err.span_label(sp, format!("variant `{assoc_ident}` not found here"));
                     }
 
@@ -1730,25 +1730,23 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         .is_accessible_from(self.item_def_id(), tcx)
                     && tcx.all_impls(*trait_def_id)
                         .any(|impl_def_id| {
-                            let impl_header = tcx.impl_trait_header(impl_def_id);
-                            impl_header.is_some_and(|header| {
-                                let trait_ref = header.trait_ref.instantiate(
-                                    tcx,
-                                    infcx.fresh_args_for_item(DUMMY_SP, impl_def_id),
-                                );
-
-                                let value = fold_regions(tcx, qself_ty, |_, _| tcx.lifetimes.re_erased);
-                                // FIXME: Don't bother dealing with non-lifetime binders here...
-                                if value.has_escaping_bound_vars() {
-                                    return false;
-                                }
-                                infcx
-                                    .can_eq(
-                                        ty::ParamEnv::empty(),
-                                        trait_ref.self_ty(),
-                                        value,
-                                    ) && header.polarity != ty::ImplPolarity::Negative
-                            })
+                            let header = tcx.impl_trait_header(impl_def_id).unwrap();
+                            let trait_ref = header.trait_ref.instantiate(
+                                tcx,
+                                infcx.fresh_args_for_item(DUMMY_SP, impl_def_id),
+                            );
+
+                            let value = fold_regions(tcx, qself_ty, |_, _| tcx.lifetimes.re_erased);
+                            // FIXME: Don't bother dealing with non-lifetime binders here...
+                            if value.has_escaping_bound_vars() {
+                                return false;
+                            }
+                            infcx
+                                .can_eq(
+                                    ty::ParamEnv::empty(),
+                                    trait_ref.self_ty(),
+                                    value,
+                                ) && header.polarity != ty::ImplPolarity::Negative
                         })
             })
             .map(|trait_def_id| tcx.def_path_str(trait_def_id))
diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
index e27a81d4976..c01b81563dc 100644
--- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
@@ -170,7 +170,7 @@ fn diagnostic_hir_wf_check<'tcx>(
                 ..
             }) => vec![*ty],
             hir::Node::AnonConst(_) => {
-                if let Some(const_param_id) = tcx.hir().opt_const_param_default_param_def_id(hir_id)
+                if let Some(const_param_id) = tcx.hir_opt_const_param_default_param_def_id(hir_id)
                     && let hir::Node::GenericParam(hir::GenericParam {
                         kind: hir::GenericParamKind::Const { ty, .. },
                         ..
diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs
index 32b05dcc569..daa908c8c78 100644
--- a/compiler/rustc_hir_analysis/src/outlives/mod.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs
@@ -25,7 +25,7 @@ fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[(ty::Clau
         }
         DefKind::AnonConst if tcx.features().generic_const_exprs() => {
             let id = tcx.local_def_id_to_hir_id(item_def_id);
-            if tcx.hir().opt_const_param_default_param_def_id(id).is_some() {
+            if tcx.hir_opt_const_param_default_param_def_id(id).is_some() {
                 // In `generics_of` we set the generics' parent to be our parent's parent which means that
                 // we lose out on the predicates of our actual parent if we dont return those predicates here.
                 // (See comment in `generics_of` for more information on why the parent shenanigans is necessary)
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index ddaca89ccf8..8c0c17f7a7d 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -1193,7 +1193,8 @@ impl<'a> State<'a> {
         wth: hir::StructTailExpr<'_>,
     ) {
         self.print_qpath(qpath, true);
-        self.word("{");
+        self.nbsp();
+        self.word_space("{");
         self.commasep_cmnt(Consistent, fields, |s, field| s.print_expr_field(field), |f| f.span);
         match wth {
             hir::StructTailExpr::Base(expr) => {
@@ -1215,20 +1216,13 @@ impl<'a> State<'a> {
                 self.word("..");
                 self.end();
             }
-            hir::StructTailExpr::None => {
-                if !fields.is_empty() {
-                    self.word(",");
-                }
-            }
+            hir::StructTailExpr::None => {}
         }
-
+        self.space();
         self.word("}");
     }
 
     fn print_expr_field(&mut self, field: &hir::ExprField<'_>) {
-        if self.attrs(field.hir_id).is_empty() {
-            self.space();
-        }
         self.cbox(INDENT_UNIT);
         self.print_attrs_as_outer(self.attrs(field.hir_id));
         if !field.is_shorthand {
@@ -1277,18 +1271,18 @@ impl<'a> State<'a> {
         self.print_call_post(base_args)
     }
 
-    fn print_expr_binary(&mut self, op: hir::BinOp, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) {
-        let binop_prec = op.node.precedence();
+    fn print_expr_binary(&mut self, op: hir::BinOpKind, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) {
+        let binop_prec = op.precedence();
         let left_prec = lhs.precedence();
         let right_prec = rhs.precedence();
 
-        let (mut left_needs_paren, right_needs_paren) = match op.node.fixity() {
+        let (mut left_needs_paren, right_needs_paren) = match op.fixity() {
             Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec),
             Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec),
             Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec),
         };
 
-        match (&lhs.kind, op.node) {
+        match (&lhs.kind, op) {
             // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is
             // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead
             // of `(x as i32) < ...`. We need to convince it _not_ to do that.
@@ -1303,7 +1297,7 @@ impl<'a> State<'a> {
 
         self.print_expr_cond_paren(lhs, left_needs_paren);
         self.space();
-        self.word_space(op.node.as_str());
+        self.word_space(op.as_str());
         self.print_expr_cond_paren(rhs, right_needs_paren);
     }
 
@@ -1457,7 +1451,7 @@ impl<'a> State<'a> {
                 self.word(".use");
             }
             hir::ExprKind::Binary(op, lhs, rhs) => {
-                self.print_expr_binary(op, lhs, rhs);
+                self.print_expr_binary(op.node, lhs, rhs);
             }
             hir::ExprKind::Unary(op, expr) => {
                 self.print_expr_unary(op, expr);
@@ -1578,8 +1572,7 @@ impl<'a> State<'a> {
             hir::ExprKind::AssignOp(op, lhs, rhs) => {
                 self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign);
                 self.space();
-                self.word(op.node.as_str());
-                self.word_space("=");
+                self.word_space(op.node.as_str());
                 self.print_expr_cond_paren(rhs, rhs.precedence() < ExprPrecedence::Assign);
             }
             hir::ExprKind::Field(expr, ident) => {
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 5e2daa69628..02fd7367e2f 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -771,7 +771,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     format!("this {descr} returns an unsized value `{output_ty}`, so it cannot be called")
                 );
                 if let DefIdOrName::DefId(def_id) = maybe_def
-                    && let Some(def_span) = self.tcx.hir().span_if_local(def_id)
+                    && let Some(def_span) = self.tcx.hir_span_if_local(def_id)
                 {
                     err.span_label(def_span, "the callable type is defined here");
                 }
@@ -780,7 +780,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         }
 
-        if let Some(span) = self.tcx.hir().res_span(def) {
+        if let Some(span) = self.tcx.hir_res_span(def) {
             let callee_ty = callee_ty.to_string();
             let label = match (unit_variant, inner_callee_path) {
                 (Some((_, kind, path)), _) => {
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index a4776338f6c..467ca26e7ea 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -970,7 +970,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result);
 
         // Normalize only after registering in `user_provided_sigs`.
-        self.normalize(self.tcx.hir().span(hir_id), result)
+        self.normalize(self.tcx.hir_span(hir_id), result)
     }
 
     /// Invoked when we are translating the coroutine that results
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 4cbc42b99ea..f42ca3af2b3 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -41,8 +41,8 @@ use rustc_abi::ExternAbi;
 use rustc_attr_parsing::InlineAttr;
 use rustc_errors::codes::*;
 use rustc_errors::{Applicability, Diag, struct_span_code_err};
+use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::{self as hir, LangItem};
 use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
 use rustc_infer::infer::relate::RelateResult;
 use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
@@ -55,7 +55,7 @@ use rustc_middle::ty::adjustment::{
     Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion,
 };
 use rustc_middle::ty::error::TypeError;
-use rustc_middle::ty::{self, AliasTy, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span};
 use rustc_trait_selection::infer::InferCtxtExt as _;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
@@ -592,63 +592,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
 
         // Create an obligation for `Source: CoerceUnsized<Target>`.
         let cause = self.cause(self.cause.span, ObligationCauseCode::Coercion { source, target });
-        let root_obligation = Obligation::new(
-            self.tcx,
-            cause.clone(),
-            self.fcx.param_env,
-            ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]),
-        );
-
-        // If the root `Source: CoerceUnsized<Target>` obligation can't possibly hold,
-        // we don't have to assume that this is unsizing coercion (it will always lead to an error)
-        //
-        // However, we don't want to bail early all the time, since the unholdable obligations
-        // may be interesting for diagnostics (such as trying to coerce `&T` to `&dyn Id<This = U>`),
-        // so we only bail if there (likely) is another way to convert the types.
-        if !self.infcx.predicate_may_hold(&root_obligation) {
-            if let Some(dyn_metadata_adt_def_id) = self.tcx.lang_items().get(LangItem::DynMetadata)
-                && let Some(metadata_type_def_id) = self.tcx.lang_items().get(LangItem::Metadata)
-            {
-                self.probe(|_| {
-                    let ocx = ObligationCtxt::new(&self.infcx);
-
-                    // returns `true` if `<ty as Pointee>::Metadata` is `DynMetadata<_>`
-                    let has_dyn_trait_metadata = |ty| {
-                        let metadata_ty: Result<_, _> = ocx.structurally_normalize_ty(
-                            &ObligationCause::dummy(),
-                            self.fcx.param_env,
-                            Ty::new_alias(
-                                self.tcx,
-                                ty::AliasTyKind::Projection,
-                                AliasTy::new(self.tcx, metadata_type_def_id, [ty]),
-                            ),
-                        );
-
-                        metadata_ty.is_ok_and(|metadata_ty| {
-                            metadata_ty
-                                .ty_adt_def()
-                                .is_some_and(|d| d.did() == dyn_metadata_adt_def_id)
-                        })
-                    };
-
-                    // If both types are raw pointers to a (wrapper over a) trait object,
-                    // this might be a cast like `*const W<dyn Trait> -> *const dyn Trait`.
-                    // So it's better to bail and try that. (even if the cast is not possible, for
-                    // example due to vtables not matching, cast diagnostic will likely still be better)
-                    //
-                    // N.B. use `target`, not `coerce_target` (the latter is a var)
-                    if let &ty::RawPtr(source_pointee, _) = coerce_source.kind()
-                        && let &ty::RawPtr(target_pointee, _) = target.kind()
-                        && has_dyn_trait_metadata(source_pointee)
-                        && has_dyn_trait_metadata(target_pointee)
-                    {
-                        return Err(TypeError::Mismatch);
-                    }
-
-                    Ok(())
-                })?;
-            }
-        }
 
         // Use a FIFO queue for this custom fulfillment procedure.
         //
@@ -657,7 +600,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         // and almost never more than 3. By using a SmallVec we avoid an
         // allocation, at the (very small) cost of (occasionally) having to
         // shift subsequent elements down when removing the front element.
-        let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![root_obligation];
+        let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![Obligation::new(
+            self.tcx,
+            cause,
+            self.fcx.param_env,
+            ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target])
+        )];
 
         // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
         // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index 41bdd0ca43e..b845e2190ef 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -999,10 +999,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let container = with_no_trimmed_paths!(self.tcx.def_path_str(container_id));
         for def_id in pick.import_ids {
             let hir_id = self.tcx.local_def_id_to_hir_id(def_id);
-            path_span.push_span_label(
-                self.tcx.hir().span(hir_id),
-                format!("`{container}` imported here"),
-            );
+            path_span
+                .push_span_label(self.tcx.hir_span(hir_id), format!("`{container}` imported here"));
         }
         let tail = with_no_trimmed_paths!(match &other_methods_in_scope[..] {
             [] => return,
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index a75f6f4caac..45ab8e03db5 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -512,7 +512,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 self.check_expr_assign(expr, expected, lhs, rhs, span)
             }
             ExprKind::AssignOp(op, lhs, rhs) => {
-                self.check_expr_binop_assign(expr, op, lhs, rhs, expected)
+                self.check_expr_assign_op(expr, op, lhs, rhs, expected)
             }
             ExprKind::Unary(unop, oprnd) => self.check_expr_unop(unop, oprnd, expected, expr),
             ExprKind::AddrOf(kind, mutbl, oprnd) => {
@@ -3239,7 +3239,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Some(x) => self.tcx.local_def_id_to_hir_id(x),
             None => return,
         };
-        let param_span = self.tcx.hir().span(param_hir_id);
+        let param_span = self.tcx.hir_span(param_hir_id);
         let param_name = self.tcx.hir_ty_param_name(param_def_id.expect_local());
 
         err.span_label(param_span, format!("type parameter '{param_name}' declared here"));
diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
index c71a5ea8b97..64176dacb73 100644
--- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
@@ -1,6 +1,9 @@
 //! A different sort of visitor for walking fn bodies. Unlike the
 //! normal visitor, which just walks the entire body in one shot, the
 //! `ExprUseVisitor` determines how expressions are being used.
+//!
+//! In the compiler, this is only used for upvar inference, but there
+//! are many uses within clippy.
 
 use std::cell::{Ref, RefCell};
 use std::ops::Deref;
@@ -25,7 +28,7 @@ use rustc_middle::ty::{
 use rustc_middle::{bug, span_bug};
 use rustc_span::{ErrorGuaranteed, Span};
 use rustc_trait_selection::infer::InferCtxtExt;
-use tracing::{debug, trace};
+use tracing::{debug, instrument, trace};
 
 use crate::fn_ctxt::FnCtxt;
 
@@ -35,11 +38,8 @@ pub trait Delegate<'tcx> {
     /// The value found at `place` is moved, depending
     /// on `mode`. Where `diag_expr_id` is the id used for diagnostics for `place`.
     ///
-    /// Use of a `Copy` type in a ByValue context is considered a use
-    /// by `ImmBorrow` and `borrow` is called instead. This is because
-    /// a shared borrow is the "minimum access" that would be needed
-    /// to perform a copy.
-    ///
+    /// If the value is `Copy`, [`copy`][Self::copy] is called instead, which
+    /// by default falls back to [`borrow`][Self::borrow].
     ///
     /// The parameter `diag_expr_id` indicates the HIR id that ought to be used for
     /// diagnostics. Around pattern matching such as `let pat = expr`, the diagnostic
@@ -73,6 +73,10 @@ pub trait Delegate<'tcx> {
 
     /// The value found at `place` is being copied.
     /// `diag_expr_id` is the id used for diagnostics (see `consume` for more details).
+    ///
+    /// If an implementation is not provided, use of a `Copy` type in a ByValue context is instead
+    /// considered a use by `ImmBorrow` and `borrow` is called instead. This is because a shared
+    /// borrow is the "minimum access" that would be needed to perform a copy.
     fn copy(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
         // In most cases, copying data from `x` is equivalent to doing `*&x`, so by default
         // we treat a copy of `x` as a borrow of `x`.
@@ -141,6 +145,8 @@ impl<'tcx, D: Delegate<'tcx>> Delegate<'tcx> for &mut D {
     }
 }
 
+/// This trait makes `ExprUseVisitor` usable with both [`FnCtxt`]
+/// and [`LateContext`], depending on where in the compiler it is used.
 pub trait TypeInformationCtxt<'tcx> {
     type TypeckResults<'a>: Deref<Target = ty::TypeckResults<'tcx>>
     where
@@ -154,7 +160,7 @@ pub trait TypeInformationCtxt<'tcx> {
 
     fn try_structurally_resolve_type(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx>;
 
-    fn report_error(&self, span: Span, msg: impl ToString) -> Self::Error;
+    fn report_bug(&self, span: Span, msg: impl ToString) -> Self::Error;
 
     fn error_reported_in_ty(&self, ty: Ty<'tcx>) -> Result<(), Self::Error>;
 
@@ -189,7 +195,7 @@ impl<'tcx> TypeInformationCtxt<'tcx> for &FnCtxt<'_, 'tcx> {
         (**self).try_structurally_resolve_type(sp, ty)
     }
 
-    fn report_error(&self, span: Span, msg: impl ToString) -> Self::Error {
+    fn report_bug(&self, span: Span, msg: impl ToString) -> Self::Error {
         self.dcx().span_delayed_bug(span, msg.to_string())
     }
 
@@ -239,7 +245,7 @@ impl<'tcx> TypeInformationCtxt<'tcx> for (&LateContext<'tcx>, LocalDefId) {
         t
     }
 
-    fn report_error(&self, span: Span, msg: impl ToString) -> ! {
+    fn report_bug(&self, span: Span, msg: impl ToString) -> ! {
         span_bug!(span, "{}", msg.to_string())
     }
 
@@ -268,9 +274,9 @@ impl<'tcx> TypeInformationCtxt<'tcx> for (&LateContext<'tcx>, LocalDefId) {
     }
 }
 
-/// The ExprUseVisitor type
+/// A visitor that reports how each expression is being used.
 ///
-/// This is the code that actually walks the tree.
+/// See [module-level docs][self] and [`Delegate`] for details.
 pub struct ExprUseVisitor<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> {
     cx: Cx,
     /// We use a `RefCell` here so that delegates can mutate themselves, but we can
@@ -314,9 +320,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
         Ok(())
     }
 
+    #[instrument(skip(self), level = "debug")]
     fn consume_or_copy(&self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
-        debug!("delegate_consume(place_with_id={:?})", place_with_id);
-
         if self.cx.type_is_copy_modulo_regions(place_with_id.place.ty()) {
             self.delegate.borrow_mut().copy(place_with_id, diag_expr_id);
         } else {
@@ -324,9 +329,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
         }
     }
 
+    #[instrument(skip(self), level = "debug")]
     pub fn consume_clone_or_copy(&self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
-        debug!("delegate_consume_or_clone(place_with_id={:?})", place_with_id);
-
         // `x.use` will do one of the following
         // * if it implements `Copy`, it will be a copy
         // * if it implements `UseCloned`, it will be a call to `clone`
@@ -351,18 +355,16 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
     }
 
     // FIXME: It's suspicious that this is public; clippy should probably use `walk_expr`.
+    #[instrument(skip(self), level = "debug")]
     pub fn consume_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> {
-        debug!("consume_expr(expr={:?})", expr);
-
         let place_with_id = self.cat_expr(expr)?;
         self.consume_or_copy(&place_with_id, place_with_id.hir_id);
         self.walk_expr(expr)?;
         Ok(())
     }
 
+    #[instrument(skip(self), level = "debug")]
     pub fn consume_or_clone_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> {
-        debug!("consume_or_clone_expr(expr={:?})", expr);
-
         let place_with_id = self.cat_expr(expr)?;
         self.consume_clone_or_copy(&place_with_id, place_with_id.hir_id);
         self.walk_expr(expr)?;
@@ -376,17 +378,15 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
         Ok(())
     }
 
+    #[instrument(skip(self), level = "debug")]
     fn borrow_expr(&self, expr: &hir::Expr<'_>, bk: ty::BorrowKind) -> Result<(), Cx::Error> {
-        debug!("borrow_expr(expr={:?}, bk={:?})", expr, bk);
-
         let place_with_id = self.cat_expr(expr)?;
         self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk);
         self.walk_expr(expr)
     }
 
+    #[instrument(skip(self), level = "debug")]
     pub fn walk_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> {
-        debug!("walk_expr(expr={:?})", expr);
-
         self.walk_adjustment(expr)?;
 
         match expr.kind {
@@ -733,9 +733,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
 
     /// Indicates that the value of `blk` will be consumed, meaning either copied or moved
     /// depending on its type.
+    #[instrument(skip(self), level = "debug")]
     fn walk_block(&self, blk: &hir::Block<'_>) -> Result<(), Cx::Error> {
-        debug!("walk_block(blk.hir_id={})", blk.hir_id);
-
         for stmt in blk.stmts {
             self.walk_stmt(stmt)?;
         }
@@ -861,7 +860,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
     }
 
     /// Walks the autoref `autoref` applied to the autoderef'd
-    /// `expr`. `base_place` is the mem-categorized form of `expr`
+    /// `expr`. `base_place` is `expr` represented as a place,
     /// after all relevant autoderefs have occurred.
     fn walk_autoref(
         &self,
@@ -942,14 +941,13 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
     }
 
     /// The core driver for walking a pattern
+    #[instrument(skip(self), level = "debug")]
     fn walk_pat(
         &self,
         discr_place: &PlaceWithHirId<'tcx>,
         pat: &hir::Pat<'_>,
         has_guard: bool,
     ) -> Result<(), Cx::Error> {
-        debug!("walk_pat(discr_place={:?}, pat={:?}, has_guard={:?})", discr_place, pat, has_guard);
-
         let tcx = self.cx.tcx();
         self.cat_pattern(discr_place.clone(), pat, &mut |place, pat| {
             match pat.kind {
@@ -1042,6 +1040,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
     ///
     /// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing
     /// closure as the DefId.
+    #[instrument(skip(self), level = "debug")]
     fn walk_captures(&self, closure_expr: &hir::Closure<'_>) -> Result<(), Cx::Error> {
         fn upvar_is_local_variable(
             upvars: Option<&FxIndexMap<HirId, hir::Upvar>>,
@@ -1051,8 +1050,6 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
             upvars.map(|upvars| !upvars.contains_key(&upvar_id)).unwrap_or(body_owner_is_closure)
         }
 
-        debug!("walk_captures({:?})", closure_expr);
-
         let tcx = self.cx.tcx();
         let closure_def_id = closure_expr.def_id;
         // For purposes of this function, coroutine and closures are equivalent.
@@ -1164,55 +1161,17 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
     }
 }
 
-/// The job of the categorization methods is to analyze an expression to
-/// determine what kind of memory is used in evaluating it (for example,
-/// where dereferences occur and what kind of pointer is dereferenced;
-/// whether the memory is mutable, etc.).
-///
-/// Categorization effectively transforms all of our expressions into
-/// expressions of the following forms (the actual enum has many more
-/// possibilities, naturally, but they are all variants of these base
-/// forms):
-/// ```ignore (not-rust)
-/// E = rvalue    // some computed rvalue
-///   | x         // address of a local variable or argument
-///   | *E        // deref of a ptr
-///   | E.comp    // access to an interior component
-/// ```
-/// Imagine a routine ToAddr(Expr) that evaluates an expression and returns an
-/// address where the result is to be found. If Expr is a place, then this
-/// is the address of the place. If `Expr` is an rvalue, this is the address of
-/// some temporary spot in memory where the result is stored.
-///
-/// Now, `cat_expr()` classifies the expression `Expr` and the address `A = ToAddr(Expr)`
-/// as follows:
-///
-/// - `cat`: what kind of expression was this? This is a subset of the
-///   full expression forms which only includes those that we care about
-///   for the purpose of the analysis.
-/// - `mutbl`: mutability of the address `A`.
-/// - `ty`: the type of data found at the address `A`.
-///
-/// The resulting categorization tree differs somewhat from the expressions
-/// themselves. For example, auto-derefs are explicit. Also, an index `a[b]` is
-/// decomposed into two operations: a dereference to reach the array data and
-/// then an index to jump forward to the relevant item.
+/// The job of the methods whose name starts with `cat_` is to analyze
+/// expressions and construct the corresponding [`Place`]s. The `cat`
+/// stands for "categorize", this is a leftover from long ago when
+/// places were called "categorizations".
 ///
-/// ## By-reference upvars
-///
-/// One part of the codegen which may be non-obvious is that we translate
-/// closure upvars into the dereference of a borrowed pointer; this more closely
-/// resembles the runtime codegen. So, for example, if we had:
-///
-///     let mut x = 3;
-///     let y = 5;
-///     let inc = || x += y;
-///
-/// Then when we categorize `x` (*within* the closure) we would yield a
-/// result of `*x'`, effectively, where `x'` is a `Categorization::Upvar` reference
-/// tied to `x`. The type of `x'` will be a borrowed pointer.
+/// Note that a [`Place`] differs somewhat from the expression itself. For
+/// example, auto-derefs are explicit. Also, an index `a[b]` is decomposed into
+/// two operations: a dereference to reach the array data and then an index to
+/// jump forward to the relevant item.
 impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx, Cx, D> {
-    fn resolve_type_vars_or_error(
+    fn resolve_type_vars_or_bug(
         &self,
         id: HirId,
         ty: Option<Ty<'tcx>>,
@@ -1222,10 +1181,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
                 let ty = self.cx.resolve_vars_if_possible(ty);
                 self.cx.error_reported_in_ty(ty)?;
                 if ty.is_ty_var() {
-                    debug!("resolve_type_vars_or_error: infer var from {:?}", ty);
-                    Err(self
-                        .cx
-                        .report_error(self.cx.tcx().hir().span(id), "encountered type variable"))
+                    debug!("resolve_type_vars_or_bug: infer var from {:?}", ty);
+                    Err(self.cx.report_bug(self.cx.tcx().hir_span(id), "encountered type variable"))
                 } else {
                     Ok(ty)
                 }
@@ -1233,24 +1190,21 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
             None => {
                 // FIXME: We shouldn't be relying on the infcx being tainted.
                 self.cx.tainted_by_errors()?;
-                bug!(
-                    "no type for node {} in mem_categorization",
-                    self.cx.tcx().hir_id_to_string(id)
-                );
+                bug!("no type for node {} in ExprUseVisitor", self.cx.tcx().hir_id_to_string(id));
             }
         }
     }
 
     fn node_ty(&self, hir_id: HirId) -> Result<Ty<'tcx>, Cx::Error> {
-        self.resolve_type_vars_or_error(hir_id, self.cx.typeck_results().node_type_opt(hir_id))
+        self.resolve_type_vars_or_bug(hir_id, self.cx.typeck_results().node_type_opt(hir_id))
     }
 
     fn expr_ty(&self, expr: &hir::Expr<'_>) -> Result<Ty<'tcx>, Cx::Error> {
-        self.resolve_type_vars_or_error(expr.hir_id, self.cx.typeck_results().expr_ty_opt(expr))
+        self.resolve_type_vars_or_bug(expr.hir_id, self.cx.typeck_results().expr_ty_opt(expr))
     }
 
     fn expr_ty_adjusted(&self, expr: &hir::Expr<'_>) -> Result<Ty<'tcx>, Cx::Error> {
-        self.resolve_type_vars_or_error(
+        self.resolve_type_vars_or_bug(
             expr.hir_id,
             self.cx.typeck_results().expr_ty_adjusted_opt(expr),
         )
@@ -1285,7 +1239,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
         self.pat_ty_unadjusted(pat)
     }
 
-    /// Like `TypeckResults::pat_ty`, but ignores implicit `&` patterns.
+    /// Like [`Self::pat_ty_adjusted`], but ignores implicit `&` patterns.
     fn pat_ty_unadjusted(&self, pat: &hir::Pat<'_>) -> Result<Ty<'tcx>, Cx::Error> {
         let base_ty = self.node_ty(pat.hir_id)?;
         trace!(?base_ty);
@@ -1315,7 +1269,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
                             debug!("By-ref binding of non-derefable type");
                             Err(self
                                 .cx
-                                .report_error(pat.span, "by-ref binding of non-derefable type"))
+                                .report_bug(pat.span, "by-ref binding of non-derefable type"))
                         }
                     }
                 } else {
@@ -1511,7 +1465,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
                 }
             }
 
-            def => span_bug!(span, "unexpected definition in memory categorization: {:?}", def),
+            def => span_bug!(span, "unexpected definition in ExprUseVisitor: {:?}", def),
         }
     }
 
@@ -1553,10 +1507,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
         if node_ty != place_ty
             && self
                 .cx
-                .try_structurally_resolve_type(
-                    self.cx.tcx().hir().span(base_place.hir_id),
-                    place_ty,
-                )
+                .try_structurally_resolve_type(self.cx.tcx().hir_span(base_place.hir_id), place_ty)
                 .is_impl_trait()
         {
             projections.push(Projection { kind: ProjectionKind::OpaqueCast, ty: node_ty });
@@ -1595,17 +1546,14 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
         let base_curr_ty = base_place.place.ty();
         let deref_ty = match self
             .cx
-            .try_structurally_resolve_type(
-                self.cx.tcx().hir().span(base_place.hir_id),
-                base_curr_ty,
-            )
+            .try_structurally_resolve_type(self.cx.tcx().hir_span(base_place.hir_id), base_curr_ty)
             .builtin_deref(true)
         {
             Some(ty) => ty,
             None => {
                 debug!("explicit deref of non-derefable type: {:?}", base_curr_ty);
-                return Err(self.cx.report_error(
-                    self.cx.tcx().hir().span(node),
+                return Err(self.cx.report_bug(
+                    self.cx.tcx().hir_span(node),
                     "explicit deref of non-derefable type",
                 ));
             }
@@ -1629,7 +1577,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
         let ty::Adt(adt_def, _) = self.cx.try_structurally_resolve_type(span, ty).kind() else {
             return Err(self
                 .cx
-                .report_error(span, "struct or tuple struct pattern not applied to an ADT"));
+                .report_bug(span, "struct or tuple struct pattern not applied to an ADT"));
         };
 
         match res {
@@ -1675,7 +1623,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
         let ty = self.cx.typeck_results().node_type(pat_hir_id);
         match self.cx.try_structurally_resolve_type(span, ty).kind() {
             ty::Tuple(args) => Ok(args.len()),
-            _ => Err(self.cx.report_error(span, "tuple pattern not applied to a tuple")),
+            _ => Err(self.cx.report_bug(span, "tuple pattern not applied to a tuple")),
         }
     }
 
@@ -1854,7 +1802,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
                     debug!("explicit index of non-indexable type {:?}", place_with_id);
                     return Err(self
                         .cx
-                        .report_error(pat.span, "explicit index of non-indexable type"));
+                        .report_bug(pat.span, "explicit index of non-indexable type"));
                 };
                 let elt_place = self.cat_projection(
                     pat.hir_id,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index d75c2853ba0..91190a32ff5 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -157,7 +157,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // Lots of that diagnostics code relies on subtle effects of re-lowering, so we'll
                 // let it keep doing that and just ensure that compilation won't succeed.
                 self.dcx().span_delayed_bug(
-                    self.tcx.hir().span(id),
+                    self.tcx.hir_span(id),
                     format!("`{prev}` overridden by `{ty}` for {id:?} in {:?}", self.body_id),
                 );
             }
@@ -532,7 +532,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let ct = self.lowerer().lower_const_arg(const_arg, feed);
         self.register_wf_obligation(
             ct.into(),
-            self.tcx.hir().span(const_arg.hir_id),
+            self.tcx.hir_span(const_arg.hir_id),
             ObligationCauseCode::WellFormed(None),
         );
         ct
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 264719ca569..912098c4e2d 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -351,7 +351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         if self.suggest_fn_call(err, expr, found, |output| self.may_coerce(output, expected))
             && let ty::FnDef(def_id, ..) = *found.kind()
-            && let Some(sp) = self.tcx.hir().span_if_local(def_id)
+            && let Some(sp) = self.tcx.hir_span_if_local(def_id)
         {
             let name = self.tcx.item_name(def_id);
             let kind = self.tcx.def_kind(def_id);
@@ -407,7 +407,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))]
                     };
                     let struct_pat_shorthand_field =
-                        self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr);
+                        self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr);
                     if let Some(name) = struct_pat_shorthand_field {
                         sugg.insert(0, (expr.span.shrink_to_lo(), format!("{name}: ")));
                     }
@@ -449,7 +449,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             });
 
             let prefix_wrap = |sugg: &str| {
-                if let Some(name) = self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
+                if let Some(name) = self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
                     format!(": {}{}", name, sugg)
                 } else {
                     sugg.to_string()
@@ -671,7 +671,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         }
                         _ => {
                             let prefix = if let Some(name) =
-                                self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr)
+                                self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr)
                             {
                                 format!("{}: ", name)
                             } else {
@@ -1153,7 +1153,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
         if can_return
             && let Some(span) = expr.span.find_ancestor_inside(
-                self.tcx.hir().span_with_body(self.tcx.local_def_id_to_hir_id(fn_id)),
+                self.tcx.hir_span_with_body(self.tcx.local_def_id_to_hir_id(fn_id)),
             )
         {
             // When the expr is in a match arm's body, we shouldn't add semicolon ';' at the end.
@@ -1275,7 +1275,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 )
                 .must_apply_modulo_regions()
         {
-            let suggestion = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
+            let suggestion = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
                 Some(ident) => format!(": {ident}.clone()"),
                 None => ".clone()".to_string(),
             };
@@ -1381,7 +1381,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     (span.shrink_to_hi(), ").into()".to_owned()),
                 ]
             };
-            if let Some(name) = self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
+            if let Some(name) = self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
                 sugg.insert(0, (expr.span.shrink_to_lo(), format!("{}: ", name)));
             }
             diag.multipart_suggestion(
@@ -1436,7 +1436,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return false;
         }
 
-        let suggestion = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
+        let suggestion = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
             Some(ident) => format!(": {ident}.is_some()"),
             None => ".is_some()".to_string(),
         };
@@ -2032,7 +2032,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             )
         };
 
-        let sugg = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
+        let sugg = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
             Some(ident) => format!(": {ident}{sugg}"),
             None => sugg.to_string(),
         };
@@ -2289,7 +2289,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // Unroll desugaring, to make sure this works for `for` loops etc.
                 loop {
                     parent = self.tcx.parent_hir_id(id);
-                    let parent_span = self.tcx.hir().span(parent);
+                    let parent_span = self.tcx.hir_span(parent);
                     if parent_span.find_ancestor_inside(expr.span).is_some() {
                         // The parent node is part of the same span, so is the result of the
                         // same expansion/desugaring and not the 'real' parent node.
@@ -2378,7 +2378,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 .collect();
 
             let suggestions_for = |variant: &_, ctor_kind, field_name| {
-                let prefix = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
+                let prefix = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
                     Some(ident) => format!("{ident}: "),
                     None => String::new(),
                 };
@@ -2700,8 +2700,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         ));
                     }
 
-                    let prefix = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr)
-                    {
+                    let prefix = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
                         Some(ident) => format!("{ident}: "),
                         None => String::new(),
                     };
@@ -2911,7 +2910,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         };
 
                         let prefix =
-                            match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
+                            match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
                                 Some(ident) => format!("{ident}: "),
                                 None => String::new(),
                             };
@@ -3478,30 +3477,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         lhs_ty: Ty<'tcx>,
         rhs_expr: &'tcx hir::Expr<'tcx>,
         lhs_expr: &'tcx hir::Expr<'tcx>,
-        op: hir::BinOp,
     ) {
-        match op.node {
-            hir::BinOpKind::Eq => {
-                if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait()
-                    && self
-                        .infcx
-                        .type_implements_trait(partial_eq_def_id, [rhs_ty, lhs_ty], self.param_env)
-                        .must_apply_modulo_regions()
-                {
-                    let sm = self.tcx.sess.source_map();
-                    if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span)
-                        && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span)
-                    {
-                        err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`"));
-                        err.multipart_suggestion(
-                            "consider swapping the equality",
-                            vec![(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)],
-                            Applicability::MaybeIncorrect,
-                        );
-                    }
-                }
+        if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait()
+            && self
+                .infcx
+                .type_implements_trait(partial_eq_def_id, [rhs_ty, lhs_ty], self.param_env)
+                .must_apply_modulo_regions()
+        {
+            let sm = self.tcx.sess.source_map();
+            if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span)
+                && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span)
+            {
+                err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`"));
+                err.multipart_suggestion(
+                    "consider swapping the equality",
+                    vec![(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)],
+                    Applicability::MaybeIncorrect,
+                );
             }
-            _ => {}
         }
     }
 }
diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs
index 54e9e699353..194e420b606 100644
--- a/compiler/rustc_hir_typeck/src/intrinsicck.rs
+++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs
@@ -45,7 +45,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub(crate) fn check_transmute(&self, from: Ty<'tcx>, to: Ty<'tcx>, hir_id: HirId) {
         let tcx = self.tcx;
         let dl = &tcx.data_layout;
-        let span = tcx.hir().span(hir_id);
+        let span = tcx.hir_span(hir_id);
         let normalize = |ty| {
             let ty = self.resolve_vars_if_possible(ty);
             if let Ok(ty) =
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 4968998fd51..46389668de7 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -117,7 +117,7 @@ fn typeck_with_inspect<'tcx>(
 
     let id = tcx.local_def_id_to_hir_id(def_id);
     let node = tcx.hir_node(id);
-    let span = tcx.hir().span(id);
+    let span = tcx.hir_span(id);
 
     // Figure out what primary body this item has.
     let body_id = node.body_id().unwrap_or_else(|| {
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 908c3ee2eb8..246b23f11b6 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -254,11 +254,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     item_name
                 );
                 err.span_label(item_name.span, format!("private {kind}"));
-                let sp = self
-                    .tcx
-                    .hir()
-                    .span_if_local(def_id)
-                    .unwrap_or_else(|| self.tcx.def_span(def_id));
+                let sp =
+                    self.tcx.hir_span_if_local(def_id).unwrap_or_else(|| self.tcx.def_span(def_id));
                 err.span_label(sp, format!("private {kind} defined here"));
                 if let Some(within_macro_span) = within_macro_span {
                     err.span_label(within_macro_span, "due to this macro variable");
@@ -566,7 +563,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     span.push_span_label(sugg_let.span,
                             format!("`{rcvr_name}` of type `{self_ty}` that has method `{method_name}` defined earlier here"));
                     span.push_span_label(
-                        self.tcx.hir().span(recv_id),
+                        self.tcx.hir_span(recv_id),
                         format!(
                             "earlier `{rcvr_name}` shadowed here with type `{ty_str_reported}`"
                         ),
@@ -2561,7 +2558,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 ExprKind::Path(QPath::Resolved(_, path)) => {
                     // local binding
                     if let hir::def::Res::Local(hir_id) = path.res {
-                        let span = tcx.hir().span(hir_id);
+                        let span = tcx.hir_span(hir_id);
                         let filename = tcx.sess.source_map().span_to_filename(span);
 
                         let parent_node = self.tcx.parent_hir_node(hir_id);
diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs
index a473e14b244..93f77b8409f 100644
--- a/compiler/rustc_hir_typeck/src/op.rs
+++ b/compiler/rustc_hir_typeck/src/op.rs
@@ -4,15 +4,15 @@ use rustc_data_structures::packed::Pu128;
 use rustc_errors::codes::*;
 use rustc_errors::{Applicability, Diag, struct_span_code_err};
 use rustc_infer::traits::ObligationCauseCode;
+use rustc_middle::bug;
 use rustc_middle::ty::adjustment::{
     Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
 };
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
-use rustc_middle::{bug, span_bug};
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::source_map::Spanned;
-use rustc_span::{Ident, Span, sym};
+use rustc_span::{Ident, Span, Symbol, sym};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::{FulfillmentError, Obligation, ObligationCtxt};
 use tracing::debug;
@@ -24,24 +24,27 @@ use crate::Expectation;
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Checks a `a <op>= b`
-    pub(crate) fn check_expr_binop_assign(
+    pub(crate) fn check_expr_assign_op(
         &self,
         expr: &'tcx hir::Expr<'tcx>,
-        op: hir::BinOp,
+        op: hir::AssignOp,
         lhs: &'tcx hir::Expr<'tcx>,
         rhs: &'tcx hir::Expr<'tcx>,
         expected: Expectation<'tcx>,
     ) -> Ty<'tcx> {
         let (lhs_ty, rhs_ty, return_ty) =
-            self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes, expected);
-
-        let ty =
-            if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
-                self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, op);
-                self.tcx.types.unit
-            } else {
-                return_ty
-            };
+            self.check_overloaded_binop(expr, lhs, rhs, Op::AssignOp(op), expected);
+
+        let category = BinOpCategory::from(op.node);
+        let ty = if !lhs_ty.is_ty_var()
+            && !rhs_ty.is_ty_var()
+            && is_builtin_binop(lhs_ty, rhs_ty, category)
+        {
+            self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, category);
+            self.tcx.types.unit
+        } else {
+            return_ty
+        };
 
         self.check_lhs_assignable(lhs, E0067, op.span, |err| {
             if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
@@ -49,7 +52,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .lookup_op_method(
                         (lhs, lhs_deref_ty),
                         Some((rhs, rhs_ty)),
-                        Op::Binary(op, IsAssign::Yes),
+                        lang_item_for_binop(self.tcx, Op::AssignOp(op)),
+                        op.span,
                         expected,
                     )
                     .is_ok()
@@ -60,7 +64,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         .lookup_op_method(
                             (lhs, lhs_ty),
                             Some((rhs, rhs_ty)),
-                            Op::Binary(op, IsAssign::Yes),
+                            lang_item_for_binop(self.tcx, Op::AssignOp(op)),
+                            op.span,
                             expected,
                         )
                         .is_err()
@@ -98,7 +103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             expr.hir_id, expr, op, lhs_expr, rhs_expr
         );
 
-        match BinOpCategory::from(op) {
+        match BinOpCategory::from(op.node) {
             BinOpCategory::Shortcircuit => {
                 // && and || are a simple case.
                 self.check_expr_coercible_to_type(lhs_expr, tcx.types.bool, None);
@@ -114,14 +119,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // Otherwise, we always treat operators as if they are
                 // overloaded. This is the way to be most flexible w/r/t
                 // types that get inferred.
-                let (lhs_ty, rhs_ty, return_ty) = self.check_overloaded_binop(
-                    expr,
-                    lhs_expr,
-                    rhs_expr,
-                    op,
-                    IsAssign::No,
-                    expected,
-                );
+                let (lhs_ty, rhs_ty, return_ty) =
+                    self.check_overloaded_binop(expr, lhs_expr, rhs_expr, Op::BinOp(op), expected);
 
                 // Supply type inference hints if relevant. Probably these
                 // hints should be enforced during select as part of the
@@ -135,16 +134,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // deduce that the result type should be `u32`, even
                 // though we don't know yet what type 2 has and hence
                 // can't pin this down to a specific impl.
+                let category = BinOpCategory::from(op.node);
                 if !lhs_ty.is_ty_var()
                     && !rhs_ty.is_ty_var()
-                    && is_builtin_binop(lhs_ty, rhs_ty, op)
+                    && is_builtin_binop(lhs_ty, rhs_ty, category)
                 {
                     let builtin_return_ty = self.enforce_builtin_binop_types(
                         lhs_expr.span,
                         lhs_ty,
                         rhs_expr.span,
                         rhs_ty,
-                        op,
+                        category,
                     );
                     self.demand_eqtype(expr.span, builtin_return_ty, return_ty);
                     builtin_return_ty
@@ -161,16 +161,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         lhs_ty: Ty<'tcx>,
         rhs_span: Span,
         rhs_ty: Ty<'tcx>,
-        op: hir::BinOp,
+        category: BinOpCategory,
     ) -> Ty<'tcx> {
-        debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
+        debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, category));
 
         // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
         // (See https://github.com/rust-lang/rust/issues/57447.)
         let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty));
 
         let tcx = self.tcx;
-        match BinOpCategory::from(op) {
+        match category {
             BinOpCategory::Shortcircuit => {
                 self.demand_suptype(lhs_span, tcx.types.bool, lhs_ty);
                 self.demand_suptype(rhs_span, tcx.types.bool, rhs_ty);
@@ -201,17 +201,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expr: &'tcx hir::Expr<'tcx>,
         lhs_expr: &'tcx hir::Expr<'tcx>,
         rhs_expr: &'tcx hir::Expr<'tcx>,
-        op: hir::BinOp,
-        is_assign: IsAssign,
+        op: Op,
         expected: Expectation<'tcx>,
     ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
-        debug!(
-            "check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})",
-            expr.hir_id, op, is_assign
-        );
+        debug!("check_overloaded_binop(expr.hir_id={}, op={:?})", expr.hir_id, op);
 
-        let lhs_ty = match is_assign {
-            IsAssign::No => {
+        let lhs_ty = match op {
+            Op::BinOp(_) => {
                 // Find a suitable supertype of the LHS expression's type, by coercing to
                 // a type variable, to pass as the `Self` to the trait, avoiding invariant
                 // trait matching creating lifetime constraints that are too strict.
@@ -221,7 +217,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let fresh_var = self.next_ty_var(lhs_expr.span);
                 self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No)
             }
-            IsAssign::Yes => {
+            Op::AssignOp(_) => {
                 // rust-lang/rust#52126: We have to use strict
                 // equivalence on the LHS of an assign-op like `+=`;
                 // overwritten or mutably-borrowed places cannot be
@@ -242,7 +238,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let result = self.lookup_op_method(
             (lhs_expr, lhs_ty),
             Some((rhs_expr, rhs_ty_var)),
-            Op::Binary(op, is_assign),
+            lang_item_for_binop(self.tcx, op),
+            op.span(),
             expected,
         );
 
@@ -252,15 +249,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             rhs_ty_var,
             Some(lhs_expr),
             |err, ty| {
-                self.suggest_swapping_lhs_and_rhs(err, ty, lhs_ty, rhs_expr, lhs_expr, op);
+                if let Op::BinOp(binop) = op
+                    && binop.node == hir::BinOpKind::Eq
+                {
+                    self.suggest_swapping_lhs_and_rhs(err, ty, lhs_ty, rhs_expr, lhs_expr);
+                }
             },
         );
         let rhs_ty = self.resolve_vars_with_obligations(rhs_ty);
 
         let return_ty = match result {
             Ok(method) => {
-                let by_ref_binop = !op.node.is_by_value();
-                if is_assign == IsAssign::Yes || by_ref_binop {
+                let by_ref_binop = !op.is_by_value();
+                if matches!(op, Op::AssignOp(_)) || by_ref_binop {
                     if let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() {
                         let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes);
                         let autoref = Adjustment {
@@ -301,32 +302,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 Ty::new_misc_error(self.tcx)
             }
             Err(errors) => {
-                let (_, trait_def_id) =
-                    lang_item_for_op(self.tcx, Op::Binary(op, is_assign), op.span);
+                let (_, trait_def_id) = lang_item_for_binop(self.tcx, op);
                 let missing_trait = trait_def_id
                     .map(|def_id| with_no_trimmed_paths!(self.tcx.def_path_str(def_id)));
                 let mut path = None;
                 let lhs_ty_str = self.tcx.short_string(lhs_ty, &mut path);
                 let rhs_ty_str = self.tcx.short_string(rhs_ty, &mut path);
-                let (mut err, output_def_id) = match is_assign {
-                    IsAssign::Yes => {
+                let (mut err, output_def_id) = match op {
+                    Op::AssignOp(assign_op) => {
+                        let s = assign_op.node.as_str();
                         let mut err = struct_span_code_err!(
                             self.dcx(),
                             expr.span,
                             E0368,
-                            "binary assignment operation `{}=` cannot be applied to type `{}`",
-                            op.node.as_str(),
+                            "binary assignment operation `{}` cannot be applied to type `{}`",
+                            s,
                             lhs_ty_str,
                         );
                         err.span_label(
                             lhs_expr.span,
-                            format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty_str),
+                            format!("cannot use `{}` on type `{}`", s, lhs_ty_str),
                         );
                         self.note_unmet_impls_on_type(&mut err, errors, false);
                         (err, None)
                     }
-                    IsAssign::No => {
-                        let message = match op.node {
+                    Op::BinOp(bin_op) => {
+                        let message = match bin_op.node {
                             hir::BinOpKind::Add => {
                                 format!("cannot add `{rhs_ty_str}` to `{lhs_ty_str}`")
                             }
@@ -362,8 +363,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             }
                             _ => format!(
                                 "binary operation `{}` cannot be applied to type `{}`",
-                                op.node.as_str(),
-                                lhs_ty_str,
+                                bin_op.node.as_str(),
+                                lhs_ty_str
                             ),
                         };
                         let output_def_id = trait_def_id.and_then(|def_id| {
@@ -376,7 +377,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 .cloned()
                         });
                         let mut err =
-                            struct_span_code_err!(self.dcx(), op.span, E0369, "{message}");
+                            struct_span_code_err!(self.dcx(), bin_op.span, E0369, "{message}");
                         if !lhs_expr.span.eq(&rhs_expr.span) {
                             err.span_label(lhs_expr.span, lhs_ty_str.clone());
                             err.span_label(rhs_expr.span, rhs_ty_str);
@@ -409,18 +410,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         .lookup_op_method(
                             (lhs_expr, lhs_deref_ty),
                             Some((rhs_expr, rhs_ty)),
-                            Op::Binary(op, is_assign),
+                            lang_item_for_binop(self.tcx, op),
+                            op.span(),
                             expected,
                         )
                         .is_ok()
                     {
                         let msg = format!(
-                            "`{}{}` can be used on `{}` if you dereference the left-hand side",
-                            op.node.as_str(),
-                            match is_assign {
-                                IsAssign::Yes => "=",
-                                IsAssign::No => "",
-                            },
+                            "`{}` can be used on `{}` if you dereference the left-hand side",
+                            op.as_str(),
                             self.tcx.short_string(lhs_deref_ty, err.long_ty_path()),
                         );
                         err.span_suggestion_verbose(
@@ -442,14 +440,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             .lookup_op_method(
                                 (lhs_expr, lhs_adjusted_ty),
                                 Some((rhs_expr, rhs_adjusted_ty)),
-                                Op::Binary(op, is_assign),
+                                lang_item_for_binop(self.tcx, op),
+                                op.span(),
                                 expected,
                             )
                             .is_ok()
                         {
                             let lhs = self.tcx.short_string(lhs_adjusted_ty, err.long_ty_path());
                             let rhs = self.tcx.short_string(rhs_adjusted_ty, err.long_ty_path());
-                            let op = op.node.as_str();
+                            let op = op.as_str();
                             err.note(format!("an implementation for `{lhs} {op} {rhs}` exists"));
 
                             if let Some(lhs_new_mutbl) = lhs_new_mutbl
@@ -499,7 +498,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     self.lookup_op_method(
                         (lhs_expr, lhs_ty),
                         Some((rhs_expr, rhs_ty)),
-                        Op::Binary(op, is_assign),
+                        lang_item_for_binop(self.tcx, op),
+                        op.span(),
                         expected,
                     )
                     .is_ok()
@@ -511,13 +511,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest
                 // `a += b` => `*a += b` if a is a mut ref.
-                if !op.span.can_be_used_for_suggestions() {
+                if !op.span().can_be_used_for_suggestions() {
                     // Suppress suggestions when lhs and rhs are not in the same span as the error
-                } else if is_assign == IsAssign::Yes
+                } else if let Op::AssignOp(_) = op
                     && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty)
                 {
                     suggest_deref_binop(&mut err, lhs_deref_ty);
-                } else if is_assign == IsAssign::No
+                } else if let Op::BinOp(_) = op
                     && let ty::Ref(region, lhs_deref_ty, mutbl) = lhs_ty.kind()
                 {
                     if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty) {
@@ -572,10 +572,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
 
                 if let Some(missing_trait) = missing_trait {
-                    if op.node == hir::BinOpKind::Add
-                        && self.check_str_addition(
-                            lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, is_assign, op,
-                        )
+                    if matches!(
+                        op,
+                        Op::BinOp(Spanned { node: hir::BinOpKind::Add, .. })
+                            | Op::AssignOp(Spanned { node: hir::AssignOpKind::AddAssign, .. })
+                    ) && self
+                        .check_str_addition(lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, op)
                     {
                         // This has nothing here because it means we did string
                         // concatenation (e.g., "Hello " + "World!"). This means
@@ -592,7 +594,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             .lookup_op_method(
                                 (lhs_expr, lhs_ty),
                                 Some((rhs_expr, rhs_ty)),
-                                Op::Binary(op, is_assign),
+                                lang_item_for_binop(self.tcx, op),
+                                op.span(),
                                 expected,
                             )
                             .unwrap_err();
@@ -642,9 +645,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 // Suggest using `add`, `offset` or `offset_from` for pointer - {integer},
                 // pointer + {integer} or pointer - pointer.
-                if op.span.can_be_used_for_suggestions() {
-                    match op.node {
-                        hir::BinOpKind::Add if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() => {
+                if op.span().can_be_used_for_suggestions() {
+                    match op {
+                        Op::BinOp(Spanned { node: hir::BinOpKind::Add, .. })
+                            if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() =>
+                        {
                             err.multipart_suggestion(
                                 "consider using `wrapping_add` or `add` for pointer + {integer}",
                                 vec![
@@ -657,7 +662,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 Applicability::MaybeIncorrect,
                             );
                         }
-                        hir::BinOpKind::Sub => {
+                        Op::BinOp(Spanned { node: hir::BinOpKind::Sub, .. }) => {
                             if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() {
                                 err.multipart_suggestion(
                                     "consider using `wrapping_sub` or `sub` for \
@@ -713,8 +718,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         lhs_ty: Ty<'tcx>,
         rhs_ty: Ty<'tcx>,
         err: &mut Diag<'_>,
-        is_assign: IsAssign,
-        op: hir::BinOp,
+        op: Op,
     ) -> bool {
         let str_concat_note = "string concatenation requires an owned `String` on the left";
         let rm_borrow_msg = "remove the borrow to obtain an owned `String`";
@@ -733,8 +737,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             r_ty.kind(), ty::Ref(_, inner_ty, _) if *inner_ty.kind() == ty::Str
                         )) =>
             {
-                if let IsAssign::No = is_assign { // Do not supply this message if `&str += &str`
-                    err.span_label(op.span, "`+` cannot be used to concatenate two `&str` strings");
+                if let Op::BinOp(_) = op { // Do not supply this message if `&str += &str`
+                    err.span_label(
+                        op.span(),
+                        "`+` cannot be used to concatenate two `&str` strings"
+                    );
                     err.note(str_concat_note);
                     if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
                         err.span_suggestion_verbose(
@@ -758,11 +765,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 if (*l_ty.kind() == ty::Str || is_std_string(l_ty)) && is_std_string(rhs_ty) =>
             {
                 err.span_label(
-                    op.span,
+                    op.span(),
                     "`+` cannot be used to concatenate a `&str` with a `String`",
                 );
-                match is_assign {
-                    IsAssign::No => {
+                match op {
+                    Op::BinOp(_) => {
                         let sugg_msg;
                         let lhs_sugg = if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
                             sugg_msg = "remove the borrow on the left and add one on the right";
@@ -781,7 +788,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             Applicability::MachineApplicable,
                         );
                     }
-                    IsAssign::Yes => {
+                    Op::AssignOp(_) => {
                         err.note(str_concat_note);
                     }
                 }
@@ -799,7 +806,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Expectation<'tcx>,
     ) -> Ty<'tcx> {
         assert!(op.is_by_value());
-        match self.lookup_op_method((ex, operand_ty), None, Op::Unary(op, ex.span), expected) {
+        match self.lookup_op_method(
+            (ex, operand_ty),
+            None,
+            lang_item_for_unop(self.tcx, op),
+            ex.span,
+            expected,
+        ) {
             Ok(method) => {
                 self.write_method_call_and_enforce_effects(ex.hir_id, ex.span, method);
                 method.sig.output()
@@ -898,21 +911,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         (lhs_expr, lhs_ty): (&'tcx hir::Expr<'tcx>, Ty<'tcx>),
         opt_rhs: Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)>,
-        op: Op,
+        (opname, trait_did): (Symbol, Option<hir::def_id::DefId>),
+        span: Span,
         expected: Expectation<'tcx>,
     ) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
-        let span = match op {
-            Op::Binary(op, _) => op.span,
-            Op::Unary(_, span) => span,
-        };
-        let (opname, Some(trait_did)) = lang_item_for_op(self.tcx, op, span) else {
+        let Some(trait_did) = trait_did else {
             // Bail if the operator trait is not defined.
             return Err(vec![]);
         };
 
         debug!(
-            "lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})",
-            lhs_ty, op, opname, trait_did
+            "lookup_op_method(lhs_ty={:?}, opname={:?}, trait_did={:?})",
+            lhs_ty, opname, trait_did
         );
 
         let opname = Ident::with_dummy_span(opname);
@@ -980,37 +990,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 }
 
-fn lang_item_for_op(
-    tcx: TyCtxt<'_>,
-    op: Op,
-    span: Span,
-) -> (rustc_span::Symbol, Option<hir::def_id::DefId>) {
+fn lang_item_for_binop(tcx: TyCtxt<'_>, op: Op) -> (Symbol, Option<hir::def_id::DefId>) {
     let lang = tcx.lang_items();
-    if let Op::Binary(op, IsAssign::Yes) = op {
-        match op.node {
-            hir::BinOpKind::Add => (sym::add_assign, lang.add_assign_trait()),
-            hir::BinOpKind::Sub => (sym::sub_assign, lang.sub_assign_trait()),
-            hir::BinOpKind::Mul => (sym::mul_assign, lang.mul_assign_trait()),
-            hir::BinOpKind::Div => (sym::div_assign, lang.div_assign_trait()),
-            hir::BinOpKind::Rem => (sym::rem_assign, lang.rem_assign_trait()),
-            hir::BinOpKind::BitXor => (sym::bitxor_assign, lang.bitxor_assign_trait()),
-            hir::BinOpKind::BitAnd => (sym::bitand_assign, lang.bitand_assign_trait()),
-            hir::BinOpKind::BitOr => (sym::bitor_assign, lang.bitor_assign_trait()),
-            hir::BinOpKind::Shl => (sym::shl_assign, lang.shl_assign_trait()),
-            hir::BinOpKind::Shr => (sym::shr_assign, lang.shr_assign_trait()),
-            hir::BinOpKind::Lt
-            | hir::BinOpKind::Le
-            | hir::BinOpKind::Ge
-            | hir::BinOpKind::Gt
-            | hir::BinOpKind::Eq
-            | hir::BinOpKind::Ne
-            | hir::BinOpKind::And
-            | hir::BinOpKind::Or => {
-                span_bug!(span, "impossible assignment operation: {}=", op.node.as_str())
-            }
-        }
-    } else if let Op::Binary(op, IsAssign::No) = op {
-        match op.node {
+    match op {
+        Op::AssignOp(op) => match op.node {
+            hir::AssignOpKind::AddAssign => (sym::add_assign, lang.add_assign_trait()),
+            hir::AssignOpKind::SubAssign => (sym::sub_assign, lang.sub_assign_trait()),
+            hir::AssignOpKind::MulAssign => (sym::mul_assign, lang.mul_assign_trait()),
+            hir::AssignOpKind::DivAssign => (sym::div_assign, lang.div_assign_trait()),
+            hir::AssignOpKind::RemAssign => (sym::rem_assign, lang.rem_assign_trait()),
+            hir::AssignOpKind::BitXorAssign => (sym::bitxor_assign, lang.bitxor_assign_trait()),
+            hir::AssignOpKind::BitAndAssign => (sym::bitand_assign, lang.bitand_assign_trait()),
+            hir::AssignOpKind::BitOrAssign => (sym::bitor_assign, lang.bitor_assign_trait()),
+            hir::AssignOpKind::ShlAssign => (sym::shl_assign, lang.shl_assign_trait()),
+            hir::AssignOpKind::ShrAssign => (sym::shr_assign, lang.shr_assign_trait()),
+        },
+        Op::BinOp(op) => match op.node {
             hir::BinOpKind::Add => (sym::add, lang.add_trait()),
             hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()),
             hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()),
@@ -1028,20 +1023,24 @@ fn lang_item_for_op(
             hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()),
             hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()),
             hir::BinOpKind::And | hir::BinOpKind::Or => {
-                span_bug!(span, "&& and || are not overloadable")
+                bug!("&& and || are not overloadable")
             }
-        }
-    } else if let Op::Unary(hir::UnOp::Not, _) = op {
-        (sym::not, lang.not_trait())
-    } else if let Op::Unary(hir::UnOp::Neg, _) = op {
-        (sym::neg, lang.neg_trait())
-    } else {
-        bug!("lookup_op_method: op not supported: {:?}", op)
+        },
+    }
+}
+
+fn lang_item_for_unop(tcx: TyCtxt<'_>, op: hir::UnOp) -> (Symbol, Option<hir::def_id::DefId>) {
+    let lang = tcx.lang_items();
+    match op {
+        hir::UnOp::Not => (sym::not, lang.not_trait()),
+        hir::UnOp::Neg => (sym::neg, lang.neg_trait()),
+        hir::UnOp::Deref => bug!("Deref is not overloadable"),
     }
 }
 
 // Binary operator categories. These categories summarize the behavior
 // with respect to the builtin operations supported.
+#[derive(Clone, Copy)]
 enum BinOpCategory {
     /// &&, || -- cannot be overridden
     Shortcircuit,
@@ -1063,44 +1062,58 @@ enum BinOpCategory {
     Comparison,
 }
 
-impl BinOpCategory {
-    fn from(op: hir::BinOp) -> BinOpCategory {
-        match op.node {
-            hir::BinOpKind::Shl | hir::BinOpKind::Shr => BinOpCategory::Shift,
-
-            hir::BinOpKind::Add
-            | hir::BinOpKind::Sub
-            | hir::BinOpKind::Mul
-            | hir::BinOpKind::Div
-            | hir::BinOpKind::Rem => BinOpCategory::Math,
-
-            hir::BinOpKind::BitXor | hir::BinOpKind::BitAnd | hir::BinOpKind::BitOr => {
-                BinOpCategory::Bitwise
-            }
-
-            hir::BinOpKind::Eq
-            | hir::BinOpKind::Ne
-            | hir::BinOpKind::Lt
-            | hir::BinOpKind::Le
-            | hir::BinOpKind::Ge
-            | hir::BinOpKind::Gt => BinOpCategory::Comparison,
+impl From<hir::BinOpKind> for BinOpCategory {
+    fn from(op: hir::BinOpKind) -> BinOpCategory {
+        use hir::BinOpKind::*;
+        match op {
+            Shl | Shr => BinOpCategory::Shift,
+            Add | Sub | Mul | Div | Rem => BinOpCategory::Math,
+            BitXor | BitAnd | BitOr => BinOpCategory::Bitwise,
+            Eq | Ne | Lt | Le | Ge | Gt => BinOpCategory::Comparison,
+            And | Or => BinOpCategory::Shortcircuit,
+        }
+    }
+}
 
-            hir::BinOpKind::And | hir::BinOpKind::Or => BinOpCategory::Shortcircuit,
+impl From<hir::AssignOpKind> for BinOpCategory {
+    fn from(op: hir::AssignOpKind) -> BinOpCategory {
+        use hir::AssignOpKind::*;
+        match op {
+            ShlAssign | ShrAssign => BinOpCategory::Shift,
+            AddAssign | SubAssign | MulAssign | DivAssign | RemAssign => BinOpCategory::Math,
+            BitXorAssign | BitAndAssign | BitOrAssign => BinOpCategory::Bitwise,
         }
     }
 }
 
-/// Whether the binary operation is an assignment (`a += b`), or not (`a + b`)
+/// An assignment op (e.g. `a += b`), or a binary op (e.g. `a + b`).
 #[derive(Clone, Copy, Debug, PartialEq)]
-enum IsAssign {
-    No,
-    Yes,
+enum Op {
+    BinOp(hir::BinOp),
+    AssignOp(hir::AssignOp),
 }
 
-#[derive(Clone, Copy, Debug)]
-enum Op {
-    Binary(hir::BinOp, IsAssign),
-    Unary(hir::UnOp, Span),
+impl Op {
+    fn span(&self) -> Span {
+        match self {
+            Op::BinOp(op) => op.span,
+            Op::AssignOp(op) => op.span,
+        }
+    }
+
+    fn as_str(&self) -> &'static str {
+        match self {
+            Op::BinOp(op) => op.node.as_str(),
+            Op::AssignOp(op) => op.node.as_str(),
+        }
+    }
+
+    fn is_by_value(&self) -> bool {
+        match self {
+            Op::BinOp(op) => op.node.is_by_value(),
+            Op::AssignOp(op) => op.node.is_by_value(),
+        }
+    }
 }
 
 /// Dereferences a single level of immutable referencing.
@@ -1127,27 +1140,24 @@ fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> {
 /// Reason #2 is the killer. I tried for a while to always use
 /// overloaded logic and just check the types in constants/codegen after
 /// the fact, and it worked fine, except for SIMD types. -nmatsakis
-fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool {
+fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, category: BinOpCategory) -> bool {
     // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
     // (See https://github.com/rust-lang/rust/issues/57447.)
     let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs));
 
-    match BinOpCategory::from(op) {
+    match category.into() {
         BinOpCategory::Shortcircuit => true,
-
         BinOpCategory::Shift => {
             lhs.references_error()
                 || rhs.references_error()
                 || lhs.is_integral() && rhs.is_integral()
         }
-
         BinOpCategory::Math => {
             lhs.references_error()
                 || rhs.references_error()
                 || lhs.is_integral() && rhs.is_integral()
                 || lhs.is_floating_point() && rhs.is_floating_point()
         }
-
         BinOpCategory::Bitwise => {
             lhs.references_error()
                 || rhs.references_error()
@@ -1155,7 +1165,6 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool
                 || lhs.is_floating_point() && rhs.is_floating_point()
                 || lhs.is_bool() && rhs.is_bool()
         }
-
         BinOpCategory::Comparison => {
             lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar()
         }
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index f1f956779c9..9766ceda569 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -632,10 +632,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             {
                 let tcx = self.tcx;
                 trace!(?lt.hir_id.local_id, "polymorphic byte string lit");
-                self.typeck_results
-                    .borrow_mut()
-                    .treat_byte_string_as_slice
-                    .insert(lt.hir_id.local_id);
                 pat_ty =
                     Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, Ty::new_slice(tcx, tcx.types.u8));
             }
@@ -944,10 +940,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) {
         let var_ty = self.local_ty(span, var_id);
         if let Err(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) {
-            let hir = self.tcx.hir();
             let var_ty = self.resolve_vars_if_possible(var_ty);
             let msg = format!("first introduced with type `{var_ty}` here");
-            err.span_label(hir.span(var_id), msg);
+            err.span_label(self.tcx.hir_span(var_id), msg);
             let in_match = self.tcx.hir_parent_iter(var_id).any(|(_, n)| {
                 matches!(
                     n,
@@ -1255,7 +1250,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 })) => match self.tcx.hir_node(body_id.hir_id) {
                     hir::Node::Expr(expr) => {
                         if hir::is_range_literal(expr) {
-                            let span = self.tcx.hir().span(body_id.hir_id);
+                            let span = self.tcx.hir_span(body_id.hir_id);
                             if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) {
                                 e.span_suggestion_verbose(
                                     ident.span,
@@ -1286,7 +1281,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         pat_ty: Ty<'tcx>,
         segments: &'tcx [hir::PathSegment<'tcx>],
     ) {
-        if let Some(span) = self.tcx.hir().res_span(pat_res) {
+        if let Some(span) = self.tcx.hir_res_span(pat_res) {
             e.span_label(span, format!("{} defined here", res.descr()));
             if let [hir::PathSegment { ident, .. }] = &*segments {
                 e.span_label(
diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index fc98a603dd8..8ab71e5220b 100644
--- a/compiler/rustc_hir_typeck/src/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -18,12 +18,12 @@
 //! from there).
 //!
 //! The fact that we are inferring borrow kinds as we go results in a
-//! semi-hacky interaction with mem-categorization. In particular,
-//! mem-categorization will query the current borrow kind as it
-//! categorizes, and we'll return the *current* value, but this may get
+//! semi-hacky interaction with the way `ExprUseVisitor` is computing
+//! `Place`s. In particular, it will query the current borrow kind as it
+//! goes, and we'll return the *current* value, but this may get
 //! adjusted later. Therefore, in this module, we generally ignore the
-//! borrow kind (and derived mutabilities) that are returned from
-//! mem-categorization, since they may be inaccurate. (Another option
+//! borrow kind (and derived mutabilities) that `ExprUseVisitor` returns
+//! within `Place`s, since they may be inaccurate. (Another option
 //! would be to use a unification scheme, where instead of returning a
 //! concrete borrow kind like `ty::ImmBorrow`, we return a
 //! `ty::InferBorrow(upvar_id)` or something like that, but this would
@@ -85,7 +85,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 /// Intermediate format to store the hir_id pointing to the use that resulted in the
 /// corresponding place being captured and a String which contains the captured value's
 /// name (i.e: a.b.c)
-#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
 enum UpvarMigrationInfo {
     /// We previously captured all of `x`, but now we capture some sub-path.
     CapturingPrecise { source_expr: Option<HirId>, var_name: String },
@@ -635,7 +635,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let (place, capture_kind) = truncate_capture_for_optimization(place, capture_kind);
 
                 let usage_span = if let Some(usage_expr) = capture_info.path_expr_id {
-                    self.tcx.hir().span(usage_expr)
+                    self.tcx.hir_span(usage_expr)
                 } else {
                     unreachable!()
                 };
@@ -986,7 +986,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         for lint_note in diagnostics_info.iter() {
                             match &lint_note.captures_info {
                                 UpvarMigrationInfo::CapturingPrecise { source_expr: Some(capture_expr_id), var_name: captured_name } => {
-                                    let cause_span = self.tcx.hir().span(*capture_expr_id);
+                                    let cause_span = self.tcx.hir_span(*capture_expr_id);
                                     lint.span_label(cause_span, format!("in Rust 2018, this closure captures all of `{}`, but in Rust 2021, it will only capture `{}`",
                                         self.tcx.hir_name(*var_hir_id),
                                         captured_name,
@@ -1047,13 +1047,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         "add a dummy let to cause {migrated_variables_concat} to be fully captured"
                     );
 
-                    let closure_span = self.tcx.hir().span_with_body(closure_hir_id);
+                    let closure_span = self.tcx.hir_span_with_body(closure_hir_id);
                     let mut closure_body_span = {
                         // If the body was entirely expanded from a macro
                         // invocation, i.e. the body is not contained inside the
                         // closure span, then we walk up the expansion until we
                         // find the span before the expansion.
-                        let s = self.tcx.hir().span_with_body(body_id.hir_id);
+                        let s = self.tcx.hir_span_with_body(body_id.hir_id);
                         s.find_ancestor_inside(closure_span).unwrap_or(s)
                     };
 
@@ -1396,14 +1396,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 FxIndexSet::default()
             };
 
-            // Combine all the captures responsible for needing migrations into one HashSet
+            // Combine all the captures responsible for needing migrations into one IndexSet
             let mut capture_diagnostic = drop_reorder_diagnostic.clone();
             for key in auto_trait_diagnostic.keys() {
                 capture_diagnostic.insert(key.clone());
             }
 
             let mut capture_diagnostic = capture_diagnostic.into_iter().collect::<Vec<_>>();
-            capture_diagnostic.sort();
+            capture_diagnostic.sort_by_cached_key(|info| match info {
+                UpvarMigrationInfo::CapturingPrecise { source_expr: _, var_name } => {
+                    (0, Some(var_name.clone()))
+                }
+                UpvarMigrationInfo::CapturingNothing { use_span: _ } => (1, None),
+            });
             for captures_info in capture_diagnostic {
                 // Get the auto trait reasons of why migration is needed because of that capture, if there are any
                 let capture_trait_reasons =
@@ -1752,8 +1757,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let capture_str = construct_capture_info_string(self.tcx, place, capture_info);
                 let output_str = format!("Capturing {capture_str}");
 
-                let span =
-                    capture_info.path_expr_id.map_or(closure_span, |e| self.tcx.hir().span(e));
+                let span = capture_info.path_expr_id.map_or(closure_span, |e| self.tcx.hir_span(e));
                 diag.span_note(span, output_str);
             }
             diag.emit();
@@ -1780,10 +1784,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         if capture.info.path_expr_id != capture.info.capture_kind_expr_id {
                             let path_span = capture_info
                                 .path_expr_id
-                                .map_or(closure_span, |e| self.tcx.hir().span(e));
+                                .map_or(closure_span, |e| self.tcx.hir_span(e));
                             let capture_kind_span = capture_info
                                 .capture_kind_expr_id
-                                .map_or(closure_span, |e| self.tcx.hir().span(e));
+                                .map_or(closure_span, |e| self.tcx.hir_span(e));
 
                             let mut multi_span: MultiSpan =
                                 MultiSpan::from_spans(vec![path_span, capture_kind_span]);
@@ -1799,7 +1803,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         } else {
                             let span = capture_info
                                 .path_expr_id
-                                .map_or(closure_span, |e| self.tcx.hir().span(e));
+                                .map_or(closure_span, |e| self.tcx.hir_span(e));
 
                             diag.span_note(span, output_str);
                         };
@@ -1828,8 +1832,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let mut is_mutbl = bm.1;
 
         for pointer_ty in place.deref_tys() {
-            match self.structurally_resolve_type(self.tcx.hir().span(var_hir_id), pointer_ty).kind()
-            {
+            match self.structurally_resolve_type(self.tcx.hir_span(var_hir_id), pointer_ty).kind() {
                 // We don't capture derefs of raw ptrs
                 ty::RawPtr(_, _) => unreachable!(),
 
@@ -1844,7 +1847,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 ty::Adt(def, ..) if def.is_box() => {}
 
                 unexpected_ty => span_bug!(
-                    self.tcx.hir().span(var_hir_id),
+                    self.tcx.hir_span(var_hir_id),
                     "deref of unexpected pointer type {:?}",
                     unexpected_ty
                 ),
@@ -1975,14 +1978,14 @@ fn drop_location_span(tcx: TyCtxt<'_>, hir_id: HirId) -> Span {
     let owner_node = tcx.hir_node(owner_id);
     let owner_span = match owner_node {
         hir::Node::Item(item) => match item.kind {
-            hir::ItemKind::Fn { body: owner_id, .. } => tcx.hir().span(owner_id.hir_id),
+            hir::ItemKind::Fn { body: owner_id, .. } => tcx.hir_span(owner_id.hir_id),
             _ => {
                 bug!("Drop location span error: need to handle more ItemKind '{:?}'", item.kind);
             }
         },
-        hir::Node::Block(block) => tcx.hir().span(block.hir_id),
-        hir::Node::TraitItem(item) => tcx.hir().span(item.hir_id()),
-        hir::Node::ImplItem(item) => tcx.hir().span(item.hir_id()),
+        hir::Node::Block(block) => tcx.hir_span(block.hir_id),
+        hir::Node::TraitItem(item) => tcx.hir_span(item.hir_id()),
+        hir::Node::ImplItem(item) => tcx.hir_span(item.hir_id()),
         _ => {
             bug!("Drop location span error: need to handle more Node '{:?}'", owner_node);
         }
@@ -2325,8 +2328,9 @@ fn should_do_rust_2021_incompatible_closure_captures_analysis(
         return false;
     }
 
-    let (level, _) =
-        tcx.lint_level_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id);
+    let level = tcx
+        .lint_level_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id)
+        .level;
 
     !matches!(level, lint::Level::Allow)
 }
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index b63c0b6ab7e..6ba7435cb79 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -12,10 +12,12 @@ use rustc_middle::span_bug;
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion};
 use rustc_middle::ty::{
-    self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, fold_regions,
+    self, DefiningScopeKind, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
+    TypeVisitableExt, fold_regions,
 };
 use rustc_span::{Span, sym};
 use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded;
+use rustc_trait_selection::opaque_types::check_opaque_type_parameter_valid;
 use rustc_trait_selection::solve;
 use tracing::{debug, instrument};
 
@@ -81,9 +83,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports);
         wbcx.typeck_results.used_trait_imports = used_trait_imports;
 
-        wbcx.typeck_results.treat_byte_string_as_slice =
-            mem::take(&mut self.typeck_results.borrow_mut().treat_byte_string_as_slice);
-
         debug!("writeback: typeck results for {:?} are {:#?}", item_def_id, wbcx.typeck_results);
 
         self.tcx.arena.alloc(wbcx.typeck_results)
@@ -160,7 +159,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
                     self.typeck_results.node_args_mut().remove(e.hir_id);
                 }
             }
-            hir::ExprKind::Binary(ref op, lhs, rhs) | hir::ExprKind::AssignOp(ref op, lhs, rhs) => {
+            hir::ExprKind::Binary(ref op, lhs, rhs) => {
                 let lhs_ty = self.typeck_results.node_type(lhs.hir_id);
                 let rhs_ty = self.typeck_results.node_type(rhs.hir_id);
 
@@ -168,25 +167,27 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
                     self.typeck_results.type_dependent_defs_mut().remove(e.hir_id);
                     self.typeck_results.node_args_mut().remove(e.hir_id);
 
-                    match e.kind {
-                        hir::ExprKind::Binary(..) => {
-                            if !op.node.is_by_value() {
-                                let mut adjustments = self.typeck_results.adjustments_mut();
-                                if let Some(a) = adjustments.get_mut(lhs.hir_id) {
-                                    a.pop();
-                                }
-                                if let Some(a) = adjustments.get_mut(rhs.hir_id) {
-                                    a.pop();
-                                }
-                            }
+                    if !op.node.is_by_value() {
+                        let mut adjustments = self.typeck_results.adjustments_mut();
+                        if let Some(a) = adjustments.get_mut(lhs.hir_id) {
+                            a.pop();
                         }
-                        hir::ExprKind::AssignOp(..)
-                            if let Some(a) =
-                                self.typeck_results.adjustments_mut().get_mut(lhs.hir_id) =>
-                        {
+                        if let Some(a) = adjustments.get_mut(rhs.hir_id) {
                             a.pop();
                         }
-                        _ => {}
+                    }
+                }
+            }
+            hir::ExprKind::AssignOp(_, lhs, rhs) => {
+                let lhs_ty = self.typeck_results.node_type(lhs.hir_id);
+                let rhs_ty = self.typeck_results.node_type(rhs.hir_id);
+
+                if lhs_ty.is_scalar() && rhs_ty.is_scalar() {
+                    self.typeck_results.type_dependent_defs_mut().remove(e.hir_id);
+                    self.typeck_results.node_args_mut().remove(e.hir_id);
+
+                    if let Some(a) = self.typeck_results.adjustments_mut().get_mut(lhs.hir_id) {
+                        a.pop();
                     }
                 }
             }
@@ -491,7 +492,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
 
                 if let ty::UserTypeKind::TypeOf(_, user_args) = c_ty.value.kind {
                     // This is a unit-testing mechanism.
-                    let span = self.tcx().hir().span(hir_id);
+                    let span = self.tcx().hir_span(hir_id);
                     // We need to buffer the errors in order to guarantee a consistent
                     // order when emitting them.
                     let err =
@@ -556,6 +557,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
 
     #[instrument(skip(self), level = "debug")]
     fn visit_opaque_types(&mut self) {
+        let tcx = self.tcx();
         // We clone the opaques instead of stealing them here as they are still used for
         // normalization in the next generation trait solver.
         //
@@ -578,16 +580,46 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
                 }
             }
 
-            // Here we only detect impl trait definition conflicts when they
-            // are equal modulo regions.
-            if let Some(last_opaque_ty) =
-                self.typeck_results.concrete_opaque_types.insert(opaque_type_key, hidden_type)
-                && last_opaque_ty.ty != hidden_type.ty
+            if let Err(guar) = check_opaque_type_parameter_valid(
+                &self.fcx,
+                opaque_type_key,
+                hidden_type.span,
+                DefiningScopeKind::HirTypeck,
+            ) {
+                self.typeck_results
+                    .concrete_opaque_types
+                    .insert(opaque_type_key.def_id, ty::OpaqueHiddenType::new_error(tcx, guar));
+            }
+
+            let hidden_type = hidden_type.remap_generic_params_to_declaration_params(
+                opaque_type_key,
+                tcx,
+                DefiningScopeKind::HirTypeck,
+            );
+
+            if let Some(prev) = self
+                .typeck_results
+                .concrete_opaque_types
+                .insert(opaque_type_key.def_id, hidden_type)
             {
-                assert!(!self.fcx.next_trait_solver());
-                if let Ok(d) = hidden_type.build_mismatch_error(&last_opaque_ty, self.tcx()) {
-                    d.emit();
+                let entry = &mut self
+                    .typeck_results
+                    .concrete_opaque_types
+                    .get_mut(&opaque_type_key.def_id)
+                    .unwrap();
+                if prev.ty != hidden_type.ty {
+                    if let Some(guar) = self.typeck_results.tainted_by_errors {
+                        entry.ty = Ty::new_error(tcx, guar);
+                    } else {
+                        let (Ok(guar) | Err(guar)) =
+                            prev.build_mismatch_error(&hidden_type, tcx).map(|d| d.emit());
+                        entry.ty = Ty::new_error(tcx, guar);
+                    }
                 }
+
+                // Pick a better span if there is one.
+                // FIXME(oli-obk): collect multiple spans for better diagnostics down the road.
+                entry.span = prev.span.substitute_dummy(hidden_type.span);
             }
         }
     }
@@ -775,7 +807,7 @@ impl Locatable for Span {
 
 impl Locatable for HirId {
     fn to_span(&self, tcx: TyCtxt<'_>) -> Span {
-        tcx.hir().span(*self)
+        tcx.hir_span(*self)
     }
 }
 
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index fa8dea064da..529e996ad1e 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -27,6 +27,7 @@ use rustc_middle::bug;
 use rustc_middle::infer::canonical::{CanonicalQueryInput, CanonicalVarValues};
 use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::traits::select;
+use rustc_middle::traits::solve::Goal;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::{
     self, BoundVarReplacerDelegate, ConstVid, FloatVid, GenericArg, GenericArgKind, GenericArgs,
@@ -268,7 +269,7 @@ pub struct InferCtxt<'tcx> {
     /// The set of predicates on which errors have been reported, to
     /// avoid reporting the same error twice.
     pub reported_trait_errors:
-        RefCell<FxIndexMap<Span, (Vec<ty::Predicate<'tcx>>, ErrorGuaranteed)>>,
+        RefCell<FxIndexMap<Span, (Vec<Goal<'tcx, ty::Predicate<'tcx>>>, ErrorGuaranteed)>>,
 
     pub reported_signature_mismatch: RefCell<FxHashSet<(Span, Option<Span>)>>,
 
@@ -966,7 +967,8 @@ impl<'tcx> InferCtxt<'tcx> {
     pub fn can_define_opaque_ty(&self, id: impl Into<DefId>) -> bool {
         debug_assert!(!self.next_trait_solver());
         match self.typing_mode() {
-            TypingMode::Analysis { defining_opaque_types } => {
+            TypingMode::Analysis { defining_opaque_types }
+            | TypingMode::Borrowck { defining_opaque_types } => {
                 id.into().as_local().is_some_and(|def_id| defining_opaque_types.contains(&def_id))
             }
             // FIXME(#132279): This function is quite weird in post-analysis
@@ -1260,7 +1262,8 @@ impl<'tcx> InferCtxt<'tcx> {
             // to handle them without proper canonicalization. This means we may cause cycle
             // errors and fail to reveal opaques while inside of bodies. We should rename this
             // function and require explicit comments on all use-sites in the future.
-            ty::TypingMode::Analysis { defining_opaque_types: _ } => {
+            ty::TypingMode::Analysis { defining_opaque_types: _ }
+            | ty::TypingMode::Borrowck { defining_opaque_types: _ } => {
                 TypingMode::non_body_analysis()
             }
             mode @ (ty::TypingMode::Coherence
diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs
index 215b1333726..ce5d2e6e17a 100644
--- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs
@@ -12,7 +12,7 @@ use rustc_middle::ty::{
 use rustc_span::Span;
 use tracing::{debug, instrument};
 
-use super::DefineOpaqueTypes;
+use super::{DefineOpaqueTypes, RegionVariableOrigin};
 use crate::errors::OpaqueHiddenTypeDiag;
 use crate::infer::{InferCtxt, InferOk};
 use crate::traits::{self, Obligation, PredicateObligations};
@@ -198,13 +198,12 @@ impl<'tcx> InferCtxt<'tcx> {
     /// it hasn't previously been defined. This does not emit any
     /// constraints and it's the responsibility of the caller to make
     /// sure that the item bounds of the opaque are checked.
-    pub fn inject_new_hidden_type_unchecked(
+    pub fn register_hidden_type_in_storage(
         &self,
         opaque_type_key: OpaqueTypeKey<'tcx>,
         hidden_ty: OpaqueHiddenType<'tcx>,
-    ) {
-        let prev = self.inner.borrow_mut().opaque_types().register(opaque_type_key, hidden_ty);
-        assert_eq!(prev, None);
+    ) -> Option<Ty<'tcx>> {
+        self.inner.borrow_mut().opaque_types().register(opaque_type_key, hidden_ty)
     }
 
     /// Insert a hidden type into the opaque type storage, equating it
@@ -222,6 +221,7 @@ impl<'tcx> InferCtxt<'tcx> {
         hidden_ty: Ty<'tcx>,
         goals: &mut Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
     ) -> Result<(), TypeError<'tcx>> {
+        let tcx = self.tcx;
         // Ideally, we'd get the span where *this specific `ty` came
         // from*, but right now we just use the span from the overall
         // value being folded. In simple cases like `-> impl Foo`,
@@ -232,7 +232,7 @@ impl<'tcx> InferCtxt<'tcx> {
                 // During intercrate we do not define opaque types but instead always
                 // force ambiguity unless the hidden type is known to not implement
                 // our trait.
-                goals.push(Goal::new(self.tcx, param_env, ty::PredicateKind::Ambiguous));
+                goals.push(Goal::new(tcx, param_env, ty::PredicateKind::Ambiguous));
             }
             ty::TypingMode::Analysis { .. } => {
                 let prev = self
@@ -250,6 +250,36 @@ impl<'tcx> InferCtxt<'tcx> {
                     );
                 }
             }
+            ty::TypingMode::Borrowck { .. } => {
+                let prev = self
+                    .inner
+                    .borrow_mut()
+                    .opaque_types()
+                    .register(opaque_type_key, OpaqueHiddenType { ty: hidden_ty, span });
+
+                // We either equate the new hidden type with the previous entry or with the type
+                // inferred by HIR typeck.
+                let actual = prev.unwrap_or_else(|| {
+                    let actual = tcx
+                        .type_of_opaque_hir_typeck(opaque_type_key.def_id)
+                        .instantiate(self.tcx, opaque_type_key.args);
+                    let actual = ty::fold_regions(tcx, actual, |re, _dbi| match re.kind() {
+                        ty::ReErased => {
+                            self.next_region_var(RegionVariableOrigin::MiscVariable(span))
+                        }
+                        _ => re,
+                    });
+                    actual
+                });
+
+                goals.extend(
+                    self.at(&ObligationCause::dummy_with_span(span), param_env)
+                        .eq(DefineOpaqueTypes::Yes, hidden_ty, actual)?
+                        .obligations
+                        .into_iter()
+                        .map(|obligation| obligation.as_goal()),
+                );
+            }
             mode @ (ty::TypingMode::PostBorrowckAnalysis { .. } | ty::TypingMode::PostAnalysis) => {
                 bug!("insert hidden type in {mode:?}")
             }
diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl
index ffbe708ba8d..4b9c71d7172 100644
--- a/compiler/rustc_interface/messages.ftl
+++ b/compiler/rustc_interface/messages.ftl
@@ -50,11 +50,5 @@ interface_out_dir_error =
 interface_proc_macro_crate_panic_abort =
     building proc macro crate with `panic=abort` may crash the compiler should the proc-macro panic
 
-interface_rustc_error_fatal =
-    fatal error triggered by #[rustc_error]
-
-interface_rustc_error_unexpected_annotation =
-    unexpected annotation used with `#[rustc_error(...)]`!
-
 interface_temps_dir_error =
     failed to find or create the directory specified by `--temps-dir`
diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs
index ef0235b5577..6b39b4f1891 100644
--- a/compiler/rustc_interface/src/errors.rs
+++ b/compiler/rustc_interface/src/errors.rs
@@ -74,20 +74,6 @@ pub struct TempsDirError;
 pub struct OutDirError;
 
 #[derive(Diagnostic)]
-#[diag(interface_rustc_error_fatal)]
-pub struct RustcErrorFatal {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
-#[diag(interface_rustc_error_unexpected_annotation)]
-pub struct RustcErrorUnexpectedAnnotation {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(interface_failed_writing_file)]
 pub struct FailedWritingFile<'a> {
     pub path: &'a Path,
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index 3f87b1a547b..33b4a48b28d 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -204,6 +204,14 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch
                     error!("`cfg()` names cannot be after values");
                 }
                 names.push(ident);
+            } else if let Some(boolean) = arg.boolean_literal() {
+                if values_specified {
+                    error!("`cfg()` names cannot be after values");
+                }
+                names.push(rustc_span::Ident::new(
+                    if boolean { rustc_span::kw::True } else { rustc_span::kw::False },
+                    arg.span(),
+                ));
             } else if arg.has_name(sym::any)
                 && let Some(args) = arg.meta_item_list()
             {
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 2440f0639c8..747e36b6a1a 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -976,7 +976,7 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
         tcx.par_hir_body_owners(|def_id| {
             if tcx.is_coroutine(def_id.to_def_id()) {
                 tcx.ensure_ok().mir_coroutine_witnesses(def_id);
-                tcx.ensure_ok().check_coroutine_obligations(
+                let _ = tcx.ensure_ok().check_coroutine_obligations(
                     tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(),
                 );
                 // Eagerly check the unsubstituted layout for cycles.
@@ -1067,48 +1067,18 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) {
     });
 }
 
-/// Check for the `#[rustc_error]` annotation, which forces an error in codegen. This is used
-/// to write UI tests that actually test that compilation succeeds without reporting
-/// an error.
-fn check_for_rustc_errors_attr(tcx: TyCtxt<'_>) {
-    let Some((def_id, _)) = tcx.entry_fn(()) else { return };
-    for attr in tcx.get_attrs(def_id, sym::rustc_error) {
-        match attr.meta_item_list() {
-            // Check if there is a `#[rustc_error(delayed_bug_from_inside_query)]`.
-            Some(list)
-                if list.iter().any(|list_item| {
-                    matches!(
-                        list_item.ident().map(|i| i.name),
-                        Some(sym::delayed_bug_from_inside_query)
-                    )
-                }) =>
-            {
-                tcx.ensure_ok().trigger_delayed_bug(def_id);
-            }
-
-            // Bare `#[rustc_error]`.
-            None => {
-                tcx.dcx().emit_fatal(errors::RustcErrorFatal { span: tcx.def_span(def_id) });
-            }
-
-            // Some other attribute.
-            Some(_) => {
-                tcx.dcx().emit_warn(errors::RustcErrorUnexpectedAnnotation {
-                    span: tcx.def_span(def_id),
-                });
-            }
-        }
-    }
-}
-
 /// Runs the codegen backend, after which the AST and analysis can
 /// be discarded.
 pub(crate) fn start_codegen<'tcx>(
     codegen_backend: &dyn CodegenBackend,
     tcx: TyCtxt<'tcx>,
 ) -> Box<dyn Any> {
-    // Hook for UI tests.
-    check_for_rustc_errors_attr(tcx);
+    // Hook for tests.
+    if let Some((def_id, _)) = tcx.entry_fn(())
+        && tcx.has_attr(def_id, sym::rustc_delayed_bug_from_inside_query)
+    {
+        tcx.ensure_ok().trigger_delayed_bug(def_id);
+    }
 
     // Don't run this test assertions when not doing codegen. Compiletest tries to build
     // build-fail tests in check mode first and expects it to not give an error in that case.
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index b44be1710ed..a8e55663257 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -787,6 +787,7 @@ fn test_unstable_options_tracking_hash() {
     tracked!(direct_access_external_data, Some(true));
     tracked!(dual_proc_macros, true);
     tracked!(dwarf_version, Some(5));
+    tracked!(embed_metadata, false);
     tracked!(embed_source, true);
     tracked!(emit_thin_lto, false);
     tracked!(emscripten_wasm_eh, true);
@@ -852,6 +853,7 @@ fn test_unstable_options_tracking_hash() {
     tracked!(sanitizer_cfi_generalize_pointers, Some(true));
     tracked!(sanitizer_cfi_normalize_integers, Some(true));
     tracked!(sanitizer_dataflow_abilist, vec![String::from("/rustc/abc")]);
+    tracked!(sanitizer_kcfi_arity, Some(true));
     tracked!(sanitizer_memory_track_origins, 2);
     tracked!(sanitizer_recover, SanitizerSet::ADDRESS);
     tracked!(saturating_float_casts, Some(true));
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 0a3eb434d3f..782d328a951 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -456,6 +456,10 @@ lint_invalid_nan_comparisons_eq_ne = incorrect NaN comparison, NaN cannot be dir
 
 lint_invalid_nan_comparisons_lt_le_gt_ge = incorrect NaN comparison, NaN is not orderable
 
+lint_invalid_null_arguments = calling this function with a null pointer is undefined behavior, even if the result of the function is unused
+    .origin = null pointer originates from here
+    .doc = for more information, visit <https://doc.rust-lang.org/std/ptr/index.html> and <https://doc.rust-lang.org/reference/behavior-considered-undefined.html>
+
 lint_invalid_reference_casting_assign_to_ref = assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
     .label = casting happened here
 
@@ -680,15 +684,6 @@ lint_private_extern_crate_reexport = extern crate `{$ident}` is private and cann
 lint_proc_macro_derive_resolution_fallback = cannot find {$ns} `{$ident}` in this scope
     .label = names from parent modules are not accessible without an explicit import
 
-lint_ptr_null_checks_fn_ptr = function pointers are not nullable, so checking them for null will always return false
-    .help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
-    .label = expression has type `{$orig_ty}`
-
-lint_ptr_null_checks_fn_ret = returned pointer of `{$fn_name}` call is never null, so checking it for null will always return false
-
-lint_ptr_null_checks_ref = references are not nullable, so checking them for null will always return false
-    .label = expression has type `{$orig_ty}`
-
 lint_query_instability = using `{$query}` can result in unstable query results
     .note = if you believe this case to be fine, allow this lint and add a comment explaining your rationale
 
@@ -981,6 +976,15 @@ lint_unused_result = unused result of type `{$ty}`
 
 lint_use_let_underscore_ignore_suggestion = use `let _ = ...` to ignore the expression or result
 
+lint_useless_ptr_null_checks_fn_ptr = function pointers are not nullable, so checking them for null will always return false
+    .help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
+    .label = expression has type `{$orig_ty}`
+
+lint_useless_ptr_null_checks_fn_ret = returned pointer of `{$fn_name}` call is never null, so checking it for null will always return false
+
+lint_useless_ptr_null_checks_ref = references are not nullable, so checking them for null will always return false
+    .label = expression has type `{$orig_ty}`
+
 lint_uses_power_alignment = repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type
 
 lint_variant_size_differences =
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 9dccd4a0552..dae0efcbbc4 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -29,6 +29,7 @@ use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
 use rustc_hir::intravisit::FnKind as HirFnKind;
 use rustc_hir::{Body, FnDecl, GenericParamKind, PatKind, PredicateOrigin};
 use rustc_middle::bug;
+use rustc_middle::lint::LevelAndSource;
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, Upcast, VariantDef};
@@ -330,7 +331,6 @@ impl EarlyLintPass for UnsafeCode {
         if let FnKind::Fn(
             ctxt,
             _,
-            _,
             ast::Fn {
                 sig: ast::FnSig { header: ast::FnHeader { safety: ast::Safety::Unsafe(_), .. }, .. },
                 body,
@@ -695,7 +695,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations {
         }
 
         // Avoid listing trait impls if the trait is allowed.
-        let (level, _) = cx.tcx.lint_level_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id());
+        let LevelAndSource { level, .. } =
+            cx.tcx.lint_level_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id());
         if level == Level::Allow {
             return;
         }
@@ -3116,6 +3117,7 @@ impl EarlyLintPass for SpecialModuleName {
         for item in &krate.items {
             if let ast::ItemKind::Mod(
                 _,
+                ident,
                 ast::ModKind::Unloaded | ast::ModKind::Loaded(_, ast::Inline::No, _, _),
             ) = item.kind
             {
@@ -3123,7 +3125,7 @@ impl EarlyLintPass for SpecialModuleName {
                     continue;
                 }
 
-                match item.ident.name.as_str() {
+                match ident.name.as_str() {
                     "lib" => cx.emit_span_lint(
                         SPECIAL_MODULE_NAME,
                         item.span,
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 017ae943e91..885a7308bdc 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -17,13 +17,12 @@ use rustc_hir::def_id::{CrateNum, DefId};
 use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
 use rustc_hir::{Pat, PatKind};
 use rustc_middle::bug;
+use rustc_middle::lint::LevelAndSource;
 use rustc_middle::middle::privacy::EffectiveVisibilities;
 use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
 use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths};
 use rustc_middle::ty::{self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode};
-use rustc_session::lint::{
-    FutureIncompatibleInfo, Level, Lint, LintBuffer, LintExpectationId, LintId,
-};
+use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintBuffer, LintExpectationId, LintId};
 use rustc_session::{LintStoreMarker, Session};
 use rustc_span::edit_distance::find_best_match_for_names;
 use rustc_span::{Ident, Span, Symbol, sym};
@@ -573,7 +572,7 @@ pub trait LintContext {
     }
 
     /// This returns the lint level for the given lint at the current location.
-    fn get_lint_level(&self, lint: &'static Lint) -> Level;
+    fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource;
 
     /// This function can be used to manually fulfill an expectation. This can
     /// be used for lints which contain several spans, and should be suppressed,
@@ -642,8 +641,8 @@ impl<'tcx> LintContext for LateContext<'tcx> {
         }
     }
 
-    fn get_lint_level(&self, lint: &'static Lint) -> Level {
-        self.tcx.lint_level_at_node(lint, self.last_node_with_lint_attrs).0
+    fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource {
+        self.tcx.lint_level_at_node(lint, self.last_node_with_lint_attrs)
     }
 }
 
@@ -663,8 +662,8 @@ impl LintContext for EarlyContext<'_> {
         self.builder.opt_span_lint(lint, span.map(|s| s.into()), decorate)
     }
 
-    fn get_lint_level(&self, lint: &'static Lint) -> Level {
-        self.builder.lint_level(lint).0
+    fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource {
+        self.builder.lint_level(lint)
     }
 }
 
diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
index c2404a7b843..26481b97076 100644
--- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs
+++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
@@ -392,7 +392,7 @@ where
                         }
                         _ => {
                             self.tcx.dcx().span_delayed_bug(
-                                self.tcx.hir().span(arg.hir_id()),
+                                self.tcx.hir_span(arg.hir_id()),
                                 "no valid for captured arg",
                             );
                         }
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index 8718fb807ec..f1fe07cfcfa 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -84,10 +84,10 @@ impl LintLevelSets {
     ) -> LevelAndSource {
         let lint = LintId::of(lint);
         let (level, mut src) = self.raw_lint_id_level(lint, idx, aux);
-        let level = reveal_actual_level(level, &mut src, sess, lint, |id| {
+        let (level, lint_id) = reveal_actual_level(level, &mut src, sess, lint, |id| {
             self.raw_lint_id_level(id, idx, aux)
         });
-        (level, src)
+        LevelAndSource { level, lint_id, src }
     }
 
     fn raw_lint_id_level(
@@ -95,17 +95,17 @@ impl LintLevelSets {
         id: LintId,
         mut idx: LintStackIndex,
         aux: Option<&FxIndexMap<LintId, LevelAndSource>>,
-    ) -> (Option<Level>, LintLevelSource) {
+    ) -> (Option<(Level, Option<LintExpectationId>)>, LintLevelSource) {
         if let Some(specs) = aux
-            && let Some(&(level, src)) = specs.get(&id)
+            && let Some(&LevelAndSource { level, lint_id, src }) = specs.get(&id)
         {
-            return (Some(level), src);
+            return (Some((level, lint_id)), src);
         }
 
         loop {
             let LintSet { ref specs, parent } = self.list[idx];
-            if let Some(&(level, src)) = specs.get(&id) {
-                return (Some(level), src);
+            if let Some(&LevelAndSource { level, lint_id, src }) = specs.get(&id) {
+                return (Some((level, lint_id)), src);
             }
             if idx == COMMAND_LINE {
                 return (None, LintLevelSource::Default);
@@ -131,8 +131,8 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LintId> {
         })
         .filter_map(|lint| {
             let lint_level = map.lint_level_id_at_node(tcx, LintId::of(lint), CRATE_HIR_ID);
-            if matches!(lint_level, (Level::Allow, ..))
-                || (matches!(lint_level, (.., LintLevelSource::Default)))
+            if matches!(lint_level.level, Level::Allow)
+                || (matches!(lint_level.src, LintLevelSource::Default))
                     && lint.default_level(tcx.sess.edition()) == Level::Allow
             {
                 Some(LintId::of(lint))
@@ -379,13 +379,7 @@ impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> {
     fn visit_attribute(&mut self, attribute: &'tcx hir::Attribute) {
         if matches!(
             Level::from_attr(attribute),
-            Some(
-                Level::Warn
-                    | Level::Deny
-                    | Level::Forbid
-                    | Level::Expect(..)
-                    | Level::ForceWarn(..),
-            )
+            Some((Level::Warn | Level::Deny | Level::Forbid | Level::Expect | Level::ForceWarn, _))
         ) {
             let store = unerased_lint_store(self.tcx.sess);
             // Lint attributes are always a metalist inside a
@@ -450,6 +444,19 @@ impl<'s> LintLevelsBuilder<'s, TopDown> {
         builder
     }
 
+    pub fn crate_root(
+        sess: &'s Session,
+        features: &'s Features,
+        lint_added_lints: bool,
+        store: &'s LintStore,
+        registered_tools: &'s RegisteredTools,
+        crate_attrs: &[ast::Attribute],
+    ) -> Self {
+        let mut builder = Self::new(sess, features, lint_added_lints, store, registered_tools);
+        builder.add(crate_attrs, true, None);
+        builder
+    }
+
     fn process_command_line(&mut self) {
         self.provider.cur = self
             .provider
@@ -528,9 +535,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
         for &(ref lint_name, level) in &self.sess.opts.lint_opts {
             // Checks the validity of lint names derived from the command line.
             let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
-            if lint_name_only == crate::WARNINGS.name_lower()
-                && matches!(level, Level::ForceWarn(_))
-            {
+            if lint_name_only == crate::WARNINGS.name_lower() && matches!(level, Level::ForceWarn) {
                 self.sess
                     .dcx()
                     .emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
@@ -573,7 +578,6 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                 _ => {}
             };
 
-            let orig_level = level;
             let lint_flag_val = Symbol::intern(lint_name);
 
             let Ok(ids) = self.store.find_lints(lint_name) else {
@@ -582,15 +586,15 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
             };
             for id in ids {
                 // ForceWarn and Forbid cannot be overridden
-                if let Some((Level::ForceWarn(_) | Level::Forbid, _)) =
+                if let Some(LevelAndSource { level: Level::ForceWarn | Level::Forbid, .. }) =
                     self.current_specs().get(&id)
                 {
                     continue;
                 }
 
                 if self.check_gated_lint(id, DUMMY_SP, true) {
-                    let src = LintLevelSource::CommandLine(lint_flag_val, orig_level);
-                    self.insert(id, (level, src));
+                    let src = LintLevelSource::CommandLine(lint_flag_val, level);
+                    self.insert(id, LevelAndSource { level, lint_id: None, src });
                 }
             }
         }
@@ -599,8 +603,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
     /// Attempts to insert the `id` to `level_src` map entry. If unsuccessful
     /// (e.g. if a forbid was already inserted on the same scope), then emits a
     /// diagnostic with no change to `specs`.
-    fn insert_spec(&mut self, id: LintId, (level, src): LevelAndSource) {
-        let (old_level, old_src) = self.provider.get_lint_level(id.lint, self.sess);
+    fn insert_spec(&mut self, id: LintId, LevelAndSource { level, lint_id, src }: LevelAndSource) {
+        let LevelAndSource { level: old_level, src: old_src, .. } =
+            self.provider.get_lint_level(id.lint, self.sess);
 
         // Setting to a non-forbid level is an error if the lint previously had
         // a forbid level. Note that this is not necessarily true even with a
@@ -672,7 +677,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
         // The lint `unfulfilled_lint_expectations` can't be expected, as it would suppress itself.
         // Handling expectations of this lint would add additional complexity with little to no
         // benefit. The expect level for this lint will therefore be ignored.
-        if let Level::Expect(_) = level
+        if let Level::Expect = level
             && id == LintId::of(UNFULFILLED_LINT_EXPECTATIONS)
         {
             return;
@@ -680,13 +685,16 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
 
         match (old_level, level) {
             // If the new level is an expectation store it in `ForceWarn`
-            (Level::ForceWarn(_), Level::Expect(expectation_id)) => {
-                self.insert(id, (Level::ForceWarn(Some(expectation_id)), old_src))
+            (Level::ForceWarn, Level::Expect) => {
+                self.insert(id, LevelAndSource { level: Level::ForceWarn, lint_id, src: old_src })
             }
             // Keep `ForceWarn` level but drop the expectation
-            (Level::ForceWarn(_), _) => self.insert(id, (Level::ForceWarn(None), old_src)),
+            (Level::ForceWarn, _) => self.insert(
+                id,
+                LevelAndSource { level: Level::ForceWarn, lint_id: None, src: old_src },
+            ),
             // Set the lint level as normal
-            _ => self.insert(id, (level, src)),
+            _ => self.insert(id, LevelAndSource { level, lint_id, src }),
         };
     }
 
@@ -701,7 +709,11 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
             if attr.has_name(sym::automatically_derived) {
                 self.insert(
                     LintId::of(SINGLE_USE_LIFETIMES),
-                    (Level::Allow, LintLevelSource::Default),
+                    LevelAndSource {
+                        level: Level::Allow,
+                        lint_id: None,
+                        src: LintLevelSource::Default,
+                    },
                 );
                 continue;
             }
@@ -712,15 +724,22 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                     .meta_item_list()
                     .is_some_and(|l| ast::attr::list_contains_name(&l, sym::hidden))
             {
-                self.insert(LintId::of(MISSING_DOCS), (Level::Allow, LintLevelSource::Default));
+                self.insert(
+                    LintId::of(MISSING_DOCS),
+                    LevelAndSource {
+                        level: Level::Allow,
+                        lint_id: None,
+                        src: LintLevelSource::Default,
+                    },
+                );
                 continue;
             }
 
-            let level = match Level::from_attr(attr) {
+            let (level, lint_id) = match Level::from_attr(attr) {
                 None => continue,
                 // This is the only lint level with a `LintExpectationId` that can be created from
                 // an attribute.
-                Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => {
+                Some((Level::Expect, Some(unstable_id))) if let Some(hir_id) = source_hir_id => {
                     let LintExpectationId::Unstable { lint_index: None, attr_id: _ } = unstable_id
                     else {
                         bug!("stable id Level::from_attr")
@@ -732,9 +751,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                         lint_index: None,
                     };
 
-                    Level::Expect(stable_id)
+                    (Level::Expect, Some(stable_id))
                 }
-                Some(lvl) => lvl,
+                Some((lvl, id)) => (lvl, id),
             };
 
             let Some(mut metas) = attr.meta_item_list() else { continue };
@@ -782,13 +801,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
             }
 
             for (lint_index, li) in metas.iter_mut().enumerate() {
-                let level = match level {
-                    Level::Expect(mut id) => {
-                        id.set_lint_index(Some(lint_index as u16));
-                        Level::Expect(id)
-                    }
-                    level => level,
-                };
+                let mut lint_id = lint_id;
+                if let Some(id) = &mut lint_id {
+                    id.set_lint_index(Some(lint_index as u16));
+                }
 
                 let sp = li.span();
                 let meta_item = match li {
@@ -920,7 +936,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                 let src = LintLevelSource::Node { name, span: sp, reason };
                 for &id in ids {
                     if self.check_gated_lint(id, sp, false) {
-                        self.insert_spec(id, (level, src));
+                        self.insert_spec(id, LevelAndSource { level, lint_id, src });
                     }
                 }
 
@@ -929,7 +945,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                 // overriding the lint level but instead add an expectation that can't be
                 // fulfilled. The lint message will include an explanation, that the
                 // `unfulfilled_lint_expectations` lint can't be expected.
-                if let Level::Expect(expect_id) = level {
+                if let (Level::Expect, Some(expect_id)) = (level, lint_id) {
                     // The `unfulfilled_lint_expectations` lint is not part of any lint
                     // groups. Therefore. we only need to check the slice if it contains a
                     // single lint.
@@ -951,7 +967,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
         }
 
         if self.lint_added_lints && !is_crate_node {
-            for (id, &(level, ref src)) in self.current_specs().iter() {
+            for (id, &LevelAndSource { level, ref src, .. }) in self.current_specs().iter() {
                 if !id.lint.crate_level_only {
                     continue;
                 }
@@ -989,10 +1005,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
 
         if self.lint_added_lints {
             let lint = builtin::UNKNOWN_LINTS;
-            let (level, src) = self.lint_level(builtin::UNKNOWN_LINTS);
+            let level = self.lint_level(builtin::UNKNOWN_LINTS);
             // FIXME: make this translatable
             #[allow(rustc::diagnostic_outside_of_impl)]
-            lint_level(self.sess, lint, level, src, Some(span.into()), |lint| {
+            lint_level(self.sess, lint, level, Some(span.into()), |lint| {
                 lint.primary_message(fluent::lint_unknown_gated_lint);
                 lint.arg("name", lint_id.lint.name_lower());
                 lint.note(fluent::lint_note);
@@ -1027,8 +1043,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
         span: Option<MultiSpan>,
         decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
     ) {
-        let (level, src) = self.lint_level(lint);
-        lint_level(self.sess, lint, level, src, span, decorate)
+        let level = self.lint_level(lint);
+        lint_level(self.sess, lint, level, span, decorate)
     }
 
     #[track_caller]
@@ -1038,16 +1054,16 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
         span: MultiSpan,
         decorate: impl for<'a> LintDiagnostic<'a, ()>,
     ) {
-        let (level, src) = self.lint_level(lint);
-        lint_level(self.sess, lint, level, src, Some(span), |lint| {
+        let level = self.lint_level(lint);
+        lint_level(self.sess, lint, level, Some(span), |lint| {
             decorate.decorate_lint(lint);
         });
     }
 
     #[track_caller]
     pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> LintDiagnostic<'a, ()>) {
-        let (level, src) = self.lint_level(lint);
-        lint_level(self.sess, lint, level, src, None, |lint| {
+        let level = self.lint_level(lint);
+        lint_level(self.sess, lint, level, None, |lint| {
             decorate.decorate_lint(lint);
         });
     }
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index cd474f1b7db..9b5c564d332 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -80,6 +80,7 @@ mod types;
 mod unit_bindings;
 mod unqualified_local_imports;
 mod unused;
+mod utils;
 
 use async_closures::AsyncClosureUsage;
 use async_fn_in_trait::AsyncFnInTrait;
@@ -129,6 +130,7 @@ pub use context::{
 };
 pub use early::{EarlyCheckNode, check_ast_node};
 pub use late::{check_crate, late_lint_mod, unerased_lint_store};
+pub use levels::LintLevelsBuilder;
 pub use passes::{EarlyLintPass, LateLintPass};
 pub use rustc_session::lint::Level::{self, *};
 pub use rustc_session::lint::{
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 036d68d13fa..55d010e6d34 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -591,24 +591,40 @@ pub(crate) struct ExpectationNote {
 
 // ptr_nulls.rs
 #[derive(LintDiagnostic)]
-pub(crate) enum PtrNullChecksDiag<'a> {
-    #[diag(lint_ptr_null_checks_fn_ptr)]
-    #[help(lint_help)]
+pub(crate) enum UselessPtrNullChecksDiag<'a> {
+    #[diag(lint_useless_ptr_null_checks_fn_ptr)]
+    #[help]
     FnPtr {
         orig_ty: Ty<'a>,
         #[label]
         label: Span,
     },
-    #[diag(lint_ptr_null_checks_ref)]
+    #[diag(lint_useless_ptr_null_checks_ref)]
     Ref {
         orig_ty: Ty<'a>,
         #[label]
         label: Span,
     },
-    #[diag(lint_ptr_null_checks_fn_ret)]
+    #[diag(lint_useless_ptr_null_checks_fn_ret)]
     FnRet { fn_name: Ident },
 }
 
+#[derive(LintDiagnostic)]
+pub(crate) enum InvalidNullArgumentsDiag {
+    #[diag(lint_invalid_null_arguments)]
+    #[help(lint_doc)]
+    NullPtrInline {
+        #[label(lint_origin)]
+        null_span: Span,
+    },
+    #[diag(lint_invalid_null_arguments)]
+    #[help(lint_doc)]
+    NullPtrThroughBinding {
+        #[note(lint_origin)]
+        null_span: Span,
+    },
+}
+
 // for_loops_over_fallibles.rs
 #[derive(LintDiagnostic)]
 #[diag(lint_for_loops_over_fallibles)]
diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs
index 66e207a451e..9c11fb41aa6 100644
--- a/compiler/rustc_lint/src/non_ascii_idents.rs
+++ b/compiler/rustc_lint/src/non_ascii_idents.rs
@@ -159,12 +159,13 @@ impl EarlyLintPass for NonAsciiIdents {
         use rustc_span::Span;
         use unicode_security::GeneralSecurityProfile;
 
-        let check_non_ascii_idents = cx.builder.lint_level(NON_ASCII_IDENTS).0 != Level::Allow;
+        let check_non_ascii_idents = cx.builder.lint_level(NON_ASCII_IDENTS).level != Level::Allow;
         let check_uncommon_codepoints =
-            cx.builder.lint_level(UNCOMMON_CODEPOINTS).0 != Level::Allow;
-        let check_confusable_idents = cx.builder.lint_level(CONFUSABLE_IDENTS).0 != Level::Allow;
+            cx.builder.lint_level(UNCOMMON_CODEPOINTS).level != Level::Allow;
+        let check_confusable_idents =
+            cx.builder.lint_level(CONFUSABLE_IDENTS).level != Level::Allow;
         let check_mixed_script_confusables =
-            cx.builder.lint_level(MIXED_SCRIPT_CONFUSABLES).0 != Level::Allow;
+            cx.builder.lint_level(MIXED_SCRIPT_CONFUSABLES).level != Level::Allow;
 
         if !check_non_ascii_idents
             && !check_uncommon_codepoints
diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs
index 752636ccaf0..df567e80e55 100644
--- a/compiler/rustc_lint/src/nonstandard_style.rs
+++ b/compiler/rustc_lint/src/nonstandard_style.rs
@@ -172,20 +172,22 @@ impl EarlyLintPass for NonCamelCaseTypes {
         }
 
         match &it.kind {
-            ast::ItemKind::TyAlias(..)
-            | ast::ItemKind::Enum(..)
-            | ast::ItemKind::Struct(..)
-            | ast::ItemKind::Union(..) => self.check_case(cx, "type", &it.ident),
-            ast::ItemKind::Trait(..) => self.check_case(cx, "trait", &it.ident),
-            ast::ItemKind::TraitAlias(..) => self.check_case(cx, "trait alias", &it.ident),
+            ast::ItemKind::TyAlias(box ast::TyAlias { ident, .. })
+            | ast::ItemKind::Enum(ident, ..)
+            | ast::ItemKind::Struct(ident, ..)
+            | ast::ItemKind::Union(ident, ..) => self.check_case(cx, "type", ident),
+            ast::ItemKind::Trait(box ast::Trait { ident, .. }) => {
+                self.check_case(cx, "trait", ident)
+            }
+            ast::ItemKind::TraitAlias(ident, _, _) => self.check_case(cx, "trait alias", ident),
 
             // N.B. This check is only for inherent associated types, so that we don't lint against
             // trait impls where we should have warned for the trait definition already.
             ast::ItemKind::Impl(box ast::Impl { of_trait: None, items, .. }) => {
                 for it in items {
                     // FIXME: this doesn't respect `#[allow(..)]` on the item itself.
-                    if let ast::AssocItemKind::Type(..) = it.kind {
-                        self.check_case(cx, "associated type", &it.ident);
+                    if let ast::AssocItemKind::Type(alias) = &it.kind {
+                        self.check_case(cx, "associated type", &alias.ident);
                     }
                 }
             }
@@ -194,8 +196,8 @@ impl EarlyLintPass for NonCamelCaseTypes {
     }
 
     fn check_trait_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) {
-        if let ast::AssocItemKind::Type(..) = it.kind {
-            self.check_case(cx, "associated type", &it.ident);
+        if let ast::AssocItemKind::Type(alias) = &it.kind {
+            self.check_case(cx, "associated type", &alias.ident);
         }
     }
 
diff --git a/compiler/rustc_lint/src/ptr_nulls.rs b/compiler/rustc_lint/src/ptr_nulls.rs
index 1489f9de819..826bce2c315 100644
--- a/compiler/rustc_lint/src/ptr_nulls.rs
+++ b/compiler/rustc_lint/src/ptr_nulls.rs
@@ -1,9 +1,11 @@
 use rustc_ast::LitKind;
 use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind};
+use rustc_middle::ty::RawPtr;
 use rustc_session::{declare_lint, declare_lint_pass};
-use rustc_span::sym;
+use rustc_span::{Span, sym};
 
-use crate::lints::PtrNullChecksDiag;
+use crate::lints::{InvalidNullArgumentsDiag, UselessPtrNullChecksDiag};
+use crate::utils::peel_casts;
 use crate::{LateContext, LateLintPass, LintContext};
 
 declare_lint! {
@@ -31,17 +33,40 @@ declare_lint! {
     "useless checking of non-null-typed pointer"
 }
 
-declare_lint_pass!(PtrNullChecks => [USELESS_PTR_NULL_CHECKS]);
+declare_lint! {
+    /// The `invalid_null_arguments` lint checks for invalid usage of null pointers in arguments.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// # use std::{slice, ptr};
+    /// // Undefined behavior
+    /// # let _slice: &[u8] =
+    /// unsafe { slice::from_raw_parts(ptr::null(), 0) };
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Calling methods whos safety invariants requires non-null ptr with a null pointer
+    /// is [Undefined Behavior](https://doc.rust-lang.org/reference/behavior-considered-undefined.html)!
+    INVALID_NULL_ARGUMENTS,
+    Deny,
+    "invalid null pointer in arguments"
+}
+
+declare_lint_pass!(PtrNullChecks => [USELESS_PTR_NULL_CHECKS, INVALID_NULL_ARGUMENTS]);
 
 /// This function checks if the expression is from a series of consecutive casts,
 /// ie. `(my_fn as *const _ as *mut _).cast_mut()` and whether the original expression is either
 /// a fn ptr, a reference, or a function call whose definition is
 /// annotated with `#![rustc_never_returns_null_ptr]`.
 /// If this situation is present, the function returns the appropriate diagnostic.
-fn incorrect_check<'a, 'tcx: 'a>(
+fn useless_check<'a, 'tcx: 'a>(
     cx: &'a LateContext<'tcx>,
     mut e: &'a Expr<'a>,
-) -> Option<PtrNullChecksDiag<'tcx>> {
+) -> Option<UselessPtrNullChecksDiag<'tcx>> {
     let mut had_at_least_one_cast = false;
     loop {
         e = e.peel_blocks();
@@ -50,14 +75,14 @@ fn incorrect_check<'a, 'tcx: 'a>(
             && cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr)
             && let Some(fn_name) = cx.tcx.opt_item_ident(def_id)
         {
-            return Some(PtrNullChecksDiag::FnRet { fn_name });
+            return Some(UselessPtrNullChecksDiag::FnRet { fn_name });
         } else if let ExprKind::Call(path, _args) = e.kind
             && let ExprKind::Path(ref qpath) = path.kind
             && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
             && cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr)
             && let Some(fn_name) = cx.tcx.opt_item_ident(def_id)
         {
-            return Some(PtrNullChecksDiag::FnRet { fn_name });
+            return Some(UselessPtrNullChecksDiag::FnRet { fn_name });
         }
         e = if let ExprKind::Cast(expr, t) = e.kind
             && let TyKind::Ptr(_) = t.kind
@@ -73,9 +98,9 @@ fn incorrect_check<'a, 'tcx: 'a>(
         } else if had_at_least_one_cast {
             let orig_ty = cx.typeck_results().expr_ty(e);
             return if orig_ty.is_fn() {
-                Some(PtrNullChecksDiag::FnPtr { orig_ty, label: e.span })
+                Some(UselessPtrNullChecksDiag::FnPtr { orig_ty, label: e.span })
             } else if orig_ty.is_ref() {
-                Some(PtrNullChecksDiag::Ref { orig_ty, label: e.span })
+                Some(UselessPtrNullChecksDiag::Ref { orig_ty, label: e.span })
             } else {
                 None
             };
@@ -85,6 +110,25 @@ fn incorrect_check<'a, 'tcx: 'a>(
     }
 }
 
+/// Checks if the given expression is a null pointer (modulo casting)
+fn is_null_ptr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Span> {
+    let (expr, _) = peel_casts(cx, expr);
+
+    if let ExprKind::Call(path, []) = expr.kind
+        && let ExprKind::Path(ref qpath) = path.kind
+        && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
+        && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
+    {
+        (diag_item == sym::ptr_null || diag_item == sym::ptr_null_mut).then_some(expr.span)
+    } else if let ExprKind::Lit(spanned) = expr.kind
+        && let LitKind::Int(v, _) = spanned.node
+    {
+        (v == 0).then_some(expr.span)
+    } else {
+        None
+    }
+}
+
 impl<'tcx> LateLintPass<'tcx> for PtrNullChecks {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         match expr.kind {
@@ -97,12 +141,68 @@ impl<'tcx> LateLintPass<'tcx> for PtrNullChecks {
                         cx.tcx.get_diagnostic_name(def_id),
                         Some(sym::ptr_const_is_null | sym::ptr_is_null)
                     )
-                    && let Some(diag) = incorrect_check(cx, arg) =>
+                    && let Some(diag) = useless_check(cx, arg) =>
             {
                 cx.emit_span_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag)
             }
 
             // Catching:
+            // <path>(arg...) where `arg` is null-ptr and `path` is a fn that expect non-null-ptr
+            ExprKind::Call(path, args)
+                if let ExprKind::Path(ref qpath) = path.kind
+                    && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
+                    && let Some(diag_name) = cx.tcx.get_diagnostic_name(def_id) =>
+            {
+                // `arg` positions where null would cause U.B and whenever ZST are allowed.
+                //
+                // We should probably have a `rustc` attribute, but checking them is costly,
+                // maybe if we checked for null ptr first, it would be acceptable?
+                let (arg_indices, are_zsts_allowed): (&[_], _) = match diag_name {
+                    sym::ptr_read
+                    | sym::ptr_read_unaligned
+                    | sym::ptr_read_volatile
+                    | sym::ptr_replace
+                    | sym::ptr_write
+                    | sym::ptr_write_bytes
+                    | sym::ptr_write_unaligned
+                    | sym::ptr_write_volatile => (&[0], true),
+                    sym::slice_from_raw_parts | sym::slice_from_raw_parts_mut => (&[0], false),
+                    sym::ptr_copy
+                    | sym::ptr_copy_nonoverlapping
+                    | sym::ptr_swap
+                    | sym::ptr_swap_nonoverlapping => (&[0, 1], true),
+                    _ => return,
+                };
+
+                for &arg_idx in arg_indices {
+                    if let Some(arg) = args.get(arg_idx)
+                        && let Some(null_span) = is_null_ptr(cx, arg)
+                        && let Some(ty) = cx.typeck_results().expr_ty_opt(arg)
+                        && let RawPtr(ty, _mutbl) = ty.kind()
+                    {
+                        // If ZST are fine, don't lint on them
+                        let typing_env = cx.typing_env();
+                        if are_zsts_allowed
+                            && cx
+                                .tcx
+                                .layout_of(typing_env.as_query_input(*ty))
+                                .is_ok_and(|layout| layout.is_1zst())
+                        {
+                            break;
+                        }
+
+                        let diag = if arg.span.contains(null_span) {
+                            InvalidNullArgumentsDiag::NullPtrInline { null_span }
+                        } else {
+                            InvalidNullArgumentsDiag::NullPtrThroughBinding { null_span }
+                        };
+
+                        cx.emit_span_lint(INVALID_NULL_ARGUMENTS, expr.span, diag)
+                    }
+                }
+            }
+
+            // Catching:
             // (fn_ptr as *<const/mut> <ty>).is_null()
             ExprKind::MethodCall(_, receiver, _, _)
                 if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
@@ -110,18 +210,18 @@ impl<'tcx> LateLintPass<'tcx> for PtrNullChecks {
                         cx.tcx.get_diagnostic_name(def_id),
                         Some(sym::ptr_const_is_null | sym::ptr_is_null)
                     )
-                    && let Some(diag) = incorrect_check(cx, receiver) =>
+                    && let Some(diag) = useless_check(cx, receiver) =>
             {
                 cx.emit_span_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag)
             }
 
             ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Eq) => {
                 let to_check: &Expr<'_>;
-                let diag: PtrNullChecksDiag<'_>;
-                if let Some(ddiag) = incorrect_check(cx, left) {
+                let diag: UselessPtrNullChecksDiag<'_>;
+                if let Some(ddiag) = useless_check(cx, left) {
                     to_check = right;
                     diag = ddiag;
-                } else if let Some(ddiag) = incorrect_check(cx, right) {
+                } else if let Some(ddiag) = useless_check(cx, right) {
                     to_check = left;
                     diag = ddiag;
                 } else {
diff --git a/compiler/rustc_lint/src/reference_casting.rs b/compiler/rustc_lint/src/reference_casting.rs
index 7c6656f91c9..1d4d380cb68 100644
--- a/compiler/rustc_lint/src/reference_casting.rs
+++ b/compiler/rustc_lint/src/reference_casting.rs
@@ -6,6 +6,7 @@ use rustc_session::{declare_lint, declare_lint_pass};
 use rustc_span::sym;
 
 use crate::lints::InvalidReferenceCastingDiag;
+use crate::utils::peel_casts;
 use crate::{LateContext, LateLintPass, LintContext};
 
 declare_lint! {
@@ -235,46 +236,3 @@ fn is_cast_to_bigger_memory_layout<'tcx>(
         None
     }
 }
-
-fn peel_casts<'tcx>(cx: &LateContext<'tcx>, mut e: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, bool) {
-    let mut gone_trough_unsafe_cell_raw_get = false;
-
-    loop {
-        e = e.peel_blocks();
-        // <expr> as ...
-        e = if let ExprKind::Cast(expr, _) = e.kind {
-            expr
-        // <expr>.cast(), <expr>.cast_mut() or <expr>.cast_const()
-        } else if let ExprKind::MethodCall(_, expr, [], _) = e.kind
-            && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
-            && matches!(
-                cx.tcx.get_diagnostic_name(def_id),
-                Some(sym::ptr_cast | sym::const_ptr_cast | sym::ptr_cast_mut | sym::ptr_cast_const)
-            )
-        {
-            expr
-        // ptr::from_ref(<expr>), UnsafeCell::raw_get(<expr>) or mem::transmute<_, _>(<expr>)
-        } else if let ExprKind::Call(path, [arg]) = e.kind
-            && let ExprKind::Path(ref qpath) = path.kind
-            && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
-            && matches!(
-                cx.tcx.get_diagnostic_name(def_id),
-                Some(sym::ptr_from_ref | sym::unsafe_cell_raw_get | sym::transmute)
-            )
-        {
-            if cx.tcx.is_diagnostic_item(sym::unsafe_cell_raw_get, def_id) {
-                gone_trough_unsafe_cell_raw_get = true;
-            }
-            arg
-        } else {
-            let init = cx.expr_or_init(e);
-            if init.hir_id != e.hir_id {
-                init
-            } else {
-                break;
-            }
-        };
-    }
-
-    (e, gone_trough_unsafe_cell_raw_get)
-}
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 7109fefbe78..a6c82f574a1 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -14,7 +14,7 @@ use rustc_middle::ty::{
 };
 use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
 use rustc_span::def_id::LocalDefId;
-use rustc_span::{Span, Symbol, source_map, sym};
+use rustc_span::{Span, Symbol, sym};
 use tracing::debug;
 use {rustc_ast as ast, rustc_hir as hir};
 
@@ -223,7 +223,7 @@ impl TypeLimits {
 fn lint_nan<'tcx>(
     cx: &LateContext<'tcx>,
     e: &'tcx hir::Expr<'tcx>,
-    binop: hir::BinOp,
+    binop: hir::BinOpKind,
     l: &'tcx hir::Expr<'tcx>,
     r: &'tcx hir::Expr<'tcx>,
 ) {
@@ -262,19 +262,19 @@ fn lint_nan<'tcx>(
         InvalidNanComparisons::EqNe { suggestion }
     }
 
-    let lint = match binop.node {
+    let lint = match binop {
         hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, l) => {
             eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
                 nan_plus_binop: l_span.until(r_span),
                 float: r_span.shrink_to_hi(),
-                neg: (binop.node == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()),
+                neg: (binop == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()),
             })
         }
         hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, r) => {
             eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
                 nan_plus_binop: l_span.shrink_to_hi().to(r_span),
                 float: l_span.shrink_to_hi(),
-                neg: (binop.node == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()),
+                neg: (binop == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()),
             })
         }
         hir::BinOpKind::Lt | hir::BinOpKind::Le | hir::BinOpKind::Gt | hir::BinOpKind::Ge
@@ -560,11 +560,11 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
                 }
             }
             hir::ExprKind::Binary(binop, ref l, ref r) => {
-                if is_comparison(binop) {
-                    if !check_limits(cx, binop, l, r) {
+                if is_comparison(binop.node) {
+                    if !check_limits(cx, binop.node, l, r) {
                         cx.emit_span_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
                     } else {
-                        lint_nan(cx, e, binop, l, r);
+                        lint_nan(cx, e, binop.node, l, r);
                         let cmpop = ComparisonOp::BinOp(binop.node);
                         lint_wide_pointer(cx, e, cmpop, l, r);
                         lint_fn_pointer(cx, e, cmpop, l, r);
@@ -591,8 +591,8 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
             _ => {}
         };
 
-        fn is_valid<T: PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool {
-            match binop.node {
+        fn is_valid<T: PartialOrd>(binop: hir::BinOpKind, v: T, min: T, max: T) -> bool {
+            match binop {
                 hir::BinOpKind::Lt => v > min && v <= max,
                 hir::BinOpKind::Le => v >= min && v < max,
                 hir::BinOpKind::Gt => v >= min && v < max,
@@ -602,22 +602,19 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
             }
         }
 
-        fn rev_binop(binop: hir::BinOp) -> hir::BinOp {
-            source_map::respan(
-                binop.span,
-                match binop.node {
-                    hir::BinOpKind::Lt => hir::BinOpKind::Gt,
-                    hir::BinOpKind::Le => hir::BinOpKind::Ge,
-                    hir::BinOpKind::Gt => hir::BinOpKind::Lt,
-                    hir::BinOpKind::Ge => hir::BinOpKind::Le,
-                    _ => return binop,
-                },
-            )
+        fn rev_binop(binop: hir::BinOpKind) -> hir::BinOpKind {
+            match binop {
+                hir::BinOpKind::Lt => hir::BinOpKind::Gt,
+                hir::BinOpKind::Le => hir::BinOpKind::Ge,
+                hir::BinOpKind::Gt => hir::BinOpKind::Lt,
+                hir::BinOpKind::Ge => hir::BinOpKind::Le,
+                _ => binop,
+            }
         }
 
         fn check_limits(
             cx: &LateContext<'_>,
-            binop: hir::BinOp,
+            binop: hir::BinOpKind,
             l: &hir::Expr<'_>,
             r: &hir::Expr<'_>,
         ) -> bool {
@@ -659,9 +656,9 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
             }
         }
 
-        fn is_comparison(binop: hir::BinOp) -> bool {
+        fn is_comparison(binop: hir::BinOpKind) -> bool {
             matches!(
-                binop.node,
+                binop,
                 hir::BinOpKind::Eq
                     | hir::BinOpKind::Lt
                     | hir::BinOpKind::Le
@@ -1403,7 +1400,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
             CItemKind::Definition => "fn",
         };
         let span_note = if let ty::Adt(def, _) = ty.kind()
-            && let Some(sp) = self.cx.tcx.hir().span_if_local(def.did())
+            && let Some(sp) = self.cx.tcx.hir_span_if_local(def.did())
         {
             Some(sp)
         } else {
@@ -1634,6 +1631,9 @@ impl ImproperCTypesDefinitions {
             return true;
         } else if let Adt(adt_def, _) = ty.kind()
             && adt_def.is_struct()
+            && adt_def.repr().c()
+            && !adt_def.repr().packed()
+            && adt_def.repr().align.is_none()
         {
             let struct_variant = adt_def.variant(VariantIdx::ZERO);
             // Within a nested struct, all fields are examined to correctly
@@ -1655,8 +1655,11 @@ impl ImproperCTypesDefinitions {
         item: &'tcx hir::Item<'tcx>,
     ) {
         let adt_def = cx.tcx.adt_def(item.owner_id.to_def_id());
+        // repr(C) structs also with packed or aligned representation
+        // should be ignored.
         if adt_def.repr().c()
             && !adt_def.repr().packed()
+            && adt_def.repr().align.is_none()
             && cx.tcx.sess.target.os == "aix"
             && !adt_def.all_fields().next().is_none()
         {
diff --git a/compiler/rustc_lint/src/utils.rs b/compiler/rustc_lint/src/utils.rs
new file mode 100644
index 00000000000..a7295d9c532
--- /dev/null
+++ b/compiler/rustc_lint/src/utils.rs
@@ -0,0 +1,55 @@
+use rustc_hir::{Expr, ExprKind};
+use rustc_span::sym;
+
+use crate::LateContext;
+
+/// Given an expression, peel all of casts (`<expr> as ...`, `<expr>.cast{,_mut,_const}()`,
+/// `ptr::from_ref(<expr>)`, ...) and init expressions.
+///
+/// Returns the innermost expression and a boolean representing if one of the casts was
+/// `UnsafeCell::raw_get(<expr>)`
+pub(crate) fn peel_casts<'tcx>(
+    cx: &LateContext<'tcx>,
+    mut e: &'tcx Expr<'tcx>,
+) -> (&'tcx Expr<'tcx>, bool) {
+    let mut gone_trough_unsafe_cell_raw_get = false;
+
+    loop {
+        e = e.peel_blocks();
+        // <expr> as ...
+        e = if let ExprKind::Cast(expr, _) = e.kind {
+            expr
+        // <expr>.cast(), <expr>.cast_mut() or <expr>.cast_const()
+        } else if let ExprKind::MethodCall(_, expr, [], _) = e.kind
+            && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
+            && matches!(
+                cx.tcx.get_diagnostic_name(def_id),
+                Some(sym::ptr_cast | sym::const_ptr_cast | sym::ptr_cast_mut | sym::ptr_cast_const)
+            )
+        {
+            expr
+        // ptr::from_ref(<expr>), UnsafeCell::raw_get(<expr>) or mem::transmute<_, _>(<expr>)
+        } else if let ExprKind::Call(path, [arg]) = e.kind
+            && let ExprKind::Path(ref qpath) = path.kind
+            && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
+            && matches!(
+                cx.tcx.get_diagnostic_name(def_id),
+                Some(sym::ptr_from_ref | sym::unsafe_cell_raw_get | sym::transmute)
+            )
+        {
+            if cx.tcx.is_diagnostic_item(sym::unsafe_cell_raw_get, def_id) {
+                gone_trough_unsafe_cell_raw_get = true;
+            }
+            arg
+        } else {
+            let init = cx.expr_or_init(e);
+            if init.hir_id != e.hir_id {
+                init
+            } else {
+                break;
+            }
+        };
+    }
+
+    (e, gone_trough_unsafe_cell_raw_get)
+}
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 46b4b1d4383..7fdbae3a59d 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -8,7 +8,8 @@ use rustc_data_structures::stable_hasher::{
 };
 use rustc_error_messages::{DiagMessage, MultiSpan};
 use rustc_hir::def::Namespace;
-use rustc_hir::{HashStableContext, HirId, MissingLifetimeKind};
+use rustc_hir::def_id::DefPathHash;
+use rustc_hir::{HashStableContext, HirId, ItemLocalId, MissingLifetimeKind};
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 pub use rustc_span::edition::Edition;
 use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym};
@@ -102,7 +103,7 @@ pub enum Applicability {
 /// The index values have a type of `u16` to reduce the size of the `LintExpectationId`.
 /// It's reasonable to assume that no user will define 2^16 attributes on one node or
 /// have that amount of lints listed. `u16` values should therefore suffice.
-#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, Encodable, Decodable)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Encodable, Decodable)]
 pub enum LintExpectationId {
     /// Used for lints emitted during the `EarlyLintPass`. This id is not
     /// hash stable and should not be cached.
@@ -156,13 +157,14 @@ impl<HCX: rustc_hir::HashStableContext> HashStable<HCX> for LintExpectationId {
 }
 
 impl<HCX: rustc_hir::HashStableContext> ToStableHashKey<HCX> for LintExpectationId {
-    type KeyType = (HirId, u16, u16);
+    type KeyType = (DefPathHash, ItemLocalId, u16, u16);
 
     #[inline]
-    fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType {
+    fn to_stable_hash_key(&self, hcx: &HCX) -> Self::KeyType {
         match self {
             LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => {
-                (*hir_id, *attr_index, *lint_index)
+                let (def_path_hash, lint_idx) = hir_id.to_stable_hash_key(hcx);
+                (def_path_hash, lint_idx, *attr_index, *lint_index)
             }
             _ => {
                 unreachable!("HashStable should only be called for a filled `LintExpectationId`")
@@ -199,9 +201,9 @@ pub enum Level {
     ///
     /// See RFC 2383.
     ///
-    /// The [`LintExpectationId`] is used to later link a lint emission to the actual
+    /// Requires a [`LintExpectationId`] to later link a lint emission to the actual
     /// expectation. It can be ignored in most cases.
-    Expect(LintExpectationId),
+    Expect,
     /// The `warn` level will produce a warning if the lint was violated, however the
     /// compiler will continue with its execution.
     Warn,
@@ -209,9 +211,9 @@ pub enum Level {
     /// to ensure that a lint can't be suppressed. This lint level can currently only be set
     /// via the console and is therefore session specific.
     ///
-    /// The [`LintExpectationId`] is intended to fulfill expectations marked via the
+    /// Requires a [`LintExpectationId`] to fulfill expectations marked via the
     /// `#[expect]` attribute, that will still be suppressed due to the level.
-    ForceWarn(Option<LintExpectationId>),
+    ForceWarn,
     /// The `deny` level will produce an error and stop further execution after the lint
     /// pass is complete.
     Deny,
@@ -225,9 +227,9 @@ impl Level {
     pub fn as_str(self) -> &'static str {
         match self {
             Level::Allow => "allow",
-            Level::Expect(_) => "expect",
+            Level::Expect => "expect",
             Level::Warn => "warn",
-            Level::ForceWarn(_) => "force-warn",
+            Level::ForceWarn => "force-warn",
             Level::Deny => "deny",
             Level::Forbid => "forbid",
         }
@@ -246,24 +248,30 @@ impl Level {
     }
 
     /// Converts an `Attribute` to a level.
-    pub fn from_attr(attr: &impl AttributeExt) -> Option<Self> {
+    pub fn from_attr(attr: &impl AttributeExt) -> Option<(Self, Option<LintExpectationId>)> {
         Self::from_symbol(attr.name_or_empty(), || Some(attr.id()))
     }
 
     /// Converts a `Symbol` to a level.
-    pub fn from_symbol(s: Symbol, id: impl FnOnce() -> Option<AttrId>) -> Option<Self> {
+    pub fn from_symbol(
+        s: Symbol,
+        id: impl FnOnce() -> Option<AttrId>,
+    ) -> Option<(Self, Option<LintExpectationId>)> {
         match s {
-            sym::allow => Some(Level::Allow),
+            sym::allow => Some((Level::Allow, None)),
             sym::expect => {
                 if let Some(attr_id) = id() {
-                    Some(Level::Expect(LintExpectationId::Unstable { attr_id, lint_index: None }))
+                    Some((
+                        Level::Expect,
+                        Some(LintExpectationId::Unstable { attr_id, lint_index: None }),
+                    ))
                 } else {
                     None
                 }
             }
-            sym::warn => Some(Level::Warn),
-            sym::deny => Some(Level::Deny),
-            sym::forbid => Some(Level::Forbid),
+            sym::warn => Some((Level::Warn, None)),
+            sym::deny => Some((Level::Deny, None)),
+            sym::forbid => Some((Level::Forbid, None)),
             _ => None,
         }
     }
@@ -274,8 +282,8 @@ impl Level {
             Level::Deny => "-D",
             Level::Forbid => "-F",
             Level::Allow => "-A",
-            Level::ForceWarn(_) => "--force-warn",
-            Level::Expect(_) => {
+            Level::ForceWarn => "--force-warn",
+            Level::Expect => {
                 unreachable!("the expect level does not have a commandline flag")
             }
         }
@@ -283,17 +291,10 @@ impl Level {
 
     pub fn is_error(self) -> bool {
         match self {
-            Level::Allow | Level::Expect(_) | Level::Warn | Level::ForceWarn(_) => false,
+            Level::Allow | Level::Expect | Level::Warn | Level::ForceWarn => false,
             Level::Deny | Level::Forbid => true,
         }
     }
-
-    pub fn get_expectation_id(&self) -> Option<LintExpectationId> {
-        match self {
-            Level::Expect(id) | Level::ForceWarn(Some(id)) => Some(*id),
-            _ => None,
-        }
-    }
 }
 
 /// Specification of a single lint.
diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
index b8884486c33..4695de8ea09 100644
--- a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
@@ -47,7 +47,6 @@ struct LLVMRustMCDCBranchParameters {
   int16_t ConditionIDs[2];
 };
 
-#if LLVM_VERSION_GE(19, 0)
 static coverage::mcdc::BranchParameters
 fromRust(LLVMRustMCDCBranchParameters Params) {
   return coverage::mcdc::BranchParameters(
@@ -59,7 +58,6 @@ fromRust(LLVMRustMCDCDecisionParameters Params) {
   return coverage::mcdc::DecisionParameters(Params.BitmapIdx,
                                             Params.NumConditions);
 }
-#endif
 
 // Must match the layout of
 // `rustc_codegen_llvm::coverageinfo::ffi::CoverageSpan`.
@@ -203,7 +201,6 @@ extern "C" void LLVMRustCoverageWriteFunctionMappingsToBuffer(
         Region.Span.LineEnd, Region.Span.ColumnEnd));
   }
 
-#if LLVM_VERSION_GE(19, 0)
   // MC/DC branch regions:
   for (const auto &Region : ArrayRef(MCDCBranchRegions, NumMCDCBranchRegions)) {
     MappingRegions.push_back(coverage::CounterMappingRegion::makeBranchRegion(
@@ -221,7 +218,6 @@ extern "C" void LLVMRustCoverageWriteFunctionMappingsToBuffer(
         Region.Span.LineStart, Region.Span.ColumnStart, Region.Span.LineEnd,
         Region.Span.ColumnEnd));
   }
-#endif
 
   // Write the converted expressions and mappings to a byte buffer.
   auto CoverageMappingWriter = coverage::CoverageMappingWriter(
diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
index 86f1bcc46ee..e02c80c50b1 100644
--- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
@@ -47,10 +47,7 @@
 #include <vector>
 
 // Conditional includes prevent clang-format from fully sorting the list,
-// so keep them separate.
-#if LLVM_VERSION_GE(19, 0)
-#include "llvm/Support/PGOOptions.h"
-#endif
+// so if any are needed, keep them separate down here.
 
 using namespace llvm;
 
@@ -432,31 +429,15 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
   }
   if (!strcmp("zlib", DebugInfoCompression) &&
       llvm::compression::zlib::isAvailable()) {
-#if LLVM_VERSION_GE(19, 0)
     Options.MCOptions.CompressDebugSections = DebugCompressionType::Zlib;
-#else
-    Options.CompressDebugSections = DebugCompressionType::Zlib;
-#endif
   } else if (!strcmp("zstd", DebugInfoCompression) &&
              llvm::compression::zstd::isAvailable()) {
-#if LLVM_VERSION_GE(19, 0)
     Options.MCOptions.CompressDebugSections = DebugCompressionType::Zstd;
-#else
-    Options.CompressDebugSections = DebugCompressionType::Zstd;
-#endif
   } else if (!strcmp("none", DebugInfoCompression)) {
-#if LLVM_VERSION_GE(19, 0)
     Options.MCOptions.CompressDebugSections = DebugCompressionType::None;
-#else
-    Options.CompressDebugSections = DebugCompressionType::None;
-#endif
   }
 
-#if LLVM_VERSION_GE(19, 0)
   Options.MCOptions.X86RelaxRelocations = RelaxELFRelocations;
-#else
-  Options.RelaxELFRelocations = RelaxELFRelocations;
-#endif
   Options.UseInitArray = UseInitArray;
   Options.EmulatedTLS = UseEmulatedTls;
 
@@ -753,34 +734,23 @@ extern "C" LLVMRustResult LLVMRustOptimize(
   auto FS = vfs::getRealFileSystem();
   if (PGOGenPath) {
     assert(!PGOUsePath && !PGOSampleUsePath);
-    PGOOpt = PGOOptions(PGOGenPath, "", "", "", FS, PGOOptions::IRInstr,
-                        PGOOptions::NoCSAction,
-#if LLVM_VERSION_GE(19, 0)
-                        PGOOptions::ColdFuncOpt::Default,
-#endif
-                        DebugInfoForProfiling);
+    PGOOpt = PGOOptions(
+        PGOGenPath, "", "", "", FS, PGOOptions::IRInstr, PGOOptions::NoCSAction,
+        PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling);
   } else if (PGOUsePath) {
     assert(!PGOSampleUsePath);
-    PGOOpt = PGOOptions(PGOUsePath, "", "", "", FS, PGOOptions::IRUse,
-                        PGOOptions::NoCSAction,
-#if LLVM_VERSION_GE(19, 0)
-                        PGOOptions::ColdFuncOpt::Default,
-#endif
-                        DebugInfoForProfiling);
+    PGOOpt = PGOOptions(
+        PGOUsePath, "", "", "", FS, PGOOptions::IRUse, PGOOptions::NoCSAction,
+        PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling);
   } else if (PGOSampleUsePath) {
-    PGOOpt = PGOOptions(PGOSampleUsePath, "", "", "", FS, PGOOptions::SampleUse,
-                        PGOOptions::NoCSAction,
-#if LLVM_VERSION_GE(19, 0)
-                        PGOOptions::ColdFuncOpt::Default,
-#endif
-                        DebugInfoForProfiling);
+    PGOOpt =
+        PGOOptions(PGOSampleUsePath, "", "", "", FS, PGOOptions::SampleUse,
+                   PGOOptions::NoCSAction, PGOOptions::ColdFuncOpt::Default,
+                   DebugInfoForProfiling);
   } else if (DebugInfoForProfiling) {
-    PGOOpt = PGOOptions("", "", "", "", FS, PGOOptions::NoAction,
-                        PGOOptions::NoCSAction,
-#if LLVM_VERSION_GE(19, 0)
-                        PGOOptions::ColdFuncOpt::Default,
-#endif
-                        DebugInfoForProfiling);
+    PGOOpt = PGOOptions(
+        "", "", "", "", FS, PGOOptions::NoAction, PGOOptions::NoCSAction,
+        PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling);
   }
 
   auto PB = PassBuilder(TM, PTO, PGOOpt, &PIC);
@@ -855,10 +825,15 @@ extern "C" LLVMRustResult LLVMRustOptimize(
   }
 
   if (LintIR) {
-    PipelineStartEPCallbacks.push_back(
-        [](ModulePassManager &MPM, OptimizationLevel Level) {
-          MPM.addPass(createModuleToFunctionPassAdaptor(LintPass()));
-        });
+    PipelineStartEPCallbacks.push_back([](ModulePassManager &MPM,
+                                          OptimizationLevel Level) {
+#if LLVM_VERSION_GE(21, 0)
+      MPM.addPass(
+          createModuleToFunctionPassAdaptor(LintPass(/*AbortOnError=*/true)));
+#else
+      MPM.addPass(createModuleToFunctionPassAdaptor(LintPass()));
+#endif
+    });
   }
 
   if (InstrumentCoverage) {
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 53df59930f4..5f0e4d745e8 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -384,6 +384,12 @@ static inline void AddAttributes(T *t, unsigned Index, LLVMAttributeRef *Attrs,
   t->setAttributes(PALNew);
 }
 
+extern "C" bool LLVMRustHasAttributeAtIndex(LLVMValueRef Fn, unsigned Index,
+                                            LLVMRustAttributeKind RustAttr) {
+  Function *F = unwrap<Function>(Fn);
+  return F->hasParamAttribute(Index, fromRust(RustAttr));
+}
+
 extern "C" void LLVMRustAddFunctionAttributes(LLVMValueRef Fn, unsigned Index,
                                               LLVMAttributeRef *Attrs,
                                               size_t AttrsLen) {
@@ -467,12 +473,8 @@ extern "C" LLVMAttributeRef
 LLVMRustCreateRangeAttribute(LLVMContextRef C, unsigned NumBits,
                              const uint64_t LowerWords[],
                              const uint64_t UpperWords[]) {
-#if LLVM_VERSION_GE(19, 0)
   return LLVMCreateConstantRangeAttribute(C, Attribute::Range, NumBits,
                                           LowerWords, UpperWords);
-#else
-  report_fatal_error("LLVM 19.0 is required for Range Attribute");
-#endif
 }
 
 // These values **must** match ffi::AllocKindFlags.
@@ -636,6 +638,10 @@ static InlineAsm::AsmDialect fromRust(LLVMRustAsmDialect Dialect) {
   }
 }
 
+extern "C" uint64_t LLVMRustGetArrayNumElements(LLVMTypeRef Ty) {
+  return unwrap(Ty)->getArrayNumElements();
+}
+
 extern "C" LLVMValueRef
 LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, size_t AsmStringLen,
                   char *Constraints, size_t ConstraintsLen,
@@ -1591,43 +1597,6 @@ extern "C" LLVMValueRef LLVMRustBuildMemSet(LLVMBuilderRef B, LLVMValueRef Dst,
                                       MaybeAlign(DstAlign), IsVolatile));
 }
 
-// Polyfill for `LLVMBuildCallBr`, which was added in LLVM 19.
-// <https://github.com/llvm/llvm-project/commit/584253c4e2f788f870488fc32193b52d67ddaccc>
-// FIXME: Remove when Rust's minimum supported LLVM version reaches 19.
-#if LLVM_VERSION_LT(19, 0)
-DEFINE_SIMPLE_CONVERSION_FUNCTIONS(OperandBundleDef, LLVMOperandBundleRef)
-
-extern "C" LLVMValueRef
-LLVMBuildCallBr(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
-                LLVMBasicBlockRef DefaultDest, LLVMBasicBlockRef *IndirectDests,
-                unsigned NumIndirectDests, LLVMValueRef *Args, unsigned NumArgs,
-                LLVMOperandBundleRef *Bundles, unsigned NumBundles,
-                const char *Name) {
-  Value *Callee = unwrap(Fn);
-  FunctionType *FTy = unwrap<FunctionType>(Ty);
-
-  // FIXME: Is there a way around this?
-  std::vector<BasicBlock *> IndirectDestsUnwrapped;
-  IndirectDestsUnwrapped.reserve(NumIndirectDests);
-  for (unsigned i = 0; i < NumIndirectDests; ++i) {
-    IndirectDestsUnwrapped.push_back(unwrap(IndirectDests[i]));
-  }
-
-  // FIXME: Is there a way around this?
-  SmallVector<OperandBundleDef> OpBundles;
-  OpBundles.reserve(NumBundles);
-  for (unsigned i = 0; i < NumBundles; ++i) {
-    OpBundles.push_back(*unwrap(Bundles[i]));
-  }
-
-  return wrap(
-      unwrap(B)->CreateCallBr(FTy, Callee, unwrap(DefaultDest),
-                              ArrayRef<BasicBlock *>(IndirectDestsUnwrapped),
-                              ArrayRef<Value *>(unwrap(Args), NumArgs),
-                              ArrayRef<OperandBundleDef>(OpBundles), Name));
-}
-#endif
-
 extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B,
                                                LLVMBasicBlockRef BB) {
   auto Point = unwrap(BB)->getFirstInsertionPt();
@@ -1771,24 +1740,6 @@ extern "C" LLVMValueRef LLVMRustBuildMaxNum(LLVMBuilderRef B, LLVMValueRef LHS,
   return wrap(unwrap(B)->CreateMaxNum(unwrap(LHS), unwrap(RHS)));
 }
 
-#if LLVM_VERSION_LT(19, 0)
-enum {
-  LLVMGEPFlagInBounds = (1 << 0),
-  LLVMGEPFlagNUSW = (1 << 1),
-  LLVMGEPFlagNUW = (1 << 2),
-};
-extern "C" LLVMValueRef
-LLVMBuildGEPWithNoWrapFlags(LLVMBuilderRef B, LLVMTypeRef Ty,
-                            LLVMValueRef Pointer, LLVMValueRef *Indices,
-                            unsigned NumIndices, const char *Name,
-                            unsigned NoWrapFlags) {
-  if (NoWrapFlags & LLVMGEPFlagInBounds)
-    return LLVMBuildInBoundsGEP2(B, Ty, Pointer, Indices, NumIndices, Name);
-  else
-    return LLVMBuildGEP2(B, Ty, Pointer, Indices, NumIndices, Name);
-}
-#endif
-
 // Transfers ownership of DiagnosticHandler unique_ptr to the caller.
 extern "C" DiagnosticHandler *
 LLVMRustContextGetDiagnosticHandler(LLVMContextRef C) {
@@ -1856,11 +1807,7 @@ extern "C" void LLVMRustContextConfigureDiagnosticHandler(
         }
       }
       if (DiagnosticHandlerCallback) {
-#if LLVM_VERSION_GE(19, 0)
         DiagnosticHandlerCallback(&DI, DiagnosticHandlerContext);
-#else
-        DiagnosticHandlerCallback(DI, DiagnosticHandlerContext);
-#endif
         return true;
       }
       return false;
@@ -2008,21 +1955,3 @@ extern "C" void LLVMRustSetNoSanitizeHWAddress(LLVMValueRef Global) {
   MD.NoHWAddress = true;
   GV.setSanitizerMetadata(MD);
 }
-
-// Operations on composite constants.
-// These are clones of LLVM api functions that will become available in future
-// releases. They can be removed once Rust's minimum supported LLVM version
-// supports them. See https://github.com/rust-lang/rust/issues/121868 See
-// https://llvm.org/doxygen/group__LLVMCCoreValueConstantComposite.html
-
-// FIXME: Remove when Rust's minimum supported LLVM version reaches 19.
-// https://github.com/llvm/llvm-project/commit/e1405e4f71c899420ebf8262d5e9745598419df8
-#if LLVM_VERSION_LT(19, 0)
-extern "C" LLVMValueRef LLVMConstStringInContext2(LLVMContextRef C,
-                                                  const char *Str,
-                                                  size_t Length,
-                                                  bool DontNullTerminate) {
-  return wrap(ConstantDataArray::getString(*unwrap(C), StringRef(Str, Length),
-                                           !DontNullTerminate));
-}
-#endif
diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl
index 9adbcabcf45..d997ba198ac 100644
--- a/compiler/rustc_metadata/messages.ftl
+++ b/compiler/rustc_metadata/messages.ftl
@@ -97,6 +97,10 @@ metadata_found_staticlib =
     found staticlib `{$crate_name}` instead of rlib or dylib{$add_info}
     .help = please recompile that crate using --crate-type lib
 
+metadata_full_metadata_not_found =
+    only metadata stub found for `{$flavor}` dependency `{$crate_name}`
+    please provide path to the corresponding .rmeta file with full metadata
+
 metadata_global_alloc_required =
     no global memory allocator found but one is required; link to std or add `#[global_allocator]` to a static item that implements the GlobalAlloc trait
 
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs
index b7f13e0afdc..1c3222bbfeb 100644
--- a/compiler/rustc_metadata/src/creader.rs
+++ b/compiler/rustc_metadata/src/creader.rs
@@ -340,7 +340,7 @@ impl CStore {
         }
         let level = tcx
             .lint_level_at_node(lint::builtin::UNUSED_CRATE_DEPENDENCIES, rustc_hir::CRATE_HIR_ID)
-            .0;
+            .level;
         if level != lint::Level::Allow {
             let unused_externs =
                 self.unused_externs.iter().map(|ident| ident.to_ident_string()).collect::<Vec<_>>();
@@ -1032,14 +1032,19 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
     }
 
     fn inject_allocator_crate(&mut self, krate: &ast::Crate) {
-        self.cstore.has_global_allocator = match &*global_allocator_spans(krate) {
-            [span1, span2, ..] => {
-                self.dcx().emit_err(errors::NoMultipleGlobalAlloc { span2: *span2, span1: *span1 });
-                true
-            }
-            spans => !spans.is_empty(),
-        };
-        self.cstore.has_alloc_error_handler = match &*alloc_error_handler_spans(krate) {
+        self.cstore.has_global_allocator =
+            match &*fn_spans(krate, Symbol::intern(&global_fn_name(sym::alloc))) {
+                [span1, span2, ..] => {
+                    self.dcx()
+                        .emit_err(errors::NoMultipleGlobalAlloc { span2: *span2, span1: *span1 });
+                    true
+                }
+                spans => !spans.is_empty(),
+            };
+        self.cstore.has_alloc_error_handler = match &*fn_spans(
+            krate,
+            Symbol::intern(alloc_error_handler_name(AllocatorKind::Global)),
+        ) {
             [span1, span2, ..] => {
                 self.dcx()
                     .emit_err(errors::NoMultipleAllocErrorHandler { span2: *span2, span1: *span1 });
@@ -1310,17 +1315,14 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
         definitions: &Definitions,
     ) -> Option<CrateNum> {
         match item.kind {
-            ast::ItemKind::ExternCrate(orig_name) => {
-                debug!(
-                    "resolving extern crate stmt. ident: {} orig_name: {:?}",
-                    item.ident, orig_name
-                );
+            ast::ItemKind::ExternCrate(orig_name, ident) => {
+                debug!("resolving extern crate stmt. ident: {} orig_name: {:?}", ident, orig_name);
                 let name = match orig_name {
                     Some(orig_name) => {
                         validate_crate_name(self.sess, orig_name, Some(item.span));
                         orig_name
                     }
-                    None => item.ident.name,
+                    None => ident.name,
                 };
                 let dep_kind = if attr::contains_name(&item.attrs, sym::no_link) {
                     CrateDepKind::MacrosOnly
@@ -1368,36 +1370,15 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
     }
 }
 
-fn global_allocator_spans(krate: &ast::Crate) -> Vec<Span> {
-    struct Finder {
-        name: Symbol,
-        spans: Vec<Span>,
-    }
-    impl<'ast> visit::Visitor<'ast> for Finder {
-        fn visit_item(&mut self, item: &'ast ast::Item) {
-            if item.ident.name == self.name
-                && attr::contains_name(&item.attrs, sym::rustc_std_internal_symbol)
-            {
-                self.spans.push(item.span);
-            }
-            visit::walk_item(self, item)
-        }
-    }
-
-    let name = Symbol::intern(&global_fn_name(sym::alloc));
-    let mut f = Finder { name, spans: Vec::new() };
-    visit::walk_crate(&mut f, krate);
-    f.spans
-}
-
-fn alloc_error_handler_spans(krate: &ast::Crate) -> Vec<Span> {
+fn fn_spans(krate: &ast::Crate, name: Symbol) -> Vec<Span> {
     struct Finder {
         name: Symbol,
         spans: Vec<Span>,
     }
     impl<'ast> visit::Visitor<'ast> for Finder {
         fn visit_item(&mut self, item: &'ast ast::Item) {
-            if item.ident.name == self.name
+            if let Some(ident) = item.kind.ident()
+                && ident.name == self.name
                 && attr::contains_name(&item.attrs, sym::rustc_std_internal_symbol)
             {
                 self.spans.push(item.span);
@@ -1406,7 +1387,6 @@ fn alloc_error_handler_spans(krate: &ast::Crate) -> Vec<Span> {
         }
     }
 
-    let name = Symbol::intern(alloc_error_handler_name(AllocatorKind::Global));
     let mut f = Finder { name, spans: Vec::new() };
     visit::walk_crate(&mut f, krate);
     f.spans
diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs
index 0c54628598c..c45daeda85d 100644
--- a/compiler/rustc_metadata/src/errors.rs
+++ b/compiler/rustc_metadata/src/errors.rs
@@ -526,6 +526,15 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for MultipleCandidates {
 }
 
 #[derive(Diagnostic)]
+#[diag(metadata_full_metadata_not_found)]
+pub(crate) struct FullMetadataNotFound {
+    #[primary_span]
+    pub span: Span,
+    pub flavor: CrateFlavor,
+    pub crate_name: Symbol,
+}
+
+#[derive(Diagnostic)]
 #[diag(metadata_symbol_conflicts_current, code = E0519)]
 pub struct SymbolConflictsCurrent {
     #[primary_span]
diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs
index 4450d050c8e..c4e1e0f1d1a 100644
--- a/compiler/rustc_metadata/src/fs.rs
+++ b/compiler/rustc_metadata/src/fs.rs
@@ -3,7 +3,7 @@ use std::{fs, io};
 
 use rustc_data_structures::temp_dir::MaybeTempDir;
 use rustc_middle::ty::TyCtxt;
-use rustc_session::config::{OutFileName, OutputType};
+use rustc_session::config::{CrateType, OutFileName, OutputType};
 use rustc_session::output::filename_for_metadata;
 use rustc_session::{MetadataKind, Session};
 use tempfile::Builder as TempFileBuilder;
@@ -50,7 +50,14 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
         .tempdir_in(out_filename.parent().unwrap_or_else(|| Path::new("")))
         .unwrap_or_else(|err| tcx.dcx().emit_fatal(FailedCreateTempdir { err }));
     let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps);
-    let metadata_filename = metadata_tmpdir.as_ref().join(METADATA_FILENAME);
+    let metadata_filename = metadata_tmpdir.as_ref().join("full.rmeta");
+    let metadata_stub_filename = if !tcx.sess.opts.unstable_opts.embed_metadata
+        && !tcx.crate_types().contains(&CrateType::ProcMacro)
+    {
+        Some(metadata_tmpdir.as_ref().join("stub.rmeta"))
+    } else {
+        None
+    };
 
     // Always create a file at `metadata_filename`, even if we have nothing to write to it.
     // This simplifies the creation of the output `out_filename` when requested.
@@ -60,9 +67,15 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
             std::fs::File::create(&metadata_filename).unwrap_or_else(|err| {
                 tcx.dcx().emit_fatal(FailedCreateFile { filename: &metadata_filename, err });
             });
+            if let Some(metadata_stub_filename) = &metadata_stub_filename {
+                std::fs::File::create(metadata_stub_filename).unwrap_or_else(|err| {
+                    tcx.dcx()
+                        .emit_fatal(FailedCreateFile { filename: &metadata_stub_filename, err });
+                });
+            }
         }
         MetadataKind::Uncompressed | MetadataKind::Compressed => {
-            encode_metadata(tcx, &metadata_filename);
+            encode_metadata(tcx, &metadata_filename, metadata_stub_filename.as_deref())
         }
     };
 
@@ -100,9 +113,10 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
 
     // Load metadata back to memory: codegen may need to include it in object files.
     let metadata =
-        EncodedMetadata::from_path(metadata_filename, metadata_tmpdir).unwrap_or_else(|err| {
-            tcx.dcx().emit_fatal(FailedCreateEncodedMetadata { err });
-        });
+        EncodedMetadata::from_path(metadata_filename, metadata_stub_filename, metadata_tmpdir)
+            .unwrap_or_else(|err| {
+                tcx.dcx().emit_fatal(FailedCreateEncodedMetadata { err });
+            });
 
     let need_metadata_module = metadata_kind == MetadataKind::Compressed;
 
diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs
index d5dd5059aac..112954eca0d 100644
--- a/compiler/rustc_metadata/src/locator.rs
+++ b/compiler/rustc_metadata/src/locator.rs
@@ -654,7 +654,24 @@ impl<'a> CrateLocator<'a> {
                     continue;
                 }
             }
-            *slot = Some((hash, metadata, lib.clone()));
+
+            // We error eagerly here. If we're locating a rlib, then in theory the full metadata
+            // could still be in a (later resolved) dylib. In practice, if the rlib and dylib
+            // were produced in a way where one has full metadata and the other hasn't, it would
+            // mean that they were compiled using different compiler flags and probably also have
+            // a different SVH value.
+            if metadata.get_header().is_stub {
+                // `is_stub` should never be true for .rmeta files.
+                assert_ne!(flavor, CrateFlavor::Rmeta);
+
+                // Because rmeta files are resolved before rlib/dylib files, if this is a stub and
+                // we haven't found a slot already, it means that the full metadata is missing.
+                if slot.is_none() {
+                    return Err(CrateError::FullMetadataNotFound(self.crate_name, flavor));
+                }
+            } else {
+                *slot = Some((hash, metadata, lib.clone()));
+            }
             ret = Some((lib, kind));
         }
 
@@ -728,37 +745,25 @@ impl<'a> CrateLocator<'a> {
             let Some(file) = loc_orig.file_name().and_then(|s| s.to_str()) else {
                 return Err(CrateError::ExternLocationNotFile(self.crate_name, loc_orig.clone()));
             };
-            // FnMut cannot return reference to captured value, so references
-            // must be taken outside the closure.
-            let rlibs = &mut rlibs;
-            let rmetas = &mut rmetas;
-            let dylibs = &mut dylibs;
-            let type_via_filename = (|| {
-                if file.starts_with("lib") {
-                    if file.ends_with(".rlib") {
-                        return Some(rlibs);
-                    }
-                    if file.ends_with(".rmeta") {
-                        return Some(rmetas);
-                    }
-                }
-                let dll_prefix = self.target.dll_prefix.as_ref();
-                let dll_suffix = self.target.dll_suffix.as_ref();
-                if file.starts_with(dll_prefix) && file.ends_with(dll_suffix) {
-                    return Some(dylibs);
-                }
-                None
-            })();
-            match type_via_filename {
-                Some(type_via_filename) => {
-                    type_via_filename.insert(loc_canon.clone(), PathKind::ExternFlag);
+            if file.starts_with("lib") {
+                if file.ends_with(".rlib") {
+                    rlibs.insert(loc_canon.clone(), PathKind::ExternFlag);
+                    continue;
                 }
-                None => {
-                    self.crate_rejections
-                        .via_filename
-                        .push(CrateMismatch { path: loc_orig.clone(), got: String::new() });
+                if file.ends_with(".rmeta") {
+                    rmetas.insert(loc_canon.clone(), PathKind::ExternFlag);
+                    continue;
                 }
             }
+            let dll_prefix = self.target.dll_prefix.as_ref();
+            let dll_suffix = self.target.dll_suffix.as_ref();
+            if file.starts_with(dll_prefix) && file.ends_with(dll_suffix) {
+                dylibs.insert(loc_canon.clone(), PathKind::ExternFlag);
+                continue;
+            }
+            self.crate_rejections
+                .via_filename
+                .push(CrateMismatch { path: loc_orig.clone(), got: String::new() });
         }
 
         // Extract the dylib/rlib/rmeta triple.
@@ -928,6 +933,7 @@ pub(crate) enum CrateError {
     ExternLocationNotExist(Symbol, PathBuf),
     ExternLocationNotFile(Symbol, PathBuf),
     MultipleCandidates(Symbol, CrateFlavor, Vec<PathBuf>),
+    FullMetadataNotFound(Symbol, CrateFlavor),
     SymbolConflictsCurrent(Symbol),
     StableCrateIdCollision(Symbol, Symbol),
     DlOpen(String, String),
@@ -978,6 +984,9 @@ impl CrateError {
             CrateError::MultipleCandidates(crate_name, flavor, candidates) => {
                 dcx.emit_err(errors::MultipleCandidates { span, crate_name, flavor, candidates });
             }
+            CrateError::FullMetadataNotFound(crate_name, flavor) => {
+                dcx.emit_err(errors::FullMetadataNotFound { span, crate_name, flavor });
+            }
             CrateError::SymbolConflictsCurrent(root_name) => {
                 dcx.emit_err(errors::SymbolConflictsCurrent { span, crate_name: root_name });
             }
diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs
index f63ae8079dc..130a425e9c7 100644
--- a/compiler/rustc_metadata/src/native_libs.rs
+++ b/compiler/rustc_metadata/src/native_libs.rs
@@ -73,7 +73,7 @@ pub fn walk_native_lib_search_dirs<R>(
         || sess.target.os == "linux"
         || sess.target.os == "fuchsia"
         || sess.target.is_like_aix
-        || sess.target.is_like_osx && !sess.opts.unstable_opts.sanitizer.is_empty()
+        || sess.target.is_like_darwin && !sess.opts.unstable_opts.sanitizer.is_empty()
     {
         f(&sess.target_tlib_path.dir, false)?;
     }
@@ -257,7 +257,7 @@ impl<'tcx> Collector<'tcx> {
                             "static" => NativeLibKind::Static { bundle: None, whole_archive: None },
                             "dylib" => NativeLibKind::Dylib { as_needed: None },
                             "framework" => {
-                                if !sess.target.is_like_osx {
+                                if !sess.target.is_like_darwin {
                                     sess.dcx().emit_err(errors::LinkFrameworkApple { span });
                                 }
                                 NativeLibKind::Framework { as_needed: None }
@@ -531,7 +531,7 @@ impl<'tcx> Collector<'tcx> {
         let mut renames = FxHashSet::default();
         for lib in &self.tcx.sess.opts.libs {
             if let NativeLibKind::Framework { .. } = lib.kind
-                && !self.tcx.sess.target.is_like_osx
+                && !self.tcx.sess.target.is_like_darwin
             {
                 // Cannot check this when parsing options because the target is not yet available.
                 self.tcx.dcx().emit_err(errors::LibFrameworkApple);
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index 776b081a463..3dc82ce9d18 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -330,14 +330,8 @@ provide! { tcx, def_id, other, cdata,
 
     visibility => { cdata.get_visibility(def_id.index) }
     adt_def => { cdata.get_adt_def(def_id.index, tcx) }
-    adt_destructor => {
-        let _ = cdata;
-        tcx.calculate_dtor(def_id, |_,_| Ok(()))
-    }
-    adt_async_destructor => {
-        let _ = cdata;
-        tcx.calculate_async_dtor(def_id, |_,_| Ok(()))
-    }
+    adt_destructor => { table }
+    adt_async_destructor => { table }
     associated_item_def_ids => {
         tcx.arena.alloc_from_iter(cdata.get_associated_item_or_field_def_ids(def_id.index))
     }
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 7ab3d432bdf..167122a9793 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -701,6 +701,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                     triple: tcx.sess.opts.target_triple.clone(),
                     hash: tcx.crate_hash(LOCAL_CRATE),
                     is_proc_macro_crate: proc_macro_data.is_some(),
+                    is_stub: false,
                 },
                 extra_filename: tcx.sess.opts.cg.extra_filename.clone(),
                 stable_crate_id: tcx.def_path_hash(LOCAL_CRATE.as_def_id()).stable_crate_id(),
@@ -1633,6 +1634,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 record!(self.tables.fn_sig[variant.def_id] <- fn_sig);
             }
         }
+
+        if let Some(destructor) = tcx.adt_destructor(local_def_id) {
+            record!(self.tables.adt_destructor[def_id] <- destructor);
+        }
+
+        if let Some(destructor) = tcx.adt_async_destructor(local_def_id) {
+            record!(self.tables.adt_async_destructor[def_id] <- destructor);
+        }
     }
 
     #[instrument(level = "debug", skip(self))]
@@ -1881,8 +1890,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         let is_proc_macro = self.tcx.crate_types().contains(&CrateType::ProcMacro);
         if is_proc_macro {
             let tcx = self.tcx;
-            let hir = tcx.hir();
-
             let proc_macro_decls_static = tcx.proc_macro_decls_static(()).unwrap().local_def_index;
             let stability = tcx.lookup_stability(CRATE_DEF_ID);
             let macros =
@@ -1915,7 +1922,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 let id = proc_macro;
                 let proc_macro = tcx.local_def_id_to_hir_id(proc_macro);
                 let mut name = tcx.hir_name(proc_macro);
-                let span = hir.span(proc_macro);
+                let span = tcx.hir_span(proc_macro);
                 // Proc-macros may have attributes like `#[allow_internal_unstable]`,
                 // so downstream crates need access to them.
                 let attrs = tcx.hir_attrs(proc_macro);
@@ -2231,8 +2238,12 @@ fn prefetch_mir(tcx: TyCtxt<'_>) {
 // generated regardless of trailing bytes that end up in it.
 
 pub struct EncodedMetadata {
-    // The declaration order matters because `mmap` should be dropped before `_temp_dir`.
-    mmap: Option<Mmap>,
+    // The declaration order matters because `full_metadata` should be dropped
+    // before `_temp_dir`.
+    full_metadata: Option<Mmap>,
+    // This is an optional stub metadata containing only the crate header.
+    // The header should be very small, so we load it directly into memory.
+    stub_metadata: Option<Vec<u8>>,
     // We need to carry MaybeTempDir to avoid deleting the temporary
     // directory while accessing the Mmap.
     _temp_dir: Option<MaybeTempDir>,
@@ -2240,33 +2251,50 @@ pub struct EncodedMetadata {
 
 impl EncodedMetadata {
     #[inline]
-    pub fn from_path(path: PathBuf, temp_dir: Option<MaybeTempDir>) -> std::io::Result<Self> {
+    pub fn from_path(
+        path: PathBuf,
+        stub_path: Option<PathBuf>,
+        temp_dir: Option<MaybeTempDir>,
+    ) -> std::io::Result<Self> {
         let file = std::fs::File::open(&path)?;
         let file_metadata = file.metadata()?;
         if file_metadata.len() == 0 {
-            return Ok(Self { mmap: None, _temp_dir: None });
+            return Ok(Self { full_metadata: None, stub_metadata: None, _temp_dir: None });
         }
-        let mmap = unsafe { Some(Mmap::map(file)?) };
-        Ok(Self { mmap, _temp_dir: temp_dir })
+        let full_mmap = unsafe { Some(Mmap::map(file)?) };
+
+        let stub =
+            if let Some(stub_path) = stub_path { Some(std::fs::read(stub_path)?) } else { None };
+
+        Ok(Self { full_metadata: full_mmap, stub_metadata: stub, _temp_dir: temp_dir })
     }
 
     #[inline]
-    pub fn raw_data(&self) -> &[u8] {
-        self.mmap.as_deref().unwrap_or_default()
+    pub fn full(&self) -> &[u8] {
+        &self.full_metadata.as_deref().unwrap_or_default()
+    }
+
+    #[inline]
+    pub fn stub_or_full(&self) -> &[u8] {
+        self.stub_metadata.as_deref().unwrap_or(self.full())
     }
 }
 
 impl<S: Encoder> Encodable<S> for EncodedMetadata {
     fn encode(&self, s: &mut S) {
-        let slice = self.raw_data();
+        self.stub_metadata.encode(s);
+
+        let slice = self.full();
         slice.encode(s)
     }
 }
 
 impl<D: Decoder> Decodable<D> for EncodedMetadata {
     fn decode(d: &mut D) -> Self {
+        let stub = <Option<Vec<u8>>>::decode(d);
+
         let len = d.read_usize();
-        let mmap = if len > 0 {
+        let full_metadata = if len > 0 {
             let mut mmap = MmapMut::map_anon(len).unwrap();
             mmap.copy_from_slice(d.read_raw_bytes(len));
             Some(mmap.make_read_only().unwrap())
@@ -2274,11 +2302,11 @@ impl<D: Decoder> Decodable<D> for EncodedMetadata {
             None
         };
 
-        Self { mmap, _temp_dir: None }
+        Self { full_metadata, stub_metadata: stub, _temp_dir: None }
     }
 }
 
-pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) {
+pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) {
     let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata");
 
     // Since encoding metadata is not in a query, and nothing is cached,
@@ -2292,6 +2320,42 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) {
         join(|| prefetch_mir(tcx), || tcx.exported_symbols(LOCAL_CRATE));
     }
 
+    with_encode_metadata_header(tcx, path, |ecx| {
+        // Encode all the entries and extra information in the crate,
+        // culminating in the `CrateRoot` which points to all of it.
+        let root = ecx.encode_crate_root();
+
+        // Flush buffer to ensure backing file has the correct size.
+        ecx.opaque.flush();
+        // Record metadata size for self-profiling
+        tcx.prof.artifact_size(
+            "crate_metadata",
+            "crate_metadata",
+            ecx.opaque.file().metadata().unwrap().len(),
+        );
+
+        root.position.get()
+    });
+
+    if let Some(ref_path) = ref_path {
+        with_encode_metadata_header(tcx, ref_path, |ecx| {
+            let header: LazyValue<CrateHeader> = ecx.lazy(CrateHeader {
+                name: tcx.crate_name(LOCAL_CRATE),
+                triple: tcx.sess.opts.target_triple.clone(),
+                hash: tcx.crate_hash(LOCAL_CRATE),
+                is_proc_macro_crate: false,
+                is_stub: true,
+            });
+            header.position.get()
+        });
+    }
+}
+
+fn with_encode_metadata_header(
+    tcx: TyCtxt<'_>,
+    path: &Path,
+    f: impl FnOnce(&mut EncodeContext<'_, '_>) -> usize,
+) {
     let mut encoder = opaque::FileEncoder::new(path)
         .unwrap_or_else(|err| tcx.dcx().emit_fatal(FailCreateFileEncoder { err }));
     encoder.emit_raw_bytes(METADATA_HEADER);
@@ -2326,9 +2390,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) {
     // Encode the rustc version string in a predictable location.
     rustc_version(tcx.sess.cfg_version).encode(&mut ecx);
 
-    // Encode all the entries and extra information in the crate,
-    // culminating in the `CrateRoot` which points to all of it.
-    let root = ecx.encode_crate_root();
+    let root_position = f(&mut ecx);
 
     // Make sure we report any errors from writing to the file.
     // If we forget this, compilation can succeed with an incomplete rmeta file,
@@ -2338,12 +2400,9 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) {
     }
 
     let file = ecx.opaque.file();
-    if let Err(err) = encode_root_position(file, root.position.get()) {
+    if let Err(err) = encode_root_position(file, root_position) {
         tcx.dcx().emit_fatal(FailWriteFile { path: ecx.opaque.path(), err });
     }
-
-    // Record metadata size for self-profiling
-    tcx.prof.artifact_size("crate_metadata", "crate_metadata", file.metadata().unwrap().len());
 }
 
 fn encode_root_position(mut file: &File, pos: usize) -> Result<(), std::io::Error> {
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index dc453b1e747..96a1f65eeb0 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -36,7 +36,9 @@ use rustc_serialize::opaque::FileEncoder;
 use rustc_session::config::{SymbolManglingVersion, TargetModifier};
 use rustc_session::cstore::{CrateDepKind, ForeignModule, LinkagePreference, NativeLib};
 use rustc_span::edition::Edition;
-use rustc_span::hygiene::{ExpnIndex, MacroKind, SyntaxContextData};
+use rustc_span::hygiene::{
+    ExpnIndex, MacroKind, SyntaxContextDataNonRecursive as SyntaxContextData,
+};
 use rustc_span::{self, ExpnData, ExpnHash, ExpnId, Ident, Span, Symbol};
 use rustc_target::spec::{PanicStrategy, TargetTuple};
 use table::TableBuilder;
@@ -56,7 +58,7 @@ pub(crate) fn rustc_version(cfg_version: &'static str) -> String {
 /// Metadata encoding version.
 /// N.B., increment this if you change the format of metadata such that
 /// the rustc version can't be found to compare with `rustc_version()`.
-const METADATA_VERSION: u8 = 9;
+const METADATA_VERSION: u8 = 10;
 
 /// Metadata header which includes `METADATA_VERSION`.
 ///
@@ -221,6 +223,12 @@ pub(crate) struct CrateHeader {
     /// This is separate from [`ProcMacroData`] to avoid having to update [`METADATA_VERSION`] every
     /// time ProcMacroData changes.
     pub(crate) is_proc_macro_crate: bool,
+    /// Whether this crate metadata section is just a stub.
+    /// Stubs do not contain the full metadata (it will be typically stored
+    /// in a separate rmeta file).
+    ///
+    /// This is used inside rlibs and dylibs when using `-Zembed-metadata=no`.
+    pub(crate) is_stub: bool,
 }
 
 /// Serialized `.rmeta` data for a crate.
@@ -446,6 +454,8 @@ define_tables! {
     fn_arg_names: Table<DefIndex, LazyArray<Option<Ident>>>,
     coroutine_kind: Table<DefIndex, hir::CoroutineKind>,
     coroutine_for_closure: Table<DefIndex, RawDefId>,
+    adt_destructor: Table<DefIndex, LazyValue<ty::Destructor>>,
+    adt_async_destructor: Table<DefIndex, LazyValue<ty::AsyncDestructor>>,
     coroutine_by_move_body_def_id: Table<DefIndex, RawDefId>,
     eval_static_initializer: Table<DefIndex, LazyValue<mir::interpret::ConstAllocation<'static>>>,
     trait_def: Table<DefIndex, LazyValue<ty::TraitDef>>,
diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs
index be34c7ef4bd..644cdac5d55 100644
--- a/compiler/rustc_middle/src/dep_graph/dep_node.rs
+++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs
@@ -89,6 +89,7 @@ rustc_query_append!(define_dep_nodes![
     /// We use this to create a forever-red node.
     [] fn Red() -> (),
     [] fn SideEffect() -> (),
+    [] fn AnonZeroDeps() -> (),
     [] fn TraitSelect() -> (),
     [] fn CompileCodegenUnit() -> (),
     [] fn CompileMonoItem() -> (),
diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs
index 739c0be1a91..931d67087ac 100644
--- a/compiler/rustc_middle/src/dep_graph/mod.rs
+++ b/compiler/rustc_middle/src/dep_graph/mod.rs
@@ -53,6 +53,7 @@ impl Deps for DepsType {
     const DEP_KIND_NULL: DepKind = dep_kinds::Null;
     const DEP_KIND_RED: DepKind = dep_kinds::Red;
     const DEP_KIND_SIDE_EFFECT: DepKind = dep_kinds::SideEffect;
+    const DEP_KIND_ANON_ZERO_DEPS: DepKind = dep_kinds::AnonZeroDeps;
     const DEP_KIND_MAX: u16 = dep_node::DEP_KIND_VARIANTS - 1;
 }
 
diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs
index 52f155a16b8..21ab06c98a7 100644
--- a/compiler/rustc_middle/src/hir/map.rs
+++ b/compiler/rustc_middle/src/hir/map.rs
@@ -22,6 +22,7 @@ use crate::ty::TyCtxt;
 // only serves as "namespace" for HIR-related methods, and can be
 // removed if all the methods are reasonably renamed and moved to tcx
 // (https://github.com/rust-lang/rust/pull/118256#issuecomment-1826442834).
+#[allow(unused)] // FIXME: temporary
 #[derive(Copy, Clone)]
 pub struct Map<'hir> {
     pub(super) tcx: TyCtxt<'hir>,
@@ -273,7 +274,7 @@ impl<'tcx> TyCtxt<'tcx> {
         self.hir_maybe_body_owned_by(id).unwrap_or_else(|| {
             let hir_id = self.local_def_id_to_hir_id(id);
             span_bug!(
-                self.hir().span(hir_id),
+                self.hir_span(hir_id),
                 "body_owned_by: {} has no associated body",
                 self.hir_id_to_string(hir_id)
             );
@@ -367,10 +368,6 @@ impl<'tcx> TyCtxt<'tcx> {
         }
     }
 
-    pub fn hir_trait_impls(self, trait_did: DefId) -> &'tcx [LocalDefId] {
-        self.all_local_trait_impls(()).get(&trait_did).map_or(&[], |xs| &xs[..])
-    }
-
     /// Gets the attributes on the crate. This is preferable to
     /// invoking `krate.attrs` because it registers a tighter
     /// dep-graph access.
@@ -681,9 +678,8 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn hir_id_to_string(self, id: HirId) -> String {
         let path_str = |def_id: LocalDefId| self.def_path_str(def_id);
 
-        let span_str = || {
-            self.sess.source_map().span_to_snippet(Map { tcx: self }.span(id)).unwrap_or_default()
-        };
+        let span_str =
+            || self.sess.source_map().span_to_snippet(self.hir_span(id)).unwrap_or_default();
         let node_str = |prefix| format!("{id} ({prefix} `{}`)", span_str());
 
         match self.hir_node(id) {
@@ -879,12 +875,10 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn hir_attrs(self, id: HirId) -> &'tcx [Attribute] {
         self.hir_attr_map(id.owner).get(id.local_id)
     }
-}
 
-impl<'hir> Map<'hir> {
     /// Gets the span of the definition of the specified HIR node.
     /// This is used by `tcx.def_span`.
-    pub fn span(self, hir_id: HirId) -> Span {
+    pub fn hir_span(self, hir_id: HirId) -> Span {
         fn until_within(outer: Span, end: Span) -> Span {
             if let Some(end) = end.find_ancestor_inside(outer) {
                 outer.with_hi(end.hi())
@@ -904,7 +898,7 @@ impl<'hir> Map<'hir> {
             span
         }
 
-        let span = match self.tcx.hir_node(hir_id) {
+        let span = match self.hir_node(hir_id) {
             // Function-like.
             Node::Item(Item { kind: ItemKind::Fn { sig, .. }, span: outer_span, .. })
             | Node::TraitItem(TraitItem {
@@ -984,7 +978,7 @@ impl<'hir> Map<'hir> {
             Node::Variant(variant) => named_span(variant.span, variant.ident, None),
             Node::ImplItem(item) => named_span(item.span, item.ident, Some(item.generics)),
             Node::ForeignItem(item) => named_span(item.span, item.ident, None),
-            Node::Ctor(_) => return self.span(self.tcx.parent_hir_id(hir_id)),
+            Node::Ctor(_) => return self.hir_span(self.parent_hir_id(hir_id)),
             Node::Expr(Expr {
                 kind: ExprKind::Closure(Closure { fn_decl_span, .. }),
                 span,
@@ -993,16 +987,16 @@ impl<'hir> Map<'hir> {
                 // Ensure that the returned span has the item's SyntaxContext.
                 fn_decl_span.find_ancestor_inside(*span).unwrap_or(*span)
             }
-            _ => self.span_with_body(hir_id),
+            _ => self.hir_span_with_body(hir_id),
         };
-        debug_assert_eq!(span.ctxt(), self.span_with_body(hir_id).ctxt());
+        debug_assert_eq!(span.ctxt(), self.hir_span_with_body(hir_id).ctxt());
         span
     }
 
-    /// Like `hir.span()`, but includes the body of items
+    /// Like `hir_span()`, but includes the body of items
     /// (instead of just the item header)
-    pub fn span_with_body(self, hir_id: HirId) -> Span {
-        match self.tcx.hir_node(hir_id) {
+    pub fn hir_span_with_body(self, hir_id: HirId) -> Span {
+        match self.hir_node(hir_id) {
             Node::Param(param) => param.span,
             Node::Item(item) => item.span,
             Node::ForeignItem(foreign_item) => foreign_item.span,
@@ -1011,7 +1005,7 @@ impl<'hir> Map<'hir> {
             Node::Variant(variant) => variant.span,
             Node::Field(field) => field.span,
             Node::AnonConst(constant) => constant.span,
-            Node::ConstBlock(constant) => self.tcx.hir_body(constant.body).value.span,
+            Node::ConstBlock(constant) => self.hir_body(constant.body).value.span,
             Node::ConstArg(const_arg) => const_arg.span(),
             Node::Expr(expr) => expr.span,
             Node::ExprField(field) => field.span,
@@ -1031,7 +1025,7 @@ impl<'hir> Map<'hir> {
             Node::PatExpr(lit) => lit.span,
             Node::Arm(arm) => arm.span,
             Node::Block(block) => block.span,
-            Node::Ctor(..) => self.span_with_body(self.tcx.parent_hir_id(hir_id)),
+            Node::Ctor(..) => self.hir_span_with_body(self.parent_hir_id(hir_id)),
             Node::Lifetime(lifetime) => lifetime.ident.span,
             Node::GenericParam(param) => param.span,
             Node::Infer(i) => i.span,
@@ -1044,23 +1038,23 @@ impl<'hir> Map<'hir> {
         }
     }
 
-    pub fn span_if_local(self, id: DefId) -> Option<Span> {
-        id.is_local().then(|| self.tcx.def_span(id))
+    pub fn hir_span_if_local(self, id: DefId) -> Option<Span> {
+        id.is_local().then(|| self.def_span(id))
     }
 
-    pub fn res_span(self, res: Res) -> Option<Span> {
+    pub fn hir_res_span(self, res: Res) -> Option<Span> {
         match res {
             Res::Err => None,
-            Res::Local(id) => Some(self.span(id)),
-            res => self.span_if_local(res.opt_def_id()?),
+            Res::Local(id) => Some(self.hir_span(id)),
+            res => self.hir_span_if_local(res.opt_def_id()?),
         }
     }
 
     /// Returns the HirId of `N` in `struct Foo<const N: usize = { ... }>` when
     /// called with the HirId for the `{ ... }` anon const
-    pub fn opt_const_param_default_param_def_id(self, anon_const: HirId) -> Option<LocalDefId> {
-        let const_arg = self.tcx.parent_hir_id(anon_const);
-        match self.tcx.parent_hir_node(const_arg) {
+    pub fn hir_opt_const_param_default_param_def_id(self, anon_const: HirId) -> Option<LocalDefId> {
+        let const_arg = self.parent_hir_id(anon_const);
+        match self.parent_hir_node(const_arg) {
             Node::GenericParam(GenericParam {
                 def_id: param_id,
                 kind: GenericParamKind::Const { .. },
@@ -1070,7 +1064,7 @@ impl<'hir> Map<'hir> {
         }
     }
 
-    pub fn maybe_get_struct_pattern_shorthand_field(&self, expr: &Expr<'_>) -> Option<Symbol> {
+    pub fn hir_maybe_get_struct_pattern_shorthand_field(self, expr: &Expr<'_>) -> Option<Symbol> {
         let local = match expr {
             Expr {
                 kind:
@@ -1085,7 +1079,7 @@ impl<'hir> Map<'hir> {
             _ => None,
         }?;
 
-        match self.tcx.parent_hir_node(expr.hir_id) {
+        match self.parent_hir_node(expr.hir_id) {
             Node::ExprField(field) => {
                 if field.ident.name == local.name && field.is_shorthand {
                     return Some(local.name);
diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs
index 347bc5ea312..74369b6636c 100644
--- a/compiler/rustc_middle/src/hir/mod.rs
+++ b/compiler/rustc_middle/src/hir/mod.rs
@@ -210,13 +210,12 @@ pub fn provide(providers: &mut Providers) {
     providers.hir_attr_map = |tcx, id| {
         tcx.hir_crate(()).owners[id.def_id].as_owner().map_or(AttributeMap::EMPTY, |o| &o.attrs)
     };
-    providers.def_span = |tcx, def_id| tcx.hir().span(tcx.local_def_id_to_hir_id(def_id));
+    providers.def_span = |tcx, def_id| tcx.hir_span(tcx.local_def_id_to_hir_id(def_id));
     providers.def_ident_span = |tcx, def_id| {
         let hir_id = tcx.local_def_id_to_hir_id(def_id);
         tcx.hir_opt_ident_span(hir_id)
     };
     providers.fn_arg_names = |tcx, def_id| {
-        let hir = tcx.hir();
         if let Some(body_id) = tcx.hir_node_by_def_id(def_id).body_id() {
             tcx.arena.alloc_from_iter(tcx.hir_body_param_names(body_id))
         } else if let Node::TraitItem(&TraitItem {
@@ -231,13 +230,15 @@ pub fn provide(providers: &mut Providers) {
             idents
         } else {
             span_bug!(
-                hir.span(tcx.local_def_id_to_hir_id(def_id)),
+                tcx.hir_span(tcx.local_def_id_to_hir_id(def_id)),
                 "fn_arg_names: unexpected item {:?}",
                 def_id
             );
         }
     };
     providers.all_local_trait_impls = |tcx, ()| &tcx.resolutions(()).trait_impls;
+    providers.local_trait_impls =
+        |tcx, trait_id| tcx.resolutions(()).trait_impls.get(&trait_id).map_or(&[], |xs| &xs[..]);
     providers.expn_that_defined =
         |tcx, id| tcx.resolutions(()).expn_that_defined.get(&id).copied().unwrap_or(ExpnId::root());
     providers.in_scope_traits_map = |tcx, id| {
diff --git a/compiler/rustc_middle/src/hir/place.rs b/compiler/rustc_middle/src/hir/place.rs
index 316ad80eb98..60ce8544aa0 100644
--- a/compiler/rustc_middle/src/hir/place.rs
+++ b/compiler/rustc_middle/src/hir/place.rs
@@ -53,7 +53,10 @@ pub struct Projection<'tcx> {
     pub kind: ProjectionKind,
 }
 
-/// A `Place` represents how a value is located in memory.
+/// A `Place` represents how a value is located in memory. This does not
+/// always correspond to a syntactic place expression. For example, when
+/// processing a pattern, a `Place` can be used to refer to the sub-value
+/// currently being inspected.
 ///
 /// This is an HIR version of [`rustc_middle::mir::Place`].
 #[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
@@ -67,7 +70,10 @@ pub struct Place<'tcx> {
     pub projections: Vec<Projection<'tcx>>,
 }
 
-/// A `PlaceWithHirId` represents how a value is located in memory.
+/// A `PlaceWithHirId` represents how a value is located in memory. This does not
+/// always correspond to a syntactic place expression. For example, when
+/// processing a pattern, a `Place` can be used to refer to the sub-value
+/// currently being inspected.
 ///
 /// This is an HIR version of [`rustc_middle::mir::Place`].
 #[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs
index 88bf17070b9..d5a408fdfa6 100644
--- a/compiler/rustc_middle/src/lint.rs
+++ b/compiler/rustc_middle/src/lint.rs
@@ -51,8 +51,13 @@ impl LintLevelSource {
     }
 }
 
-/// A tuple of a lint level and its source.
-pub type LevelAndSource = (Level, LintLevelSource);
+/// Convenience helper for moving things around together that frequently are paired
+#[derive(Copy, Clone, Debug, HashStable, Encodable, Decodable)]
+pub struct LevelAndSource {
+    pub level: Level,
+    pub lint_id: Option<LintExpectationId>,
+    pub src: LintLevelSource,
+}
 
 /// Return type for the `shallow_lint_levels_on` query.
 ///
@@ -69,14 +74,18 @@ pub struct ShallowLintLevelMap {
 ///
 /// The return of this function is suitable for diagnostics.
 pub fn reveal_actual_level(
-    level: Option<Level>,
+    level: Option<(Level, Option<LintExpectationId>)>,
     src: &mut LintLevelSource,
     sess: &Session,
     lint: LintId,
-    probe_for_lint_level: impl FnOnce(LintId) -> (Option<Level>, LintLevelSource),
-) -> Level {
+    probe_for_lint_level: impl FnOnce(
+        LintId,
+    )
+        -> (Option<(Level, Option<LintExpectationId>)>, LintLevelSource),
+) -> (Level, Option<LintExpectationId>) {
     // If `level` is none then we actually assume the default level for this lint.
-    let mut level = level.unwrap_or_else(|| lint.lint.default_level(sess.edition()));
+    let (mut level, mut lint_id) =
+        level.unwrap_or_else(|| (lint.lint.default_level(sess.edition()), None));
 
     // If we're about to issue a warning, check at the last minute for any
     // directives against the warnings "lint". If, for example, there's an
@@ -88,16 +97,17 @@ pub fn reveal_actual_level(
     // future compatibility warning.
     if level == Level::Warn && lint != LintId::of(FORBIDDEN_LINT_GROUPS) {
         let (warnings_level, warnings_src) = probe_for_lint_level(LintId::of(builtin::WARNINGS));
-        if let Some(configured_warning_level) = warnings_level {
+        if let Some((configured_warning_level, configured_lint_id)) = warnings_level {
             if configured_warning_level != Level::Warn {
                 level = configured_warning_level;
+                lint_id = configured_lint_id;
                 *src = warnings_src;
             }
         }
     }
 
     // Ensure that we never exceed the `--cap-lints` argument unless the source is a --force-warn
-    level = if let LintLevelSource::CommandLine(_, Level::ForceWarn(_)) = src {
+    level = if let LintLevelSource::CommandLine(_, Level::ForceWarn) = src {
         level
     } else {
         cmp::min(level, sess.opts.lint_cap.unwrap_or(Level::Forbid))
@@ -108,7 +118,7 @@ pub fn reveal_actual_level(
         level = cmp::min(*driver_level, level);
     }
 
-    level
+    (level, lint_id)
 }
 
 impl ShallowLintLevelMap {
@@ -121,11 +131,11 @@ impl ShallowLintLevelMap {
         tcx: TyCtxt<'_>,
         id: LintId,
         start: HirId,
-    ) -> (Option<Level>, LintLevelSource) {
+    ) -> (Option<(Level, Option<LintExpectationId>)>, LintLevelSource) {
         if let Some(map) = self.specs.get(&start.local_id)
-            && let Some(&(level, src)) = map.get(&id)
+            && let Some(&LevelAndSource { level, lint_id, src }) = map.get(&id)
         {
-            return (Some(level), src);
+            return (Some((level, lint_id)), src);
         }
 
         let mut owner = start.owner;
@@ -137,9 +147,9 @@ impl ShallowLintLevelMap {
                 specs = &tcx.shallow_lint_levels_on(owner).specs;
             }
             if let Some(map) = specs.get(&parent.local_id)
-                && let Some(&(level, src)) = map.get(&id)
+                && let Some(&LevelAndSource { level, lint_id, src }) = map.get(&id)
             {
-                return (Some(level), src);
+                return (Some((level, lint_id)), src);
             }
         }
 
@@ -153,18 +163,18 @@ impl ShallowLintLevelMap {
         tcx: TyCtxt<'_>,
         lint: LintId,
         cur: HirId,
-    ) -> (Level, LintLevelSource) {
+    ) -> LevelAndSource {
         let (level, mut src) = self.probe_for_lint_level(tcx, lint, cur);
-        let level = reveal_actual_level(level, &mut src, tcx.sess, lint, |lint| {
+        let (level, lint_id) = reveal_actual_level(level, &mut src, tcx.sess, lint, |lint| {
             self.probe_for_lint_level(tcx, lint, cur)
         });
-        (level, src)
+        LevelAndSource { level, lint_id, src }
     }
 }
 
 impl TyCtxt<'_> {
     /// Fetch and return the user-visible lint level for the given lint at the given HirId.
-    pub fn lint_level_at_node(self, lint: &'static Lint, id: HirId) -> (Level, LintLevelSource) {
+    pub fn lint_level_at_node(self, lint: &'static Lint, id: HirId) -> LevelAndSource {
         self.shallow_lint_levels_on(id.owner).lint_level_id_at_node(self, LintId::of(lint), id)
     }
 }
@@ -267,8 +277,7 @@ fn explain_lint_level_source(
 pub fn lint_level(
     sess: &Session,
     lint: &'static Lint,
-    level: Level,
-    src: LintLevelSource,
+    level: LevelAndSource,
     span: Option<MultiSpan>,
     decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
 ) {
@@ -278,11 +287,12 @@ pub fn lint_level(
     fn lint_level_impl(
         sess: &Session,
         lint: &'static Lint,
-        level: Level,
-        src: LintLevelSource,
+        level: LevelAndSource,
         span: Option<MultiSpan>,
         decorate: Box<dyn '_ + for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>)>,
     ) {
+        let LevelAndSource { level, lint_id, src } = level;
+
         // Check for future incompatibility lints and issue a stronger warning.
         let future_incompatible = lint.future_incompatible;
 
@@ -301,7 +311,7 @@ pub fn lint_level(
                     return;
                 }
             }
-            Level::Expect(expect_id) => {
+            Level::Expect => {
                 // This case is special as we actually allow the lint itself in this context, but
                 // we can't return early like in the case for `Level::Allow` because we still
                 // need the lint diagnostic to be emitted to `rustc_error::DiagCtxtInner`.
@@ -309,10 +319,9 @@ pub fn lint_level(
                 // We can also not mark the lint expectation as fulfilled here right away, as it
                 // can still be cancelled in the decorate function. All of this means that we simply
                 // create a `Diag` and continue as we would for warnings.
-                rustc_errors::Level::Expect(expect_id)
+                rustc_errors::Level::Expect
             }
-            Level::ForceWarn(Some(expect_id)) => rustc_errors::Level::ForceWarning(Some(expect_id)),
-            Level::ForceWarn(None) => rustc_errors::Level::ForceWarning(None),
+            Level::ForceWarn => rustc_errors::Level::ForceWarning,
             Level::Warn => rustc_errors::Level::Warning,
             Level::Deny | Level::Forbid => rustc_errors::Level::Error,
         };
@@ -320,6 +329,9 @@ pub fn lint_level(
         if let Some(span) = span {
             err.span(span);
         }
+        if let Some(lint_id) = lint_id {
+            err.lint_id(lint_id);
+        }
 
         // If this code originates in a foreign macro, aka something that this crate
         // did not itself author, then it's likely that there's nothing this crate
@@ -350,7 +362,7 @@ pub fn lint_level(
         // the compiler. It is therefore not necessary to add any information for the user.
         // This will therefore directly call the decorate function which will in turn emit
         // the diagnostic.
-        if let Level::Expect(_) = level {
+        if let Level::Expect = level {
             decorate(&mut err);
             err.emit();
             return;
@@ -419,5 +431,5 @@ pub fn lint_level(
         explain_lint_level_source(lint, level, src, &mut err);
         err.emit()
     }
-    lint_level_impl(sess, lint, level, src, span, Box::new(decorate))
+    lint_level_impl(sess, lint, level, span, Box::new(decorate))
 }
diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
index 0cc72a261a5..00da1a6aeec 100644
--- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
+++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
@@ -172,6 +172,8 @@ impl CodegenFnAttrs {
     /// * `#[no_mangle]` is present
     /// * `#[export_name(...)]` is present
     /// * `#[linkage]` is present
+    ///
+    /// Keep this in sync with the logic for the unused_attributes for `#[inline]` lint.
     pub fn contains_extern_indicator(&self) -> bool {
         self.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
             || self.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs
index 66ece8f0e52..92eab59dd02 100644
--- a/compiler/rustc_middle/src/middle/region.rs
+++ b/compiler/rustc_middle/src/middle/region.rs
@@ -175,7 +175,7 @@ impl Scope {
         let Some(hir_id) = self.hir_id(scope_tree) else {
             return DUMMY_SP;
         };
-        let span = tcx.hir().span(hir_id);
+        let span = tcx.hir_span(hir_id);
         if let ScopeData::Remainder(first_statement_index) = self.data {
             if let Node::Block(blk) = tcx.hir_node(hir_id) {
                 // Want span for scope starting after the
@@ -199,8 +199,6 @@ impl Scope {
     }
 }
 
-pub type ScopeDepth = u32;
-
 /// The region scope tree encodes information about region relationships.
 #[derive(Default, Debug, HashStable)]
 pub struct ScopeTree {
@@ -213,7 +211,7 @@ pub struct ScopeTree {
     /// conditional expression or repeating block. (Note that the
     /// enclosing scope ID for the block associated with a closure is
     /// the closure itself.)
-    pub parent_map: FxIndexMap<Scope, (Scope, ScopeDepth)>,
+    pub parent_map: FxIndexMap<Scope, Scope>,
 
     /// Maps from a variable or binding ID to the block in which that
     /// variable is declared.
@@ -328,7 +326,7 @@ pub struct YieldData {
 }
 
 impl ScopeTree {
-    pub fn record_scope_parent(&mut self, child: Scope, parent: Option<(Scope, ScopeDepth)>) {
+    pub fn record_scope_parent(&mut self, child: Scope, parent: Option<Scope>) {
         debug!("{:?}.parent = {:?}", child, parent);
 
         if let Some(p) = parent {
@@ -353,7 +351,7 @@ impl ScopeTree {
 
     /// Returns the narrowest scope that encloses `id`, if any.
     pub fn opt_encl_scope(&self, id: Scope) -> Option<Scope> {
-        self.parent_map.get(&id).cloned().map(|(p, _)| p)
+        self.parent_map.get(&id).cloned()
     }
 
     /// Returns the lifetime of the local variable `var_id`, if any.
diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs
index ec128c8c478..9912e659b05 100644
--- a/compiler/rustc_middle/src/middle/stability.rs
+++ b/compiler/rustc_middle/src/middle/stability.rs
@@ -255,7 +255,7 @@ fn late_report_deprecation(
     // Calculating message for lint involves calling `self.def_path_str`,
     // which will by default invoke the expensive `visible_parent_map` query.
     // Skip all that work if the lint is allowed anyway.
-    if tcx.lint_level_at_node(lint, hir_id).0 == Level::Allow {
+    if tcx.lint_level_at_node(lint, hir_id).level == Level::Allow {
         return;
     }
 
diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs
index 83ada5c8afb..1ffe958dbdd 100644
--- a/compiler/rustc_middle/src/mir/mono.rs
+++ b/compiler/rustc_middle/src/mir/mono.rs
@@ -150,6 +150,7 @@ impl<'tcx> MonoItem<'tcx> {
 
         // If the function is #[naked] or contains any other attribute that requires exactly-once
         // instantiation:
+        // We emit an unused_attributes lint for this case, which should be kept in sync if possible.
         let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id());
         if codegen_fn_attrs.contains_extern_indicator()
             || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED)
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 4f86703e953..707c8d04d55 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -334,14 +334,19 @@ pub enum StatementKind<'tcx> {
     /// See [`Rvalue`] documentation for details on each of those.
     Assign(Box<(Place<'tcx>, Rvalue<'tcx>)>),
 
-    /// This represents all the reading that a pattern match may do (e.g., inspecting constants and
-    /// discriminant values), and the kind of pattern it comes from. This is in order to adapt
-    /// potential error messages to these specific patterns.
+    /// When executed at runtime, this is a nop.
     ///
-    /// Note that this also is emitted for regular `let` bindings to ensure that locals that are
-    /// never accessed still get some sanity checks for, e.g., `let x: ! = ..;`
+    /// During static analysis, a fake read:
+    /// - requires that the value being read is initialized (or, in the case
+    ///   of closures, that it was fully initialized at some point in the past)
+    /// - constitutes a use of a value for the purposes of NLL (i.e. if the
+    ///   value being fake-read is a reference, the lifetime of that reference
+    ///   will be extended to cover the `FakeRead`)
+    /// - but, unlike an actual read, does *not* invalidate any exclusive
+    ///   borrows.
     ///
-    /// When executed at runtime this is a nop.
+    /// See [`FakeReadCause`] for more details on the situations in which a
+    /// `FakeRead` is emitted.
     ///
     /// Disallowed after drop elaboration.
     FakeRead(Box<(FakeReadCause, Place<'tcx>)>),
@@ -518,28 +523,59 @@ pub enum RetagKind {
 /// The `FakeReadCause` describes the type of pattern why a FakeRead statement exists.
 #[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, Hash, HashStable, PartialEq)]
 pub enum FakeReadCause {
-    /// Inject a fake read of the borrowed input at the end of each guards
-    /// code.
+    /// A fake read injected into a match guard to ensure that the discriminants
+    /// that are being matched on aren't modified while the match guard is being
+    /// evaluated.
+    ///
+    /// At the beginning of each match guard, a [fake borrow][FakeBorrowKind] is
+    /// inserted for each discriminant accessed in the entire `match` statement.
+    ///
+    /// Then, at the end of the match guard, a `FakeRead(ForMatchGuard)` is
+    /// inserted to keep the fake borrows alive until that point.
     ///
     /// This should ensure that you cannot change the variant for an enum while
     /// you are in the midst of matching on it.
     ForMatchGuard,
 
-    /// `let x: !; match x {}` doesn't generate any read of x so we need to
-    /// generate a read of x to check that it is initialized and safe.
+    /// Fake read of the scrutinee of a `match` or destructuring `let`
+    /// (i.e. `let` with non-trivial pattern).
+    ///
+    /// In `match x { ... }`, we generate a `FakeRead(ForMatchedPlace, x)`
+    /// and insert it into the `otherwise_block` (which is supposed to be
+    /// unreachable for irrefutable pattern-matches like `match` or `let`).
+    ///
+    /// This is necessary because `let x: !; match x {}` doesn't generate any
+    /// actual read of x, so we need to generate a `FakeRead` to check that it
+    /// is initialized.
+    ///
+    /// If the `FakeRead(ForMatchedPlace)` is being performed with a closure
+    /// that doesn't capture the required upvars, the `FakeRead` within the
+    /// closure is omitted entirely.
     ///
-    /// If a closure pattern matches a Place starting with an Upvar, then we introduce a
-    /// FakeRead for that Place outside the closure, in such a case this option would be
-    /// Some(closure_def_id).
-    /// Otherwise, the value of the optional LocalDefId will be None.
+    /// To make sure that this is still sound, if a closure matches against
+    /// a Place starting with an Upvar, we hoist the `FakeRead` to the
+    /// definition point of the closure.
+    ///
+    /// If the `FakeRead` comes from being hoisted out of a closure like this,
+    /// we record the `LocalDefId` of the closure. Otherwise, the `Option` will be `None`.
     //
     // We can use LocalDefId here since fake read statements are removed
     // before codegen in the `CleanupNonCodegenStatements` pass.
     ForMatchedPlace(Option<LocalDefId>),
 
-    /// A fake read of the RefWithinGuard version of a bind-by-value variable
-    /// in a match guard to ensure that its value hasn't change by the time
-    /// we create the OutsideGuard version.
+    /// A fake read injected into a match guard to ensure that the places
+    /// bound by the pattern are immutable for the duration of the match guard.
+    ///
+    /// Within a match guard, references are created for each place that the
+    /// pattern creates a binding for — this is known as the `RefWithinGuard`
+    /// version of the variables. To make sure that the references stay
+    /// alive until the end of the match guard, and properly prevent the
+    /// places in question from being modified, a `FakeRead(ForGuardBinding)`
+    /// is inserted at the end of the match guard.
+    ///
+    /// For details on how these references are created, see the extensive
+    /// documentation on `bind_matched_candidate_for_guard` in
+    /// `rustc_mir_build`.
     ForGuardBinding,
 
     /// Officially, the semantics of
@@ -552,22 +588,42 @@ pub enum FakeReadCause {
     /// However, if we see the simple pattern `let var = <expr>`, we optimize this to
     /// evaluate `<expr>` directly into the variable `var`. This is mostly unobservable,
     /// but in some cases it can affect the borrow checker, as in #53695.
-    /// Therefore, we insert a "fake read" here to ensure that we get
-    /// appropriate errors.
     ///
-    /// If a closure pattern matches a Place starting with an Upvar, then we introduce a
-    /// FakeRead for that Place outside the closure, in such a case this option would be
-    /// Some(closure_def_id).
-    /// Otherwise, the value of the optional DefId will be None.
+    /// Therefore, we insert a `FakeRead(ForLet)` immediately after each `let`
+    /// with a trivial pattern.
+    ///
+    /// FIXME: `ExprUseVisitor` has an entirely different opinion on what `FakeRead(ForLet)`
+    /// is supposed to mean. If it was accurate to what MIR lowering does,
+    /// would it even make sense to hoist these out of closures like
+    /// `ForMatchedPlace`?
     ForLet(Option<LocalDefId>),
 
-    /// If we have an index expression like
+    /// Currently, index expressions overloaded through the `Index` trait
+    /// get lowered differently than index expressions with builtin semantics
+    /// for arrays and slices — the latter will emit code to perform
+    /// bound checks, and then return a MIR place that will only perform the
+    /// indexing "for real" when it gets incorporated into an instruction.
     ///
-    /// (*x)[1][{ x = y; 4}]
+    /// This is observable in the fact that the following compiles:
     ///
-    /// then the first bounds check is invalidated when we evaluate the second
-    /// index expression. Thus we create a fake borrow of `x` across the second
-    /// indexer, which will cause a borrow check error.
+    /// ```
+    /// fn f(x: &mut [&mut [u32]], i: usize) {
+    ///     x[i][x[i].len() - 1] += 1;
+    /// }
+    /// ```
+    ///
+    /// However, we need to be careful to not let the user invalidate the
+    /// bound check with an expression like
+    ///
+    /// `(*x)[1][{ x = y; 4}]`
+    ///
+    /// Here, the first bounds check would be invalidated when we evaluate the
+    /// second index expression. To make sure that this doesn't happen, we
+    /// create a fake borrow of `x` and hold it while we evaluate the second
+    /// index.
+    ///
+    /// This borrow is kept alive by a `FakeRead(ForIndex)` at the end of its
+    /// scope.
     ForIndex,
 }
 
@@ -1612,6 +1668,42 @@ pub enum BinOp {
     Offset,
 }
 
+// Assignment operators, e.g. `+=`. See comments on the corresponding variants
+// in `BinOp` for details.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)]
+pub enum AssignOp {
+    AddAssign,
+    SubAssign,
+    MulAssign,
+    DivAssign,
+    RemAssign,
+    BitXorAssign,
+    BitAndAssign,
+    BitOrAssign,
+    ShlAssign,
+    ShrAssign,
+}
+
+// Sometimes `BinOp` and `AssignOp` need the same treatment. The operations
+// covered by `AssignOp` are a subset of those covered by `BinOp`, so it makes
+// sense to convert `AssignOp` to `BinOp`.
+impl From<AssignOp> for BinOp {
+    fn from(op: AssignOp) -> BinOp {
+        match op {
+            AssignOp::AddAssign => BinOp::Add,
+            AssignOp::SubAssign => BinOp::Sub,
+            AssignOp::MulAssign => BinOp::Mul,
+            AssignOp::DivAssign => BinOp::Div,
+            AssignOp::RemAssign => BinOp::Rem,
+            AssignOp::BitXorAssign => BinOp::BitXor,
+            AssignOp::BitAndAssign => BinOp::BitAnd,
+            AssignOp::BitOrAssign => BinOp::BitOr,
+            AssignOp::ShlAssign => BinOp::Shl,
+            AssignOp::ShrAssign => BinOp::Shr,
+        }
+    }
+}
+
 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
 #[cfg(target_pointer_width = "64")]
 mod size_asserts {
diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs
index c382bcd726f..9ed1f10455a 100644
--- a/compiler/rustc_middle/src/query/keys.rs
+++ b/compiler/rustc_middle/src/query/keys.rs
@@ -586,7 +586,7 @@ impl Key for HirId {
     type Cache<V> = DefaultCache<Self, V>;
 
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
-        tcx.hir().span(*self)
+        tcx.hir_span(*self)
     }
 
     #[inline(always)]
@@ -599,7 +599,7 @@ impl Key for (LocalDefId, HirId) {
     type Cache<V> = DefaultCache<Self, V>;
 
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
-        tcx.hir().span(self.1)
+        tcx.hir_span(self.1)
     }
 
     #[inline(always)]
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index d7ed703f4ae..0d5fba3cc69 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -267,6 +267,8 @@ rustc_queries! {
     ///
     /// This is a specialized instance of [`Self::type_of`] that detects query cycles.
     /// Unless `CyclePlaceholder` needs to be handled separately, call [`Self::type_of`] instead.
+    /// This is used to improve the error message in cases where revealing the hidden type
+    /// for auto-trait leakage cycles.
     ///
     /// # Panics
     ///
@@ -278,6 +280,12 @@ rustc_queries! {
         }
         cycle_stash
     }
+    query type_of_opaque_hir_typeck(key: LocalDefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> {
+        desc { |tcx|
+            "computing type of opaque `{path}` via HIR typeck",
+            path = tcx.def_path_str(key),
+        }
+    }
 
     /// Returns whether the type alias given by `DefId` is lazy.
     ///
@@ -612,6 +620,7 @@ rustc_queries! {
 
     query check_coroutine_obligations(key: LocalDefId) -> Result<(), ErrorGuaranteed> {
         desc { |tcx| "verify auto trait bounds for coroutine interior type `{}`", tcx.def_path_str(key) }
+        return_result_from_ensure_ok
     }
 
     /// MIR after our optimization passes have run. This is MIR that is ready
@@ -1033,13 +1042,12 @@ rustc_queries! {
     /// Unsafety-check this `LocalDefId`.
     query check_unsafety(key: LocalDefId) {
         desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) }
-        cache_on_disk_if { true }
     }
 
     /// Checks well-formedness of tail calls (`become f()`).
     query check_tail_calls(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> {
         desc { |tcx| "tail-call-checking `{}`", tcx.def_path_str(key) }
-        cache_on_disk_if { true }
+        return_result_from_ensure_ok
     }
 
     /// Returns the types assumed to be well formed while "inside" of the given item.
@@ -1308,7 +1316,7 @@ rustc_queries! {
 
     query check_match(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> {
         desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) }
-        cache_on_disk_if { true }
+        return_result_from_ensure_ok
     }
 
     /// Performs part of the privacy check and computes effective visibilities.
@@ -1502,6 +1510,11 @@ rustc_queries! {
         desc { "finding local trait impls" }
     }
 
+    /// Return all `impl` blocks of the given trait in the current crate.
+    query local_trait_impls(trait_id: DefId) -> &'tcx [LocalDefId] {
+        desc { "finding local trait impls of `{}`", tcx.def_path_str(trait_id) }
+    }
+
     /// Given a trait `trait_id`, return all known `impl` blocks.
     query trait_impls_of(trait_id: DefId) -> &'tcx ty::trait_def::TraitImpls {
         arena_cache
@@ -1602,7 +1615,6 @@ rustc_queries! {
     /// `Err(AlwaysRequiresDrop)` is returned.
     query adt_significant_drop_tys(def_id: DefId) -> Result<&'tcx ty::List<Ty<'tcx>>, AlwaysRequiresDrop> {
         desc { |tcx| "computing when `{}` has a significant destructor", tcx.def_path_str(def_id) }
-        cache_on_disk_if { false }
     }
 
     /// Returns a list of types which (a) have a potentially significant destructor
@@ -1624,7 +1636,6 @@ rustc_queries! {
     /// Otherwise, there is a risk of query cycles.
     query list_significant_drop_tys(ty: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> &'tcx ty::List<Ty<'tcx>> {
         desc { |tcx| "computing when `{}` has a significant destructor", ty.value }
-        cache_on_disk_if { false }
     }
 
     /// Computes the layout of a type. Note that this implicitly
@@ -2512,7 +2523,6 @@ rustc_queries! {
     /// monomorphized.
     query check_mono_item(key: ty::Instance<'tcx>) {
         desc { "monomorphization-time checking" }
-        cache_on_disk_if { true }
     }
 
     /// Builds the set of functions that should be skipped for the move-size check.
diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs
index 14e3ce8bef6..c6ecc679b7b 100644
--- a/compiler/rustc_middle/src/query/on_disk_cache.rs
+++ b/compiler/rustc_middle/src/query/on_disk_cache.rs
@@ -16,7 +16,8 @@ use rustc_serialize::opaque::{FileEncodeResult, FileEncoder, IntEncodedWithFixed
 use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 use rustc_session::Session;
 use rustc_span::hygiene::{
-    ExpnId, HygieneDecodeContext, HygieneEncodeContext, SyntaxContext, SyntaxContextData,
+    ExpnId, HygieneDecodeContext, HygieneEncodeContext, SyntaxContext,
+    SyntaxContextDataNonRecursive as SyntaxContextData,
 };
 use rustc_span::source_map::Spanned;
 use rustc_span::{
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index 6783bbf8bf4..8d373cb3b30 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -27,7 +27,7 @@ use tracing::instrument;
 
 use crate::middle::region;
 use crate::mir::interpret::AllocId;
-use crate::mir::{self, BinOp, BorrowKind, FakeReadCause, UnOp};
+use crate::mir::{self, AssignOp, BinOp, BorrowKind, FakeReadCause, UnOp};
 use crate::thir::visit::for_each_immediate_subpat;
 use crate::ty::adjustment::PointerCoercion;
 use crate::ty::layout::IntegerExt;
@@ -403,7 +403,7 @@ pub enum ExprKind<'tcx> {
     },
     /// A *non-overloaded* operation assignment, e.g. `lhs += rhs`.
     AssignOp {
-        op: BinOp,
+        op: AssignOp,
         lhs: ExprId,
         rhs: ExprId,
     },
diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs
index 811bd8fb458..aa2ee756bc5 100644
--- a/compiler/rustc_middle/src/traits/select.rs
+++ b/compiler/rustc_middle/src/traits/select.rs
@@ -95,10 +95,16 @@ pub type EvaluationCache<'tcx, ENV> = Cache<(ENV, ty::PolyTraitPredicate<'tcx>),
 /// parameter environment.
 #[derive(PartialEq, Eq, Debug, Clone, TypeVisitable)]
 pub enum SelectionCandidate<'tcx> {
+    /// A built-in implementation for the `Sized` trait. This is preferred
+    /// over all other candidates.
+    SizedCandidate {
+        has_nested: bool,
+    },
+
     /// A builtin implementation for some specific traits, used in cases
     /// where we cannot rely an ordinary library implementations.
     ///
-    /// The most notable examples are `sized`, `Copy` and `Clone`. This is also
+    /// The most notable examples are `Copy` and `Clone`. This is also
     /// used for the `DiscriminantKind` and `Pointee` trait, both of which have
     /// an associated type.
     BuiltinCandidate {
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index cb245c0aec4..00fe5cb0c5d 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -236,7 +236,7 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {
     }
 
     fn destructor(self, tcx: TyCtxt<'tcx>) -> Option<AdtDestructorKind> {
-        Some(match self.destructor(tcx)?.constness {
+        Some(match tcx.constness(self.destructor(tcx)?.did) {
             hir::Constness::Const => AdtDestructorKind::Const,
             hir::Constness::NotConst => AdtDestructorKind::NotConst,
         })
diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs
index 703b6ce9247..ff9096695d4 100644
--- a/compiler/rustc_middle/src/ty/closure.rs
+++ b/compiler/rustc_middle/src/ty/closure.rs
@@ -150,9 +150,9 @@ impl<'tcx> CapturedPlace<'tcx> {
     /// Return span pointing to use that resulted in selecting the captured path
     pub fn get_path_span(&self, tcx: TyCtxt<'tcx>) -> Span {
         if let Some(path_expr_id) = self.info.path_expr_id {
-            tcx.hir().span(path_expr_id)
+            tcx.hir_span(path_expr_id)
         } else if let Some(capture_kind_expr_id) = self.info.capture_kind_expr_id {
-            tcx.hir().span(capture_kind_expr_id)
+            tcx.hir_span(capture_kind_expr_id)
         } else {
             // Fallback on upvars mentioned if neither path or capture expr id is captured
 
@@ -166,9 +166,9 @@ impl<'tcx> CapturedPlace<'tcx> {
     /// Return span pointing to use that resulted in selecting the current capture kind
     pub fn get_capture_kind_span(&self, tcx: TyCtxt<'tcx>) -> Span {
         if let Some(capture_kind_expr_id) = self.info.capture_kind_expr_id {
-            tcx.hir().span(capture_kind_expr_id)
+            tcx.hir_span(capture_kind_expr_id)
         } else if let Some(path_expr_id) = self.info.path_expr_id {
-            tcx.hir().span(path_expr_id)
+            tcx.hir_span(path_expr_id)
         } else {
             // Fallback on upvars mentioned if neither path or capture expr id is captured
 
diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs
index 72263d84580..2f21d19e03c 100644
--- a/compiler/rustc_middle/src/ty/consts/valtree.rs
+++ b/compiler/rustc_middle/src/ty/consts/valtree.rs
@@ -33,7 +33,7 @@ pub enum ValTreeKind<'tcx> {
     /// The fields of any kind of aggregate. Structs, tuples and arrays are represented by
     /// listing their fields' values in order.
     ///
-    /// Enums are represented by storing their discriminant as a field, followed by all
+    /// Enums are represented by storing their variant index as a u32 field, followed by all
     /// the fields of the variant.
     ///
     /// ZST types are represented as an empty slice.
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 08d4c1f9cf2..162ca1f4af8 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -206,6 +206,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     fn type_of(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> {
         self.type_of(def_id)
     }
+    fn type_of_opaque_hir_typeck(self, def_id: LocalDefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> {
+        self.type_of_opaque_hir_typeck(def_id)
+    }
 
     type AdtDef = ty::AdtDef<'tcx>;
     fn adt_def(self, adt_def_id: DefId) -> Self::AdtDef {
@@ -446,6 +449,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.is_lang_item(def_id, trait_lang_item_to_lang_item(lang_item))
     }
 
+    fn is_default_trait(self, def_id: DefId) -> bool {
+        self.is_default_trait(def_id)
+    }
+
     fn as_lang_item(self, def_id: DefId) -> Option<TraitSolverLangItem> {
         lang_item_to_trait_lang_item(self.lang_items().from_def_id(def_id)?)
     }
@@ -1539,6 +1546,25 @@ impl<'tcx> TyCtxt<'tcx> {
         self.reserve_and_set_memory_dedup(alloc, salt)
     }
 
+    pub fn default_traits(self) -> &'static [rustc_hir::LangItem] {
+        match self.sess.opts.unstable_opts.experimental_default_bounds {
+            true => &[
+                LangItem::Sized,
+                LangItem::DefaultTrait1,
+                LangItem::DefaultTrait2,
+                LangItem::DefaultTrait3,
+                LangItem::DefaultTrait4,
+            ],
+            false => &[LangItem::Sized],
+        }
+    }
+
+    pub fn is_default_trait(self, def_id: DefId) -> bool {
+        self.default_traits()
+            .iter()
+            .any(|&default_trait| self.lang_items().get(default_trait) == Some(def_id))
+    }
+
     /// Returns a range of the start/end indices specified with the
     /// `rustc_layout_scalar_valid_range` attribute.
     // FIXME(eddyb) this is an awkward spot for this method, maybe move it?
@@ -1781,10 +1807,15 @@ impl<'tcx> TyCtxt<'tcx> {
         // - needs_metadata: for putting into crate metadata.
         // - instrument_coverage: for putting into coverage data (see
         //   `hash_mir_source`).
+        // - metrics_dir: metrics use the strict version hash in the filenames
+        //   for dumped metrics files to prevent overwriting distinct metrics
+        //   for similar source builds (may change in the future, this is part
+        //   of the proof of concept impl for the metrics initiative project goal)
         cfg!(debug_assertions)
             || self.sess.opts.incremental.is_some()
             || self.needs_metadata()
             || self.sess.instrument_coverage()
+            || self.sess.opts.unstable_opts.metrics_dir.is_some()
     }
 
     #[inline]
@@ -1930,10 +1961,10 @@ impl<'tcx> TyCtxt<'tcx> {
         // As a consequence, this LocalDefId is always re-created before it is needed by the incr.
         // comp. engine itself.
         //
-        // This call also writes to the value of `source_span` and `expn_that_defined` queries.
+        // This call also writes to the value of the `source_span` query.
         // This is fine because:
-        // - those queries are `eval_always` so we won't miss their result changing;
-        // - this write will have happened before these queries are called.
+        // - that query is `eval_always` so we won't miss its result changing;
+        // - this write will have happened before that query is called.
         let def_id = self.untracked.definitions.write().create_def(parent, data);
 
         // This function modifies `self.definitions` using a side-effect.
@@ -3022,8 +3053,8 @@ impl<'tcx> TyCtxt<'tcx> {
         span: impl Into<MultiSpan>,
         decorator: impl for<'a> LintDiagnostic<'a, ()>,
     ) {
-        let (level, src) = self.lint_level_at_node(lint, hir_id);
-        lint_level(self.sess, lint, level, src, Some(span.into()), |lint| {
+        let level = self.lint_level_at_node(lint, hir_id);
+        lint_level(self.sess, lint, level, Some(span.into()), |lint| {
             decorator.decorate_lint(lint);
         })
     }
@@ -3040,8 +3071,8 @@ impl<'tcx> TyCtxt<'tcx> {
         span: impl Into<MultiSpan>,
         decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
     ) {
-        let (level, src) = self.lint_level_at_node(lint, hir_id);
-        lint_level(self.sess, lint, level, src, Some(span.into()), decorate);
+        let level = self.lint_level_at_node(lint, hir_id);
+        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
@@ -3108,8 +3139,8 @@ impl<'tcx> TyCtxt<'tcx> {
         id: HirId,
         decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
     ) {
-        let (level, src) = self.lint_level_at_node(lint, id);
-        lint_level(self.sess, lint, level, src, None, decorate);
+        let level = self.lint_level_at_node(lint, id);
+        lint_level(self.sess, lint, level, None, decorate);
     }
 
     pub fn in_scope_traits(self, id: HirId) -> Option<&'tcx [TraitCandidate]> {
@@ -3248,6 +3279,11 @@ impl<'tcx> TyCtxt<'tcx> {
         self.sess.opts.unstable_opts.next_solver.coherence
     }
 
+    #[allow(rustc::bad_opt_access)]
+    pub fn use_typing_mode_borrowck(self) -> bool {
+        self.next_trait_solver_globally() || self.sess.opts.unstable_opts.typing_mode_borrowck
+    }
+
     pub fn is_impl_trait_in_trait(self, def_id: DefId) -> bool {
         self.opt_rpitit_info(def_id).is_some()
     }
diff --git a/compiler/rustc_middle/src/ty/erase_regions.rs b/compiler/rustc_middle/src/ty/erase_regions.rs
index f25c48cf42a..8bddb5c0fd7 100644
--- a/compiler/rustc_middle/src/ty/erase_regions.rs
+++ b/compiler/rustc_middle/src/ty/erase_regions.rs
@@ -44,7 +44,13 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for RegionEraserVisitor<'tcx> {
     }
 
     fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        if ty.has_infer() { ty.super_fold_with(self) } else { self.tcx.erase_regions_ty(ty) }
+        if !ty.has_type_flags(TypeFlags::HAS_BINDER_VARS | TypeFlags::HAS_FREE_REGIONS) {
+            ty
+        } else if ty.has_infer() {
+            ty.super_fold_with(self)
+        } else {
+            self.tcx.erase_regions_ty(ty)
+        }
     }
 
     fn fold_binder<T>(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T>
@@ -64,4 +70,20 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for RegionEraserVisitor<'tcx> {
             _ => self.tcx.lifetimes.re_erased,
         }
     }
+
+    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        if ct.has_type_flags(TypeFlags::HAS_BINDER_VARS | TypeFlags::HAS_FREE_REGIONS) {
+            ct.super_fold_with(self)
+        } else {
+            ct
+        }
+    }
+
+    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+        if p.has_type_flags(TypeFlags::HAS_BINDER_VARS | TypeFlags::HAS_FREE_REGIONS) {
+            p.super_fold_with(self)
+        } else {
+            p
+        }
+    }
 }
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index e3b3eccffb5..55ebd15248c 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -720,7 +720,7 @@ impl<'tcx> Instance<'tcx> {
             ty::TypingEnv::fully_monomorphized(),
             def_id,
             args,
-            ty.ty_adt_def().and_then(|adt| tcx.hir().span_if_local(adt.did())).unwrap_or(DUMMY_SP),
+            ty.ty_adt_def().and_then(|adt| tcx.hir_span_if_local(adt.did())).unwrap_or(DUMMY_SP),
         )
     }
 
@@ -732,7 +732,7 @@ impl<'tcx> Instance<'tcx> {
             ty::TypingEnv::fully_monomorphized(),
             def_id,
             args,
-            ty.ty_adt_def().and_then(|adt| tcx.hir().span_if_local(adt.did())).unwrap_or(DUMMY_SP),
+            ty.ty_adt_def().and_then(|adt| tcx.hir_span_if_local(adt.did())).unwrap_or(DUMMY_SP),
         )
     }
 
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index ac98cbc8d6c..80f1bd7c6f4 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -48,7 +48,7 @@ use rustc_serialize::{Decodable, Encodable};
 use rustc_session::lint::LintBuffer;
 pub use rustc_session::lint::RegisteredTools;
 use rustc_span::hygiene::MacroKind;
-use rustc_span::{ExpnId, ExpnKind, Ident, Span, Symbol, kw, sym};
+use rustc_span::{DUMMY_SP, ExpnId, ExpnKind, Ident, Span, Symbol, kw, sym};
 pub use rustc_type_ir::relate::VarianceDiagInfo;
 pub use rustc_type_ir::*;
 use tracing::{debug, instrument};
@@ -782,7 +782,22 @@ pub struct OpaqueHiddenType<'tcx> {
     pub ty: Ty<'tcx>,
 }
 
+/// Whether we're currently in HIR typeck or MIR borrowck.
+#[derive(Debug, Clone, Copy)]
+pub enum DefiningScopeKind {
+    /// During writeback in typeck, we don't care about regions and simply
+    /// erase them. This means we also don't check whether regions are
+    /// universal in the opaque type key. This will only be checked in
+    /// MIR borrowck.
+    HirTypeck,
+    MirBorrowck,
+}
+
 impl<'tcx> OpaqueHiddenType<'tcx> {
+    pub fn new_error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> OpaqueHiddenType<'tcx> {
+        OpaqueHiddenType { span: DUMMY_SP, ty: Ty::new_error(tcx, guar) }
+    }
+
     pub fn build_mismatch_error(
         &self,
         other: &Self,
@@ -808,8 +823,7 @@ impl<'tcx> OpaqueHiddenType<'tcx> {
         self,
         opaque_type_key: OpaqueTypeKey<'tcx>,
         tcx: TyCtxt<'tcx>,
-        // typeck errors have subpar spans for opaque types, so delay error reporting until borrowck.
-        ignore_errors: bool,
+        defining_scope_kind: DefiningScopeKind,
     ) -> Self {
         let OpaqueTypeKey { def_id, args } = opaque_type_key;
 
@@ -828,10 +842,19 @@ impl<'tcx> OpaqueHiddenType<'tcx> {
         let map = args.iter().zip(id_args).collect();
         debug!("map = {:#?}", map);
 
-        // Convert the type from the function into a type valid outside
-        // the function, by replacing invalid regions with 'static,
-        // after producing an error for each of them.
-        self.fold_with(&mut opaque_types::ReverseMapper::new(tcx, map, self.span, ignore_errors))
+        // Convert the type from the function into a type valid outside by mapping generic
+        // parameters to into the context of the opaque.
+        //
+        // We erase regions when doing this during HIR typeck.
+        let this = match defining_scope_kind {
+            DefiningScopeKind::HirTypeck => tcx.erase_regions(self),
+            DefiningScopeKind::MirBorrowck => self,
+        };
+        let result = this.fold_with(&mut opaque_types::ReverseMapper::new(tcx, map, self.span));
+        if cfg!(debug_assertions) && matches!(defining_scope_kind, DefiningScopeKind::HirTypeck) {
+            assert_eq!(result.ty, tcx.erase_regions(result.ty));
+        }
+        result
     }
 }
 
@@ -953,7 +976,7 @@ impl<'tcx> rustc_type_ir::Flags for Clauses<'tcx> {
 /// environment. `ParamEnv` is the type that represents this information. See the
 /// [dev guide chapter][param_env_guide] for more information.
 ///
-/// [param_env_guide]: https://rustc-dev-guide.rust-lang.org/param_env/param_env_summary.html
+/// [param_env_guide]: https://rustc-dev-guide.rust-lang.org/typing_parameter_envs.html
 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
 #[derive(HashStable, TypeVisitable, TypeFoldable)]
 pub struct ParamEnv<'tcx> {
@@ -977,7 +1000,7 @@ impl<'tcx> ParamEnv<'tcx> {
     /// to use an empty environment. See the [dev guide section][param_env_guide]
     /// for information on what a `ParamEnv` is and how to acquire one.
     ///
-    /// [param_env_guide]: https://rustc-dev-guide.rust-lang.org/param_env/param_env_summary.html
+    /// [param_env_guide]: https://rustc-dev-guide.rust-lang.org/typing_parameter_envs.html
     #[inline]
     pub fn empty() -> Self {
         Self::new(ListWithCachedTypeInfo::empty())
@@ -1119,17 +1142,13 @@ pub struct PseudoCanonicalInput<'tcx, T> {
 pub struct Destructor {
     /// The `DefId` of the destructor method
     pub did: DefId,
-    /// The constness of the destructor method
-    pub constness: hir::Constness,
 }
 
 // FIXME: consider combining this definition with regular `Destructor`
 #[derive(Copy, Clone, Debug, HashStable, Encodable, Decodable)]
 pub struct AsyncDestructor {
-    /// The `DefId` of the async destructor future constructor
-    pub ctor: DefId,
-    /// The `DefId` of the async destructor future type
-    pub future: DefId,
+    /// The `DefId` of the `impl AsyncDrop`
+    pub impl_did: LocalDefId,
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
diff --git a/compiler/rustc_middle/src/ty/opaque_types.rs b/compiler/rustc_middle/src/ty/opaque_types.rs
index 56c44c8a84c..c72efde0994 100644
--- a/compiler/rustc_middle/src/ty/opaque_types.rs
+++ b/compiler/rustc_middle/src/ty/opaque_types.rs
@@ -20,12 +20,6 @@ pub(super) struct ReverseMapper<'tcx> {
     /// for an explanation of this field.
     do_not_error: bool,
 
-    /// We do not want to emit any errors in typeck because
-    /// the spans in typeck are subpar at the moment.
-    /// Borrowck will do the same work again (this time with
-    /// lifetime information) and thus report better errors.
-    ignore_errors: bool,
-
     /// Span of function being checked.
     span: Span,
 }
@@ -35,9 +29,8 @@ impl<'tcx> ReverseMapper<'tcx> {
         tcx: TyCtxt<'tcx>,
         map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>,
         span: Span,
-        ignore_errors: bool,
     ) -> Self {
-        Self { tcx, map, do_not_error: false, ignore_errors, span }
+        Self { tcx, map, do_not_error: false, span }
     }
 
     fn fold_kind_no_missing_regions_error(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> {
@@ -176,20 +169,18 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> {
                     Some(u) => panic!("type mapped to unexpected kind: {u:?}"),
                     None => {
                         debug!(?param, ?self.map);
-                        if !self.ignore_errors {
-                            self.tcx
-                                .dcx()
-                                .struct_span_err(
-                                    self.span,
-                                    format!(
-                                        "type parameter `{ty}` is part of concrete type but not \
+                        let guar = self
+                            .tcx
+                            .dcx()
+                            .struct_span_err(
+                                self.span,
+                                format!(
+                                    "type parameter `{ty}` is part of concrete type but not \
                                           used in parameter list for the `impl Trait` type alias"
-                                    ),
-                                )
-                                .emit();
-                        }
-
-                        Ty::new_misc_error(self.tcx)
+                                ),
+                            )
+                            .emit();
+                        Ty::new_error(self.tcx, guar)
                     }
                 }
             }
@@ -217,8 +208,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> {
                                 ct: ct.to_string(),
                                 span: self.span,
                             })
-                            .emit_unless(self.ignore_errors);
-
+                            .emit();
                         ty::Const::new_error(self.tcx, guar)
                     }
                 }
diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs
index 19e2b574563..b56e0862692 100644
--- a/compiler/rustc_middle/src/ty/parameterized.rs
+++ b/compiler/rustc_middle/src/ty/parameterized.rs
@@ -65,9 +65,11 @@ trivially_parameterized_over_tcx! {
     crate::middle::lib_features::FeatureStability,
     crate::middle::resolve_bound_vars::ObjectLifetimeDefault,
     crate::mir::ConstQualifs,
+    ty::AsyncDestructor,
     ty::AssocItemContainer,
     ty::Asyncness,
     ty::DeducedParamAttrs,
+    ty::Destructor,
     ty::Generics,
     ty::ImplPolarity,
     ty::ImplTraitInTraitData,
@@ -111,7 +113,7 @@ trivially_parameterized_over_tcx! {
     rustc_span::Span,
     rustc_span::Symbol,
     rustc_span::def_id::DefPathHash,
-    rustc_span::hygiene::SyntaxContextData,
+    rustc_span::hygiene::SyntaxContextDataNonRecursive,
     rustc_span::Ident,
     rustc_type_ir::Variance,
     rustc_hir::Attribute,
diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs
index dc2040aa5cf..5904deaaaad 100644
--- a/compiler/rustc_middle/src/ty/print/mod.rs
+++ b/compiler/rustc_middle/src/ty/print/mod.rs
@@ -139,8 +139,7 @@ pub trait Printer<'tcx>: Sized {
 
                     match key.disambiguated_data.data {
                         DefPathData::Closure => {
-                            // FIXME(async_closures): This is somewhat ugly.
-                            // We need to additionally print the `kind` field of a closure if
+                            // We need to additionally print the `kind` field of a coroutine if
                             // it is desugared from a coroutine-closure.
                             if let Some(hir::CoroutineKind::Desugared(
                                 _,
@@ -156,6 +155,10 @@ pub trait Printer<'tcx>: Sized {
                                 // Closures' own generics are only captures, don't print them.
                             }
                         }
+                        DefPathData::SyntheticCoroutineBody => {
+                            // Synthetic coroutine bodies have no distinct generics, since like
+                            // closures they're all just internal state of the coroutine.
+                        }
                         // This covers both `DefKind::AnonConst` and `DefKind::InlineConst`.
                         // Anon consts doesn't have their own generics, and inline consts' own
                         // generics are their inferred types, so don't print them.
diff --git a/compiler/rustc_middle/src/ty/rvalue_scopes.rs b/compiler/rustc_middle/src/ty/rvalue_scopes.rs
index b00c8169a36..9bf6e3a7590 100644
--- a/compiler/rustc_middle/src/ty/rvalue_scopes.rs
+++ b/compiler/rustc_middle/src/ty/rvalue_scopes.rs
@@ -38,7 +38,7 @@ impl RvalueScopes {
         let mut id = Scope { local_id: expr_id, data: ScopeData::Node };
         let mut backwards_incompatible = None;
 
-        while let Some(&(p, _)) = region_scope_tree.parent_map.get(&id) {
+        while let Some(&p) = region_scope_tree.parent_map.get(&id) {
             match p.data {
                 ScopeData::Destruction => {
                     debug!("temporary_scope({expr_id:?}) = {id:?} [enclosing]");
diff --git a/compiler/rustc_middle/src/ty/significant_drop_order.rs b/compiler/rustc_middle/src/ty/significant_drop_order.rs
index 2d9e0331451..4881d611c12 100644
--- a/compiler/rustc_middle/src/ty/significant_drop_order.rs
+++ b/compiler/rustc_middle/src/ty/significant_drop_order.rs
@@ -154,7 +154,7 @@ pub fn ty_dtor_span<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Span> {
             let dtor = if let Some(dtor) = tcx.adt_destructor(did) {
                 dtor.did
             } else if let Some(dtor) = tcx.adt_async_destructor(did) {
-                dtor.future
+                return Some(tcx.source_span(dtor.impl_did));
             } else {
                 return Some(try_local_did_span(did));
             };
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 60fd531b4d0..798ef352c40 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -86,15 +86,15 @@ impl fmt::Debug for ty::LateParamRegion {
 impl fmt::Debug for ty::LateParamRegionKind {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match *self {
-            ty::LateParamRegionKind::Anon(idx) => write!(f, "BrAnon({idx})"),
+            ty::LateParamRegionKind::Anon(idx) => write!(f, "LateAnon({idx})"),
             ty::LateParamRegionKind::Named(did, name) => {
                 if did.is_crate_root() {
-                    write!(f, "BrNamed({name})")
+                    write!(f, "LateNamed({name})")
                 } else {
-                    write!(f, "BrNamed({did:?}, {name})")
+                    write!(f, "LateNamed({did:?}, {name})")
                 }
             }
-            ty::LateParamRegionKind::ClosureEnv => write!(f, "BrEnv"),
+            ty::LateParamRegionKind::ClosureEnv => write!(f, "LateEnv"),
         }
     }
 }
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 74a94d82784..27ee363f1c1 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -8,7 +8,7 @@ use std::iter;
 use std::ops::{ControlFlow, Range};
 
 use hir::def::{CtorKind, DefKind};
-use rustc_abi::{ExternAbi, FIRST_VARIANT, FieldIdx, VariantIdx};
+use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx};
 use rustc_errors::{ErrorGuaranteed, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::LangItem;
@@ -1441,23 +1441,7 @@ impl<'tcx> Ty<'tcx> {
 
     #[tracing::instrument(level = "trace", skip(tcx))]
     pub fn fn_sig(self, tcx: TyCtxt<'tcx>) -> PolyFnSig<'tcx> {
-        match self.kind() {
-            FnDef(def_id, args) => tcx.fn_sig(*def_id).instantiate(tcx, args),
-            FnPtr(sig_tys, hdr) => sig_tys.with(*hdr),
-            Error(_) => {
-                // ignore errors (#54954)
-                Binder::dummy(ty::FnSig {
-                    inputs_and_output: ty::List::empty(),
-                    c_variadic: false,
-                    safety: hir::Safety::Safe,
-                    abi: ExternAbi::Rust,
-                })
-            }
-            Closure(..) => bug!(
-                "to get the signature of a closure, use `args.as_closure().sig()` not `fn_sig()`",
-            ),
-            _ => bug!("Ty::fn_sig() called on non-fn type: {:?}", self),
-        }
+        self.kind().fn_sig(tcx)
     }
 
     #[inline]
@@ -2043,32 +2027,7 @@ impl<'tcx> Ty<'tcx> {
     /// nested types may be further simplified, the outermost [`TyKind`] or
     /// type constructor remains the same.
     pub fn is_known_rigid(self) -> bool {
-        match self.kind() {
-            Bool
-            | Char
-            | Int(_)
-            | Uint(_)
-            | Float(_)
-            | Adt(_, _)
-            | Foreign(_)
-            | Str
-            | Array(_, _)
-            | Pat(_, _)
-            | Slice(_)
-            | RawPtr(_, _)
-            | Ref(_, _, _)
-            | FnDef(_, _)
-            | FnPtr(..)
-            | Dynamic(_, _, _)
-            | Closure(_, _)
-            | CoroutineClosure(_, _)
-            | Coroutine(_, _)
-            | CoroutineWitness(..)
-            | Never
-            | Tuple(_)
-            | UnsafeBinder(_) => true,
-            Error(_) | Infer(_) | Alias(_, _) | Param(_) | Bound(_, _) | Placeholder(_) => false,
-        }
+        self.kind().is_known_rigid()
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs
index 8fa1c569737..ea25ce65f77 100644
--- a/compiler/rustc_middle/src/ty/trait_def.rs
+++ b/compiler/rustc_middle/src/ty/trait_def.rs
@@ -129,21 +129,6 @@ impl<'tcx> TraitDef {
 }
 
 impl<'tcx> TyCtxt<'tcx> {
-    /// `trait_def_id` MUST BE the `DefId` of a trait.
-    pub fn for_each_impl<F: FnMut(DefId)>(self, trait_def_id: DefId, mut f: F) {
-        let impls = self.trait_impls_of(trait_def_id);
-
-        for &impl_def_id in impls.blanket_impls.iter() {
-            f(impl_def_id);
-        }
-
-        for v in impls.non_blanket_impls.values() {
-            for &impl_def_id in v {
-                f(impl_def_id);
-            }
-        }
-    }
-
     /// Iterate over every impl that could possibly match the self type `self_ty`.
     ///
     /// `trait_def_id` MUST BE the `DefId` of a trait.
@@ -235,7 +220,7 @@ pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> Trait
         }
     }
 
-    for &impl_def_id in tcx.hir_trait_impls(trait_id) {
+    for &impl_def_id in tcx.local_trait_impls(trait_id) {
         let impl_def_id = impl_def_id.to_def_id();
 
         let impl_self_ty = tcx.type_of(impl_def_id).instantiate_identity();
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index 06054e22e76..7c437abfe24 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -158,7 +158,7 @@ pub struct TypeckResults<'tcx> {
     /// We also store the type here, so that the compiler can use it as a hint
     /// for figuring out hidden types, even if they are only set in dead code
     /// (which doesn't show up in MIR).
-    pub concrete_opaque_types: FxIndexMap<ty::OpaqueTypeKey<'tcx>, ty::OpaqueHiddenType<'tcx>>,
+    pub concrete_opaque_types: FxIndexMap<LocalDefId, ty::OpaqueHiddenType<'tcx>>,
 
     /// Tracks the minimum captures required for a closure;
     /// see `MinCaptureInformationMap` for more details.
@@ -197,12 +197,6 @@ pub struct TypeckResults<'tcx> {
     /// formatting modified file tests/ui/coroutine/retain-resume-ref.rs
     pub coroutine_stalled_predicates: FxIndexSet<(ty::Predicate<'tcx>, ObligationCause<'tcx>)>,
 
-    /// We sometimes treat byte string literals (which are of type `&[u8; N]`)
-    /// as `&[u8]`, depending on the pattern in which they are used.
-    /// This hashset records all instances where we behave
-    /// like this to allow `const_to_pat` to reliably handle this situation.
-    pub treat_byte_string_as_slice: ItemLocalSet,
-
     /// Contains the data for evaluating the effect of feature `capture_disjoint_fields`
     /// on closure size.
     pub closure_size_eval: LocalDefIdMap<ClosureSizeProfileData<'tcx>>,
@@ -237,7 +231,6 @@ impl<'tcx> TypeckResults<'tcx> {
             closure_fake_reads: Default::default(),
             rvalue_scopes: Default::default(),
             coroutine_stalled_predicates: Default::default(),
-            treat_byte_string_as_slice: Default::default(),
             closure_size_eval: Default::default(),
             offset_of_data: Default::default(),
         }
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index c0d4130336e..e4863896fc8 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -389,77 +389,83 @@ impl<'tcx> TyCtxt<'tcx> {
     /// Calculate the destructor of a given type.
     pub fn calculate_dtor(
         self,
-        adt_did: DefId,
-        validate: impl Fn(Self, DefId) -> Result<(), ErrorGuaranteed>,
+        adt_did: LocalDefId,
+        validate: impl Fn(Self, LocalDefId) -> Result<(), ErrorGuaranteed>,
     ) -> Option<ty::Destructor> {
         let drop_trait = self.lang_items().drop_trait()?;
         self.ensure_ok().coherent_trait(drop_trait).ok()?;
 
-        let ty = self.type_of(adt_did).instantiate_identity();
         let mut dtor_candidate = None;
-        self.for_each_relevant_impl(drop_trait, ty, |impl_did| {
+        // `Drop` impls can only be written in the same crate as the adt, and cannot be blanket impls
+        for &impl_did in self.local_trait_impls(drop_trait) {
+            let Some(adt_def) = self.type_of(impl_did).skip_binder().ty_adt_def() else { continue };
+            if adt_def.did() != adt_did.to_def_id() {
+                continue;
+            }
+
             if validate(self, impl_did).is_err() {
                 // Already `ErrorGuaranteed`, no need to delay a span bug here.
-                return;
+                continue;
             }
 
             let Some(item_id) = self.associated_item_def_ids(impl_did).first() else {
                 self.dcx()
                     .span_delayed_bug(self.def_span(impl_did), "Drop impl without drop function");
-                return;
+                continue;
             };
 
-            if let Some((old_item_id, _)) = dtor_candidate {
+            if self.def_kind(item_id) != DefKind::AssocFn {
+                self.dcx().span_delayed_bug(self.def_span(item_id), "drop is not a function");
+                continue;
+            }
+
+            if let Some(old_item_id) = dtor_candidate {
                 self.dcx()
                     .struct_span_err(self.def_span(item_id), "multiple drop impls found")
                     .with_span_note(self.def_span(old_item_id), "other impl here")
                     .delay_as_bug();
             }
 
-            dtor_candidate = Some((*item_id, self.impl_trait_header(impl_did).unwrap().constness));
-        });
+            dtor_candidate = Some(*item_id);
+        }
 
-        let (did, constness) = dtor_candidate?;
-        Some(ty::Destructor { did, constness })
+        let did = dtor_candidate?;
+        Some(ty::Destructor { did })
     }
 
     /// Calculate the async destructor of a given type.
     pub fn calculate_async_dtor(
         self,
-        adt_did: DefId,
-        validate: impl Fn(Self, DefId) -> Result<(), ErrorGuaranteed>,
+        adt_did: LocalDefId,
+        validate: impl Fn(Self, LocalDefId) -> Result<(), ErrorGuaranteed>,
     ) -> Option<ty::AsyncDestructor> {
         let async_drop_trait = self.lang_items().async_drop_trait()?;
         self.ensure_ok().coherent_trait(async_drop_trait).ok()?;
 
-        let ty = self.type_of(adt_did).instantiate_identity();
         let mut dtor_candidate = None;
-        self.for_each_relevant_impl(async_drop_trait, ty, |impl_did| {
+        // `AsyncDrop` impls can only be written in the same crate as the adt, and cannot be blanket impls
+        for &impl_did in self.local_trait_impls(async_drop_trait) {
+            let Some(adt_def) = self.type_of(impl_did).skip_binder().ty_adt_def() else { continue };
+            if adt_def.did() != adt_did.to_def_id() {
+                continue;
+            }
+
             if validate(self, impl_did).is_err() {
                 // Already `ErrorGuaranteed`, no need to delay a span bug here.
-                return;
+                continue;
             }
 
-            let [future, ctor] = self.associated_item_def_ids(impl_did) else {
-                self.dcx().span_delayed_bug(
-                    self.def_span(impl_did),
-                    "AsyncDrop impl without async_drop function or Dropper type",
-                );
-                return;
-            };
-
-            if let Some((_, _, old_impl_did)) = dtor_candidate {
+            if let Some(old_impl_did) = dtor_candidate {
                 self.dcx()
                     .struct_span_err(self.def_span(impl_did), "multiple async drop impls found")
                     .with_span_note(self.def_span(old_impl_did), "other impl here")
                     .delay_as_bug();
             }
 
-            dtor_candidate = Some((*future, *ctor, impl_did));
-        });
+            dtor_candidate = Some(impl_did);
+        }
 
-        let (future, ctor, _) = dtor_candidate?;
-        Some(ty::AsyncDestructor { future, ctor })
+        Some(ty::AsyncDestructor { impl_did: dtor_candidate? })
     }
 
     /// Returns async drop glue morphology for a definition. To get async drop
@@ -1546,55 +1552,6 @@ impl<'tcx> Ty<'tcx> {
     }
 }
 
-pub enum ExplicitSelf<'tcx> {
-    ByValue,
-    ByReference(ty::Region<'tcx>, hir::Mutability),
-    ByRawPointer(hir::Mutability),
-    ByBox,
-    Other,
-}
-
-impl<'tcx> ExplicitSelf<'tcx> {
-    /// Categorizes an explicit self declaration like `self: SomeType`
-    /// into either `self`, `&self`, `&mut self`, `Box<Self>`, or
-    /// `Other`.
-    /// This is mainly used to require the arbitrary_self_types feature
-    /// in the case of `Other`, to improve error messages in the common cases,
-    /// and to make `Other` dyn-incompatible.
-    ///
-    /// Examples:
-    ///
-    /// ```ignore (illustrative)
-    /// impl<'a> Foo for &'a T {
-    ///     // Legal declarations:
-    ///     fn method1(self: &&'a T); // ExplicitSelf::ByReference
-    ///     fn method2(self: &'a T); // ExplicitSelf::ByValue
-    ///     fn method3(self: Box<&'a T>); // ExplicitSelf::ByBox
-    ///     fn method4(self: Rc<&'a T>); // ExplicitSelf::Other
-    ///
-    ///     // Invalid cases will be caught by `check_method_receiver`:
-    ///     fn method_err1(self: &'a mut T); // ExplicitSelf::Other
-    ///     fn method_err2(self: &'static T) // ExplicitSelf::ByValue
-    ///     fn method_err3(self: &&T) // ExplicitSelf::ByReference
-    /// }
-    /// ```
-    ///
-    pub fn determine<P>(self_arg_ty: Ty<'tcx>, is_self_ty: P) -> ExplicitSelf<'tcx>
-    where
-        P: Fn(Ty<'tcx>) -> bool,
-    {
-        use self::ExplicitSelf::*;
-
-        match *self_arg_ty.kind() {
-            _ if is_self_ty(self_arg_ty) => ByValue,
-            ty::Ref(region, ty, mutbl) if is_self_ty(ty) => ByReference(region, mutbl),
-            ty::RawPtr(ty, mutbl) if is_self_ty(ty) => ByRawPointer(mutbl),
-            _ if self_arg_ty.boxed_ty().is_some_and(is_self_ty) => ByBox,
-            _ => Other,
-        }
-    }
-}
-
 /// Returns a list of types such that the given type needs drop if and only if
 /// *any* of the returned types need drop. Returns `Err(AlwaysRequiresDrop)` if
 /// this type always needs drop.
diff --git a/compiler/rustc_middle/src/util/bug.rs b/compiler/rustc_middle/src/util/bug.rs
index 7dda68b8393..c4357fae104 100644
--- a/compiler/rustc_middle/src/util/bug.rs
+++ b/compiler/rustc_middle/src/util/bug.rs
@@ -49,7 +49,7 @@ fn opt_span_bug_fmt<S: Into<MultiSpan>>(
 pub fn trigger_delayed_bug(tcx: TyCtxt<'_>, key: rustc_hir::def_id::DefId) {
     tcx.dcx().span_delayed_bug(
         tcx.def_span(key),
-        "delayed bug triggered by #[rustc_error(delayed_bug_from_inside_query)]",
+        "delayed bug triggered by #[rustc_delayed_bug_from_inside_query]",
     );
 }
 
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 50ca924baf9..fbe53081156 100644
--- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs
@@ -159,7 +159,7 @@ fn find_capture_matching_projections<'a, 'tcx>(
 ) -> Option<(usize, &'a Capture<'tcx>)> {
     let hir_projections = convert_to_hir_projections_and_truncate_for_capture(projections);
 
-    upvars.get_by_key_enumerated(var_hir_id.0).find(|(_, capture)| {
+    upvars.get_by_key_enumerated(var_hir_id.0.local_id).find(|(_, capture)| {
         let possible_ancestor_proj_kinds: Vec<_> =
             capture.captured_place.place.projections.iter().map(|proj| proj.kind).collect();
         is_ancestor_or_same_capture(&possible_ancestor_proj_kinds, &hir_projections)
@@ -258,7 +258,7 @@ impl<'tcx> PlaceBuilder<'tcx> {
                 self.projection
             ),
             PlaceBase::Upvar { var_hir_id, closure_def_id: _ } => span_bug!(
-                cx.tcx.hir().span(var_hir_id.0),
+                cx.tcx.hir_span(var_hir_id.0),
                 "could not resolve upvar: {var_hir_id:?} + {:?}",
                 self.projection
             ),
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 97d34b85f50..f9791776f71 100644
--- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
@@ -416,7 +416,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     if let Some(mir_place) = place_builder.try_to_place(this) {
                         this.cfg.push_fake_read(
                             block,
-                            this.source_info(this.tcx.hir().span(*hir_id)),
+                            this.source_info(this.tcx.hir_span(*hir_id)),
                             *cause,
                             mir_place,
                         );
diff --git a/compiler/rustc_mir_build/src/builder/expr/stmt.rs b/compiler/rustc_mir_build/src/builder/expr/stmt.rs
index 7f8a0a34c31..2dff26f02f3 100644
--- a/compiler/rustc_mir_build/src/builder/expr/stmt.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/stmt.rs
@@ -78,8 +78,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 // because AssignOp is only legal for Copy types
                 // (overloaded ops should be desugared into a call).
                 let result = unpack!(
-                    block =
-                        this.build_binary_op(block, op, expr_span, lhs_ty, Operand::Copy(lhs), rhs)
+                    block = this.build_binary_op(
+                        block,
+                        op.into(),
+                        expr_span,
+                        lhs_ty,
+                        Operand::Copy(lhs),
+                        rhs
+                    )
                 );
                 this.cfg.push_assign(block, source_info, lhs, result);
 
diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs
index c8b69a6ec62..8ca9ab58e45 100644
--- a/compiler/rustc_mir_build/src/builder/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/mod.rs
@@ -13,7 +13,7 @@ use rustc_data_structures::sorted_map::SortedIndexMultiMap;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Node};
+use rustc_hir::{self as hir, BindingMode, ByRef, HirId, ItemLocalId, Node};
 use rustc_index::bit_set::GrowableBitSet;
 use rustc_index::{Idx, IndexSlice, IndexVec};
 use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
@@ -48,11 +48,11 @@ pub(crate) fn closure_saved_names_of_captured_variables<'tcx>(
 /// this directly; instead use the cached version via `mir_built`.
 pub fn build_mir<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Body<'tcx> {
     tcx.ensure_done().thir_abstract_const(def);
-    if let Err(e) = tcx.check_match(def) {
+    if let Err(e) = tcx.ensure_ok().check_match(def) {
         return construct_error(tcx, def, e);
     }
 
-    if let Err(err) = tcx.check_tail_calls(def) {
+    if let Err(err) = tcx.ensure_ok().check_tail_calls(def) {
         return construct_error(tcx, def, err);
     }
 
@@ -221,7 +221,7 @@ struct Builder<'a, 'tcx> {
     coverage_info: Option<coverageinfo::CoverageInfoBuilder>,
 }
 
-type CaptureMap<'tcx> = SortedIndexMultiMap<usize, HirId, Capture<'tcx>>;
+type CaptureMap<'tcx> = SortedIndexMultiMap<usize, ItemLocalId, Capture<'tcx>>;
 
 #[derive(Debug)]
 struct Capture<'tcx> {
@@ -457,7 +457,7 @@ fn construct_fn<'tcx>(
 
     // Figure out what primary body this item has.
     let body = tcx.hir_body_owned_by(fn_def);
-    let span_with_body = tcx.hir().span_with_body(fn_id);
+    let span_with_body = tcx.hir_span_with_body(fn_id);
     let return_ty_span = tcx
         .hir_fn_decl_by_hir_id(fn_id)
         .unwrap_or_else(|| span_bug!(span, "can't build MIR for {:?}", fn_def))
@@ -853,6 +853,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         let capture_tys = upvar_args.upvar_tys();
 
         let tcx = self.tcx;
+        let mut upvar_owner = None;
         self.upvars = tcx
             .closure_captures(self.def_id)
             .iter()
@@ -866,6 +867,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
                     _ => bug!("Expected an upvar"),
                 };
+                let upvar_base = upvar_owner.get_or_insert(var_id.owner);
+                assert_eq!(*upvar_base, var_id.owner);
+                let var_id = var_id.local_id;
 
                 let mutability = captured_place.mutability;
 
diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs
index e56c0ae92ca..e42336a1dbb 100644
--- a/compiler/rustc_mir_build/src/builder/scope.rs
+++ b/compiler/rustc_mir_build/src/builder/scope.rs
@@ -1496,7 +1496,7 @@ fn build_scope_drops<'tcx>(
                 // path, then don't generate the drop. (We only take this into
                 // account for non-unwind paths so as not to disturb the
                 // caching mechanism.)
-                if scope.moved_locals.iter().any(|&o| o == local) {
+                if scope.moved_locals.contains(&local) {
                     continue;
                 }
 
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index 7f2e7d5ca83..6fb9974fc8e 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -195,7 +195,7 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
 
     /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
     fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool {
-        self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow
+        self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).level == Level::Allow
     }
 
     /// Handle closures/coroutines/inline-consts, which is unsafecked with their parent body.
@@ -292,8 +292,10 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                 });
             }
             BlockSafety::ExplicitUnsafe(hir_id) => {
-                let used =
-                    matches!(self.tcx.lint_level_at_node(UNUSED_UNSAFE, hir_id), (Level::Allow, _));
+                let used = matches!(
+                    self.tcx.lint_level_at_node(UNUSED_UNSAFE, hir_id).level,
+                    Level::Allow
+                );
                 self.in_safety_context(
                     SafetyContext::UnsafeBlock {
                         span: block.span,
@@ -943,7 +945,7 @@ impl UnsafeOpKind {
             }
         });
         let unsafe_not_inherited_note = if let Some((id, _)) = note_non_inherited {
-            let span = tcx.hir().span(id);
+            let span = tcx.hir_span(id);
             let span = tcx.sess.source_map().guess_head_span(span);
             Some(UnsafeNotInheritedNote { span })
         } else {
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index b8af77245f2..31e22e69111 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -9,7 +9,7 @@ use rustc_middle::hir::place::{
     Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind,
 };
 use rustc_middle::middle::region;
-use rustc_middle::mir::{self, BinOp, BorrowKind, UnOp};
+use rustc_middle::mir::{self, AssignOp, BinOp, BorrowKind, UnOp};
 use rustc_middle::thir::*;
 use rustc_middle::ty::adjustment::{
     Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCoercion,
@@ -489,7 +489,7 @@ impl<'tcx> ThirBuildCx<'tcx> {
                     self.overloaded_operator(expr, Box::new([lhs, rhs]))
                 } else {
                     ExprKind::AssignOp {
-                        op: bin_op(op.node),
+                        op: assign_op(op.node),
                         lhs: self.mirror_expr(lhs),
                         rhs: self.mirror_expr(rhs),
                     }
@@ -1347,3 +1347,18 @@ fn bin_op(op: hir::BinOpKind) -> BinOp {
         _ => bug!("no equivalent for ast binop {:?}", op),
     }
 }
+
+fn assign_op(op: hir::AssignOpKind) -> AssignOp {
+    match op {
+        hir::AssignOpKind::AddAssign => AssignOp::AddAssign,
+        hir::AssignOpKind::SubAssign => AssignOp::SubAssign,
+        hir::AssignOpKind::MulAssign => AssignOp::MulAssign,
+        hir::AssignOpKind::DivAssign => AssignOp::DivAssign,
+        hir::AssignOpKind::RemAssign => AssignOp::RemAssign,
+        hir::AssignOpKind::BitXorAssign => AssignOp::BitXorAssign,
+        hir::AssignOpKind::BitAndAssign => AssignOp::BitAndAssign,
+        hir::AssignOpKind::BitOrAssign => AssignOp::BitOrAssign,
+        hir::AssignOpKind::ShlAssign => AssignOp::ShlAssign,
+        hir::AssignOpKind::ShrAssign => AssignOp::ShrAssign,
+    }
+}
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 095d3e75da1..9f5e2c06b22 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -1025,7 +1025,7 @@ fn find_fallback_pattern_typo<'tcx>(
     pat: &Pat<'tcx>,
     lint: &mut UnreachablePattern<'_>,
 ) {
-    if let (Level::Allow, _) = cx.tcx.lint_level_at_node(UNREACHABLE_PATTERNS, hir_id) {
+    if let Level::Allow = cx.tcx.lint_level_at_node(UNREACHABLE_PATTERNS, hir_id).level {
         // This is because we use `with_no_trimmed_paths` later, so if we never emit the lint we'd
         // ICE. At the same time, we don't really need to do all of this if we won't emit anything.
         return;
@@ -1174,7 +1174,7 @@ fn report_arm_reachability<'p, 'tcx>(
     for (arm, is_useful) in report.arm_usefulness.iter() {
         if let Usefulness::Redundant(explanation) = is_useful {
             let hir_id = arm.arm_data;
-            let arm_span = cx.tcx.hir().span(hir_id);
+            let arm_span = cx.tcx.hir_span(hir_id);
             let whole_arm_span = if is_match_arm {
                 // If the arm is followed by a comma, extend the span to include it.
                 let with_whitespace = sm.span_extend_while_whitespace(arm_span);
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index 667d59d858e..372453688d2 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -58,25 +58,13 @@ struct ConstToPat<'tcx> {
     span: Span,
     id: hir::HirId,
 
-    treat_byte_string_as_slice: bool,
-
     c: ty::Const<'tcx>,
 }
 
 impl<'tcx> ConstToPat<'tcx> {
     fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self {
         trace!(?pat_ctxt.typeck_results.hir_owner);
-        ConstToPat {
-            tcx: pat_ctxt.tcx,
-            typing_env: pat_ctxt.typing_env,
-            span,
-            id,
-            treat_byte_string_as_slice: pat_ctxt
-                .typeck_results
-                .treat_byte_string_as_slice
-                .contains(&id.local_id),
-            c,
-        }
+        ConstToPat { tcx: pat_ctxt.tcx, typing_env: pat_ctxt.typing_env, span, id, c }
     }
 
     fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
@@ -108,8 +96,6 @@ impl<'tcx> ConstToPat<'tcx> {
         uv: ty::UnevaluatedConst<'tcx>,
         ty: Ty<'tcx>,
     ) -> Box<Pat<'tcx>> {
-        trace!(self.treat_byte_string_as_slice);
-
         // It's not *technically* correct to be revealing opaque types here as borrowcheck has
         // not run yet. However, CTFE itself uses `TypingMode::PostAnalysis` unconditionally even
         // during typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821).
@@ -307,21 +293,8 @@ impl<'tcx> ConstToPat<'tcx> {
                             ty,
                         );
                     } else {
-                        // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
-                        // matching against references, you can only use byte string literals.
-                        // The typechecker has a special case for byte string literals, by treating them
-                        // as slices. This means we turn `&[T; N]` constants into slice patterns, which
-                        // has no negative effects on pattern matching, even if we're actually matching on
-                        // arrays.
-                        let pointee_ty = match *pointee_ty.kind() {
-                            ty::Array(elem_ty, _) if self.treat_byte_string_as_slice => {
-                                Ty::new_slice(tcx, elem_ty)
-                            }
-                            _ => *pointee_ty,
-                        };
                         // References have the same valtree representation as their pointee.
-                        let subpattern = self.valtree_to_pat(cv, pointee_ty);
-                        PatKind::Deref { subpattern }
+                        PatKind::Deref { subpattern: self.valtree_to_pat(cv, *pointee_ty) }
                     }
                 }
             },
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 4bfeab44bf4..d20e051548b 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -11,7 +11,7 @@ use rustc_abi::{FieldIdx, Integer};
 use rustc_errors::codes::*;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::pat_util::EnumerateAndAdjustIterator;
-use rustc_hir::{self as hir, RangeEnd};
+use rustc_hir::{self as hir, LangItem, RangeEnd};
 use rustc_index::Idx;
 use rustc_middle::mir::interpret::LitToConstInput;
 use rustc_middle::thir::{
@@ -130,7 +130,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
 
         // Lower the endpoint into a temporary `PatKind` that will then be
         // deconstructed to obtain the constant value and other data.
-        let mut kind: PatKind<'tcx> = self.lower_pat_expr(expr);
+        let mut kind: PatKind<'tcx> = self.lower_pat_expr(expr, None);
 
         // Unpeel any ascription or inline-const wrapper nodes.
         loop {
@@ -294,7 +294,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
 
             hir::PatKind::Never => PatKind::Never,
 
-            hir::PatKind::Expr(value) => self.lower_pat_expr(value),
+            hir::PatKind::Expr(value) => self.lower_pat_expr(value, Some(ty)),
 
             hir::PatKind::Range(ref lo_expr, ref hi_expr, end) => {
                 let (lo_expr, hi_expr) = (lo_expr.as_deref(), hi_expr.as_deref());
@@ -630,7 +630,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
     /// - Paths (e.g. `FOO`, `foo::BAR`, `Option::None`)
     /// - Inline const blocks (e.g. `const { 1 + 1 }`)
     /// - Literals, possibly negated (e.g. `-128u8`, `"hello"`)
-    fn lower_pat_expr(&mut self, expr: &'tcx hir::PatExpr<'tcx>) -> PatKind<'tcx> {
+    fn lower_pat_expr(
+        &mut self,
+        expr: &'tcx hir::PatExpr<'tcx>,
+        pat_ty: Option<Ty<'tcx>>,
+    ) -> PatKind<'tcx> {
         let (lit, neg) = match &expr.kind {
             hir::PatExprKind::Path(qpath) => {
                 return self.lower_path(qpath, expr.hir_id, expr.span).kind;
@@ -641,7 +645,31 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
             hir::PatExprKind::Lit { lit, negated } => (lit, *negated),
         };
 
-        let ct_ty = self.typeck_results.node_type(expr.hir_id);
+        // We handle byte string literal patterns by using the pattern's type instead of the
+        // literal's type in `const_to_pat`: if the literal `b"..."` matches on a slice reference,
+        // the pattern's type will be `&[u8]` whereas the literal's type is `&[u8; 3]`; using the
+        // pattern's type means we'll properly translate it to a slice reference pattern. This works
+        // because slices and arrays have the same valtree representation.
+        // HACK: As an exception, use the literal's type if `pat_ty` is `String`; this can happen if
+        // `string_deref_patterns` is enabled. There's a special case for that when lowering to MIR.
+        // FIXME(deref_patterns): This hack won't be necessary once `string_deref_patterns` is
+        // superseded by a more general implementation of deref patterns.
+        let ct_ty = match pat_ty {
+            Some(pat_ty)
+                if let ty::Adt(def, _) = *pat_ty.kind()
+                    && self.tcx.is_lang_item(def.did(), LangItem::String) =>
+            {
+                if !self.tcx.features().string_deref_patterns() {
+                    span_bug!(
+                        expr.span,
+                        "matching on `String` went through without enabling string_deref_patterns"
+                    );
+                }
+                self.typeck_results.node_type(expr.hir_id)
+            }
+            Some(pat_ty) => pat_ty,
+            None => self.typeck_results.node_type(expr.hir_id),
+        };
         let lit_input = LitToConstInput { lit: &lit.node, ty: ct_ty, neg };
         let constant = self.tcx.at(expr.span).lit_to_const(lit_input);
         self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind
diff --git a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs
index ceea72c6755..375db17fb73 100644
--- a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs
+++ b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs
@@ -53,9 +53,13 @@ impl<'tcx> ConstMutationChecker<'_, 'tcx> {
         //
         //     #[const_mutation_allowed]
         //     pub const LOG: Log = Log { msg: "" };
-        match self.tcx.calculate_dtor(def_id, |_, _| Ok(())) {
-            Some(_) => None,
-            None => Some(def_id),
+        // FIXME: this should not be checking for `Drop` impls,
+        // but whether it or any field has a Drop impl (`needs_drop`)
+        // as fields' Drop impls may make this observable, too.
+        match self.tcx.type_of(def_id).skip_binder().ty_adt_def().map(|adt| adt.has_dtor(self.tcx))
+        {
+            Some(true) => None,
+            Some(false) | None => Some(def_id),
         }
     }
 
diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
index 89a306c6104..dd0e07f2218 100644
--- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
+++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
@@ -219,6 +219,8 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>(
         mir::MirSource::from_instance(InstanceKind::Item(body_def.def_id().to_def_id()));
     dump_mir(tcx, false, "built", &"after", &by_move_body, |_, _| Ok(()));
 
+    // Feed HIR because we try to access this body's attrs in the inliner.
+    body_def.feed_hir();
     // Inherited from the by-ref coroutine.
     body_def.codegen_fn_attrs(tcx.codegen_fn_attrs(coroutine_def_id).clone());
     body_def.coverage_attr_on(tcx.coverage_attr_on(coroutine_def_id));
diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs
index d83c0d40a7e..73bd2d0705e 100644
--- a/compiler/rustc_mir_transform/src/coverage/mappings.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs
@@ -96,7 +96,7 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>(
         }
     } else {
         // Extract coverage spans from MIR statements/terminators as normal.
-        extract_refined_covspans(mir_body, hir_info, graph, &mut code_mappings);
+        extract_refined_covspans(tcx, mir_body, hir_info, graph, &mut code_mappings);
     }
 
     branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, graph));
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs
index 8befe9c5d8d..f57a158e3e4 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -1,7 +1,9 @@
 use std::collections::VecDeque;
+use std::iter;
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_middle::mir;
+use rustc_middle::ty::TyCtxt;
 use rustc_span::{DesugaringKind, ExpnKind, MacroKind, Span};
 use tracing::{debug, debug_span, instrument};
 
@@ -11,8 +13,9 @@ use crate::coverage::{ExtractedHirInfo, mappings, unexpand};
 
 mod from_mir;
 
-pub(super) fn extract_refined_covspans(
-    mir_body: &mir::Body<'_>,
+pub(super) fn extract_refined_covspans<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    mir_body: &mir::Body<'tcx>,
     hir_info: &ExtractedHirInfo,
     graph: &CoverageGraph,
     code_mappings: &mut impl Extend<mappings::CodeMapping>,
@@ -50,7 +53,7 @@ pub(super) fn extract_refined_covspans(
     // First, perform the passes that need macro information.
     covspans.sort_by(|a, b| graph.cmp_in_dominator_order(a.bcb, b.bcb));
     remove_unwanted_expansion_spans(&mut covspans);
-    split_visible_macro_spans(&mut covspans);
+    shrink_visible_macro_spans(tcx, &mut covspans);
 
     // We no longer need the extra information in `SpanFromMir`, so convert to `Covspan`.
     let mut covspans = covspans.into_iter().map(SpanFromMir::into_covspan).collect::<Vec<_>>();
@@ -83,9 +86,7 @@ pub(super) fn extract_refined_covspans(
     // Split the covspans into separate buckets that don't overlap any holes.
     let buckets = divide_spans_into_buckets(covspans, &holes);
 
-    for mut covspans in buckets {
-        // Make sure each individual bucket is internally sorted.
-        covspans.sort_by(compare_covspans);
+    for covspans in buckets {
         let _span = debug_span!("processing bucket", ?covspans).entered();
 
         let mut covspans = remove_unwanted_overlapping_spans(covspans);
@@ -129,82 +130,50 @@ fn remove_unwanted_expansion_spans(covspans: &mut Vec<SpanFromMir>) {
 }
 
 /// When a span corresponds to a macro invocation that is visible from the
-/// function body, split it into two parts. The first part covers just the
-/// macro name plus `!`, and the second part covers the rest of the macro
-/// invocation. This seems to give better results for code that uses macros.
-fn split_visible_macro_spans(covspans: &mut Vec<SpanFromMir>) {
-    let mut extra_spans = vec![];
-
-    covspans.retain(|covspan| {
-        let Some(ExpnKind::Macro(MacroKind::Bang, visible_macro)) = covspan.expn_kind else {
-            return true;
-        };
-
-        let split_len = visible_macro.as_str().len() as u32 + 1;
-        let (before, after) = covspan.span.split_at(split_len);
-        if !covspan.span.contains(before) || !covspan.span.contains(after) {
-            // Something is unexpectedly wrong with the split point.
-            // The debug assertion in `split_at` will have already caught this,
-            // but in release builds it's safer to do nothing and maybe get a
-            // bug report for unexpected coverage, rather than risk an ICE.
-            return true;
+/// function body, truncate it to just the macro name plus `!`.
+/// This seems to give better results for code that uses macros.
+fn shrink_visible_macro_spans(tcx: TyCtxt<'_>, covspans: &mut Vec<SpanFromMir>) {
+    let source_map = tcx.sess.source_map();
+
+    for covspan in covspans {
+        if matches!(covspan.expn_kind, Some(ExpnKind::Macro(MacroKind::Bang, _))) {
+            covspan.span = source_map.span_through_char(covspan.span, '!');
         }
-
-        extra_spans.push(SpanFromMir::new(before, covspan.expn_kind.clone(), covspan.bcb));
-        extra_spans.push(SpanFromMir::new(after, covspan.expn_kind.clone(), covspan.bcb));
-        false // Discard the original covspan that we just split.
-    });
-
-    // The newly-split spans are added at the end, so any previous sorting
-    // is not preserved.
-    covspans.extend(extra_spans);
+    }
 }
 
 /// Uses the holes to divide the given covspans into buckets, such that:
-/// - No span in any hole overlaps a bucket (truncating the spans if necessary).
+/// - No span in any hole overlaps a bucket (discarding spans if necessary).
 /// - The spans in each bucket are strictly after all spans in previous buckets,
 ///   and strictly before all spans in subsequent buckets.
 ///
-/// The resulting buckets are sorted relative to each other, but might not be
-/// internally sorted.
+/// The lists of covspans and holes must be sorted.
+/// The resulting buckets are sorted relative to each other, and each bucket's
+/// contents are sorted.
 #[instrument(level = "debug")]
 fn divide_spans_into_buckets(input_covspans: Vec<Covspan>, holes: &[Hole]) -> Vec<Vec<Covspan>> {
     debug_assert!(input_covspans.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le()));
     debug_assert!(holes.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le()));
 
-    // Now we're ready to start carving holes out of the initial coverage spans,
-    // and grouping them in buckets separated by the holes.
+    // Now we're ready to start grouping spans into buckets separated by holes.
 
     let mut input_covspans = VecDeque::from(input_covspans);
-    let mut fragments = vec![];
 
     // For each hole:
     // - Identify the spans that are entirely or partly before the hole.
-    // - Put those spans in a corresponding bucket, truncated to the start of the hole.
-    // - If one of those spans also extends after the hole, put the rest of it
-    //   in a "fragments" vector that is processed by the next hole.
+    // - Discard any that overlap with the hole.
+    // - Add the remaining identified spans to the corresponding bucket.
     let mut buckets = (0..holes.len()).map(|_| vec![]).collect::<Vec<_>>();
     for (hole, bucket) in holes.iter().zip(&mut buckets) {
-        let fragments_from_prev = std::mem::take(&mut fragments);
-
-        // Only inspect spans that precede or overlap this hole,
-        // leaving the rest to be inspected by later holes.
-        // (This relies on the spans and holes both being sorted.)
-        let relevant_input_covspans =
-            drain_front_while(&mut input_covspans, |c| c.span.lo() < hole.span.hi());
-
-        for covspan in fragments_from_prev.into_iter().chain(relevant_input_covspans) {
-            let (before, after) = covspan.split_around_hole_span(hole.span);
-            bucket.extend(before);
-            fragments.extend(after);
-        }
+        bucket.extend(
+            drain_front_while(&mut input_covspans, |c| c.span.lo() < hole.span.hi())
+                .filter(|c| !c.span.overlaps(hole.span)),
+        );
     }
 
-    // After finding the spans before each hole, any remaining fragments/spans
-    // form their own final bucket, after the final hole.
+    // Any remaining spans form their own final bucket, after the final hole.
     // (If there were no holes, this will just be all of the initial spans.)
-    fragments.extend(input_covspans);
-    buckets.push(fragments);
+    buckets.push(Vec::from(input_covspans));
 
     buckets
 }
@@ -215,7 +184,7 @@ fn drain_front_while<'a, T>(
     queue: &'a mut VecDeque<T>,
     mut pred_fn: impl FnMut(&T) -> bool,
 ) -> impl Iterator<Item = T> {
-    std::iter::from_fn(move || if pred_fn(queue.front()?) { queue.pop_front() } else { None })
+    iter::from_fn(move || queue.pop_front_if(|x| pred_fn(x)))
 }
 
 /// Takes one of the buckets of (sorted) spans extracted from MIR, and "refines"
@@ -258,22 +227,6 @@ struct Covspan {
 }
 
 impl Covspan {
-    /// Splits this covspan into 0-2 parts:
-    /// - The part that is strictly before the hole span, if any.
-    /// - The part that is strictly after the hole span, if any.
-    fn split_around_hole_span(&self, hole_span: Span) -> (Option<Self>, Option<Self>) {
-        let before = try {
-            let span = self.span.trim_end(hole_span)?;
-            Self { span, ..*self }
-        };
-        let after = try {
-            let span = self.span.trim_start(hole_span)?;
-            Self { span, ..*self }
-        };
-
-        (before, after)
-    }
-
     /// If `self` and `other` can be merged (i.e. they have the same BCB),
     /// mutates `self.span` to also include `other.span` and returns true.
     ///
diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
index 1faa2171c0b..804cd8ab3f7 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
@@ -120,22 +120,20 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
         // an `if condition { block }` has a span that includes the executed block, if true,
         // but for coverage, the code region executed, up to *and* through the SwitchInt,
         // actually stops before the if's block.)
-        TerminatorKind::Unreachable // Unreachable blocks are not connected to the MIR CFG
+        TerminatorKind::Unreachable
         | TerminatorKind::Assert { .. }
         | TerminatorKind::Drop { .. }
         | TerminatorKind::SwitchInt { .. }
-        // For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`.
         | TerminatorKind::FalseEdge { .. }
         | TerminatorKind::Goto { .. } => None,
 
         // Call `func` operand can have a more specific span when part of a chain of calls
-        TerminatorKind::Call { ref func, .. }
-        | TerminatorKind::TailCall { ref func, .. } => {
+        TerminatorKind::Call { ref func, .. } | TerminatorKind::TailCall { ref func, .. } => {
             let mut span = terminator.source_info.span;
-            if let mir::Operand::Constant(box constant) = func {
-                if constant.span.lo() > span.lo() {
-                    span = span.with_lo(constant.span.lo());
-                }
+            if let mir::Operand::Constant(constant) = func
+                && span.contains(constant.span)
+            {
+                span = constant.span;
             }
             Some(span)
         }
@@ -147,9 +145,7 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
         | TerminatorKind::Yield { .. }
         | TerminatorKind::CoroutineDrop
         | TerminatorKind::FalseUnwind { .. }
-        | TerminatorKind::InlineAsm { .. } => {
-            Some(terminator.source_info.span)
-        }
+        | TerminatorKind::InlineAsm { .. } => Some(terminator.source_info.span),
     }
 }
 
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index 0a54c780f31..68bc0ffce6b 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -3,14 +3,16 @@
 //! MIR may contain repeated and/or redundant computations. The objective of this pass is to detect
 //! such redundancies and re-use the already-computed result when possible.
 //!
-//! In a first pass, we compute a symbolic representation of values that are assigned to SSA
-//! locals. This symbolic representation is defined by the `Value` enum. Each produced instance of
-//! `Value` is interned as a `VnIndex`, which allows us to cheaply compute identical values.
-//!
 //! From those assignments, we construct a mapping `VnIndex -> Vec<(Local, Location)>` of available
 //! values, the locals in which they are stored, and the assignment location.
 //!
-//! In a second pass, we traverse all (non SSA) assignments `x = rvalue` and operands. For each
+//! We traverse all assignments `x = rvalue` and operands.
+//!
+//! For each SSA one, we compute a symbolic representation of values that are assigned to SSA
+//! locals. This symbolic representation is defined by the `Value` enum. Each produced instance of
+//! `Value` is interned as a `VnIndex`, which allows us to cheaply compute identical values.
+//!
+//! For each non-SSA
 //! one, we compute the `VnIndex` of the rvalue. If this `VnIndex` is associated to a constant, we
 //! replace the rvalue/operand by that constant. Otherwise, if there is an SSA local `y`
 //! associated to this `VnIndex`, and if its definition location strictly dominates the assignment
@@ -91,7 +93,7 @@ use rustc_const_eval::interpret::{
     ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar,
     intern_const_alloc_for_constprop,
 };
-use rustc_data_structures::fx::FxIndexSet;
+use rustc_data_structures::fx::{FxIndexSet, MutableValues};
 use rustc_data_structures::graph::dominators::Dominators;
 use rustc_hir::def::DefKind;
 use rustc_index::bit_set::DenseBitSet;
@@ -107,7 +109,7 @@ use rustc_span::def_id::DefId;
 use smallvec::SmallVec;
 use tracing::{debug, instrument, trace};
 
-use crate::ssa::{AssignedValue, SsaLocals};
+use crate::ssa::SsaLocals;
 
 pub(super) struct GVN;
 
@@ -126,31 +128,11 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
         let dominators = body.basic_blocks.dominators().clone();
 
         let mut state = VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls);
-        ssa.for_each_assignment_mut(
-            body.basic_blocks.as_mut_preserves_cfg(),
-            |local, value, location| {
-                let value = match value {
-                    // We do not know anything of this assigned value.
-                    AssignedValue::Arg | AssignedValue::Terminator => None,
-                    // Try to get some insight.
-                    AssignedValue::Rvalue(rvalue) => {
-                        let value = state.simplify_rvalue(rvalue, location);
-                        // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark
-                        // `local` as reusable if we have an exact type match.
-                        if state.local_decls[local].ty != rvalue.ty(state.local_decls, tcx) {
-                            return;
-                        }
-                        value
-                    }
-                };
-                // `next_opaque` is `Some`, so `new_opaque` must return `Some`.
-                let value = value.or_else(|| state.new_opaque()).unwrap();
-                state.assign(local, value);
-            },
-        );
 
-        // Stop creating opaques during replacement as it is useless.
-        state.next_opaque = None;
+        for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) {
+            let opaque = state.new_opaque();
+            state.assign(local, opaque);
+        }
 
         let reverse_postorder = body.basic_blocks.reverse_postorder().to_vec();
         for bb in reverse_postorder {
@@ -250,14 +232,14 @@ struct VnState<'body, 'tcx> {
     locals: IndexVec<Local, Option<VnIndex>>,
     /// Locals that are assigned that value.
     // This vector does not hold all the values of `VnIndex` that we create.
-    // It stops at the largest value created in the first phase of collecting assignments.
     rev_locals: IndexVec<VnIndex, SmallVec<[Local; 1]>>,
     values: FxIndexSet<Value<'tcx>>,
     /// Values evaluated as constants if possible.
     evaluated: IndexVec<VnIndex, Option<OpTy<'tcx>>>,
     /// Counter to generate different values.
-    /// This is an option to stop creating opaques during replacement.
-    next_opaque: Option<usize>,
+    next_opaque: usize,
+    /// Cache the deref values.
+    derefs: Vec<VnIndex>,
     /// Cache the value of the `unsized_locals` features, to avoid fetching it repeatedly in a loop.
     feature_unsized_locals: bool,
     ssa: &'body SsaLocals,
@@ -289,7 +271,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             rev_locals: IndexVec::with_capacity(num_values),
             values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()),
             evaluated: IndexVec::with_capacity(num_values),
-            next_opaque: Some(1),
+            next_opaque: 1,
+            derefs: Vec::new(),
             feature_unsized_locals: tcx.features().unsized_locals(),
             ssa,
             dominators,
@@ -310,32 +293,31 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             let evaluated = self.eval_to_const(index);
             let _index = self.evaluated.push(evaluated);
             debug_assert_eq!(index, _index);
-            // No need to push to `rev_locals` if we finished listing assignments.
-            if self.next_opaque.is_some() {
-                let _index = self.rev_locals.push(SmallVec::new());
-                debug_assert_eq!(index, _index);
-            }
+            let _index = self.rev_locals.push(SmallVec::new());
+            debug_assert_eq!(index, _index);
         }
         index
     }
 
+    fn next_opaque(&mut self) -> usize {
+        let next_opaque = self.next_opaque;
+        self.next_opaque += 1;
+        next_opaque
+    }
+
     /// Create a new `Value` for which we have no information at all, except that it is distinct
     /// from all the others.
     #[instrument(level = "trace", skip(self), ret)]
-    fn new_opaque(&mut self) -> Option<VnIndex> {
-        let next_opaque = self.next_opaque.as_mut()?;
-        let value = Value::Opaque(*next_opaque);
-        *next_opaque += 1;
-        Some(self.insert(value))
+    fn new_opaque(&mut self) -> VnIndex {
+        let value = Value::Opaque(self.next_opaque());
+        self.insert(value)
     }
 
     /// Create a new `Value::Address` distinct from all the others.
     #[instrument(level = "trace", skip(self), ret)]
-    fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> Option<VnIndex> {
-        let next_opaque = self.next_opaque.as_mut()?;
-        let value = Value::Address { place, kind, provenance: *next_opaque };
-        *next_opaque += 1;
-        Some(self.insert(value))
+    fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> VnIndex {
+        let value = Value::Address { place, kind, provenance: self.next_opaque() };
+        self.insert(value)
     }
 
     fn get(&self, index: VnIndex) -> &Value<'tcx> {
@@ -345,6 +327,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
     /// Record that `local` is assigned `value`. `local` must be SSA.
     #[instrument(level = "trace", skip(self))]
     fn assign(&mut self, local: Local, value: VnIndex) {
+        debug_assert!(self.ssa.is_ssa(local));
         self.locals[local] = Some(value);
 
         // Only register the value if its type is `Sized`, as we will emit copies of it.
@@ -355,21 +338,19 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         }
     }
 
-    fn insert_constant(&mut self, value: Const<'tcx>) -> Option<VnIndex> {
+    fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex {
         let disambiguator = if value.is_deterministic() {
             // The constant is deterministic, no need to disambiguate.
             0
         } else {
             // Multiple mentions of this constant will yield different values,
             // so assign a different `disambiguator` to ensure they do not get the same `VnIndex`.
-            let next_opaque = self.next_opaque.as_mut()?;
-            let disambiguator = *next_opaque;
-            *next_opaque += 1;
+            let disambiguator = self.next_opaque();
             // `disambiguator: 0` means deterministic.
             debug_assert_ne!(disambiguator, 0);
             disambiguator
         };
-        Some(self.insert(Value::Constant { value, disambiguator }))
+        self.insert(Value::Constant { value, disambiguator })
     }
 
     fn insert_bool(&mut self, flag: bool) -> VnIndex {
@@ -390,6 +371,19 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         self.insert(Value::Aggregate(AggregateTy::Tuple, VariantIdx::ZERO, values))
     }
 
+    fn insert_deref(&mut self, value: VnIndex) -> VnIndex {
+        let value = self.insert(Value::Projection(value, ProjectionElem::Deref));
+        self.derefs.push(value);
+        value
+    }
+
+    fn invalidate_derefs(&mut self) {
+        for deref in std::mem::take(&mut self.derefs) {
+            let opaque = self.next_opaque();
+            *self.values.get_index_mut2(deref.index()).unwrap() = Value::Opaque(opaque);
+        }
+    }
+
     #[instrument(level = "trace", skip(self), ret)]
     fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
         use Value::*;
@@ -648,15 +642,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         let proj = match proj {
             ProjectionElem::Deref => {
                 let ty = place.ty(self.local_decls, self.tcx).ty;
-                // unsound: https://github.com/rust-lang/rust/issues/130853
-                if self.tcx.sess.opts.unstable_opts.unsound_mir_opts
-                    && let Some(Mutability::Not) = ty.ref_mutability()
+                if let Some(Mutability::Not) = ty.ref_mutability()
                     && let Some(pointee_ty) = ty.builtin_deref(true)
                     && pointee_ty.is_freeze(self.tcx, self.typing_env())
                 {
                     // An immutable borrow `_x` always points to the same value for the
                     // lifetime of the borrow, so we can merge all instances of `*_x`.
-                    ProjectionElem::Deref
+                    return Some(self.insert_deref(value));
                 } else {
                     return None;
                 }
@@ -830,7 +822,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         location: Location,
     ) -> Option<VnIndex> {
         match *operand {
-            Operand::Constant(ref constant) => self.insert_constant(constant.const_),
+            Operand::Constant(ref constant) => Some(self.insert_constant(constant.const_)),
             Operand::Copy(ref mut place) | Operand::Move(ref mut place) => {
                 let value = self.simplify_place_value(place, location)?;
                 if let Some(const_) = self.try_as_constant(value) {
@@ -866,11 +858,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             Rvalue::Aggregate(..) => return self.simplify_aggregate(rvalue, location),
             Rvalue::Ref(_, borrow_kind, ref mut place) => {
                 self.simplify_place_projection(place, location);
-                return self.new_pointer(*place, AddressKind::Ref(borrow_kind));
+                return Some(self.new_pointer(*place, AddressKind::Ref(borrow_kind)));
             }
             Rvalue::RawPtr(mutbl, ref mut place) => {
                 self.simplify_place_projection(place, location);
-                return self.new_pointer(*place, AddressKind::Address(mutbl));
+                return Some(self.new_pointer(*place, AddressKind::Address(mutbl)));
             }
             Rvalue::WrapUnsafeBinder(ref mut op, ty) => {
                 let value = self.simplify_operand(op, location)?;
@@ -1034,7 +1026,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
 
             if is_zst {
                 let ty = rvalue.ty(self.local_decls, tcx);
-                return self.insert_constant(Const::zero_sized(ty));
+                return Some(self.insert_constant(Const::zero_sized(ty)));
             }
         }
 
@@ -1063,11 +1055,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             }
         };
 
-        let fields: Option<Vec<_>> = field_ops
+        let mut fields: Vec<_> = field_ops
             .iter_mut()
-            .map(|op| self.simplify_operand(op, location).or_else(|| self.new_opaque()))
+            .map(|op| self.simplify_operand(op, location).unwrap_or_else(|| self.new_opaque()))
             .collect();
-        let mut fields = fields?;
 
         if let AggregateTy::RawPtr { data_pointer_ty, output_pointer_ty } = &mut ty {
             let mut was_updated = false;
@@ -1107,9 +1098,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             }
         }
 
-        // unsound: https://github.com/rust-lang/rust/issues/132353
-        if tcx.sess.opts.unstable_opts.unsound_mir_opts
-            && let AggregateTy::Def(_, _) = ty
+        if let AggregateTy::Def(_, _) = ty
             && let Some(value) =
                 self.simplify_aggregate_to_copy(rvalue, location, &fields, variant_index)
         {
@@ -1195,7 +1184,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             ) if let ty::Slice(..) = to.builtin_deref(true).unwrap().kind()
                 && let ty::Array(_, len) = from.builtin_deref(true).unwrap().kind() =>
             {
-                return self.insert_constant(Const::Ty(self.tcx.types.usize, *len));
+                return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len)));
             }
             _ => Value::UnaryOp(op, arg_index),
         };
@@ -1391,7 +1380,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         if let CastKind::PointerCoercion(ReifyFnPointer | ClosureFnPointer(_), _) = kind {
             // Each reification of a generic fn may get a different pointer.
             // Do not try to merge them.
-            return self.new_opaque();
+            return Some(self.new_opaque());
         }
 
         let mut was_ever_updated = false;
@@ -1507,7 +1496,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         // Trivial case: we are fetching a statically known length.
         let place_ty = place.ty(self.local_decls, self.tcx).ty;
         if let ty::Array(_, len) = place_ty.kind() {
-            return self.insert_constant(Const::Ty(self.tcx.types.usize, *len));
+            return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len)));
         }
 
         let mut inner = self.simplify_place_value(place, location)?;
@@ -1529,7 +1518,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             && let Some(to) = to.builtin_deref(true)
             && let ty::Slice(..) = to.kind()
         {
-            return self.insert_constant(Const::Ty(self.tcx.types.usize, *len));
+            return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len)));
         }
 
         // Fallback: a symbolic `Len`.
@@ -1739,41 +1728,70 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> {
         self.tcx
     }
 
-    fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, location: Location) {
+    fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
         self.simplify_place_projection(place, location);
+        if context.is_mutating_use() && !place.projection.is_empty() {
+            // Non-local mutation maybe invalidate deref.
+            self.invalidate_derefs();
+        }
+        self.super_place(place, context, location);
     }
 
     fn visit_operand(&mut self, operand: &mut Operand<'tcx>, location: Location) {
         self.simplify_operand(operand, location);
+        self.super_operand(operand, location);
     }
 
     fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, location: Location) {
         if let StatementKind::Assign(box (ref mut lhs, ref mut rvalue)) = stmt.kind {
             self.simplify_place_projection(lhs, location);
 
-            // Do not try to simplify a constant, it's already in canonical shape.
-            if matches!(rvalue, Rvalue::Use(Operand::Constant(_))) {
-                return;
+            let value = self.simplify_rvalue(rvalue, location);
+            let value = if let Some(local) = lhs.as_local()
+                && self.ssa.is_ssa(local)
+                // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark
+                // `local` as reusable if we have an exact type match.
+                && self.local_decls[local].ty == rvalue.ty(self.local_decls, self.tcx)
+            {
+                let value = value.unwrap_or_else(|| self.new_opaque());
+                self.assign(local, value);
+                Some(value)
+            } else {
+                value
+            };
+            if let Some(value) = value {
+                if let Some(const_) = self.try_as_constant(value) {
+                    *rvalue = Rvalue::Use(Operand::Constant(Box::new(const_)));
+                } else if let Some(local) = self.try_as_local(value, location)
+                    && *rvalue != Rvalue::Use(Operand::Move(local.into()))
+                {
+                    *rvalue = Rvalue::Use(Operand::Copy(local.into()));
+                    self.reused_locals.insert(local);
+                }
             }
+        }
+        self.super_statement(stmt, location);
+    }
 
-            let value = lhs
-                .as_local()
-                .and_then(|local| self.locals[local])
-                .or_else(|| self.simplify_rvalue(rvalue, location));
-            let Some(value) = value else { return };
-
-            if let Some(const_) = self.try_as_constant(value) {
-                *rvalue = Rvalue::Use(Operand::Constant(Box::new(const_)));
-            } else if let Some(local) = self.try_as_local(value, location)
-                && *rvalue != Rvalue::Use(Operand::Move(local.into()))
+    fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
+        if let Terminator { kind: TerminatorKind::Call { destination, .. }, .. } = terminator {
+            if let Some(local) = destination.as_local()
+                && self.ssa.is_ssa(local)
             {
-                *rvalue = Rvalue::Use(Operand::Copy(local.into()));
-                self.reused_locals.insert(local);
+                let opaque = self.new_opaque();
+                self.assign(local, opaque);
             }
-
-            return;
         }
-        self.super_statement(stmt, location);
+        // Function calls and ASM may invalidate (nested) derefs. We must handle them carefully.
+        // Currently, only preserving derefs for trivial terminators like SwitchInt and Goto.
+        let safe_to_preserve_derefs = matches!(
+            terminator.kind,
+            TerminatorKind::SwitchInt { .. } | TerminatorKind::Goto { .. }
+        );
+        if !safe_to_preserve_derefs {
+            self.invalidate_derefs();
+        }
+        self.super_terminator(terminator, location);
     }
 }
 
diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs
index da346dfc48c..2eff6b31372 100644
--- a/compiler/rustc_mir_transform/src/instsimplify.rs
+++ b/compiler/rustc_mir_transform/src/instsimplify.rs
@@ -78,20 +78,20 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
     /// GVN can also do this optimization, but GVN is only run at mir-opt-level 2 so having this in
     /// InstSimplify helps unoptimized builds.
     fn simplify_repeated_aggregate(&self, rvalue: &mut Rvalue<'tcx>) {
-        let Rvalue::Aggregate(box AggregateKind::Array(_), fields) = rvalue else {
+        let Rvalue::Aggregate(box AggregateKind::Array(_), fields) = &*rvalue else {
             return;
         };
         if fields.len() < 5 {
             return;
         }
-        let first = &fields[rustc_abi::FieldIdx::ZERO];
+        let (first, rest) = fields[..].split_first().unwrap();
         let Operand::Constant(first) = first else {
             return;
         };
         let Ok(first_val) = first.const_.eval(self.tcx, self.typing_env, first.span) else {
             return;
         };
-        if fields.iter().all(|field| {
+        if rest.iter().all(|field| {
             let Operand::Constant(field) = field else {
                 return false;
             };
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 205d388f4fb..6429d3f67ec 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -12,6 +12,7 @@
 #![feature(map_try_insert)]
 #![feature(never_type)]
 #![feature(try_blocks)]
+#![feature(vec_deque_pop_if)]
 #![feature(yeet_expr)]
 // tidy-alphabetical-end
 
@@ -528,7 +529,7 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> &
         | DefKind::Static { .. }
         | DefKind::Const
         | DefKind::AssocConst => {
-            if let Err(guar) = tcx.check_well_formed(root.expect_local()) {
+            if let Err(guar) = tcx.ensure_ok().check_well_formed(root.expect_local()) {
                 body.tainted_by_errors = Some(guar);
             }
         }
diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs
index 3d512fb064e..edd0cabca49 100644
--- a/compiler/rustc_mir_transform/src/ssa.rs
+++ b/compiler/rustc_mir_transform/src/ssa.rs
@@ -32,12 +32,6 @@ pub(super) struct SsaLocals {
     borrowed_locals: DenseBitSet<Local>,
 }
 
-pub(super) enum AssignedValue<'a, 'tcx> {
-    Arg,
-    Rvalue(&'a mut Rvalue<'tcx>),
-    Terminator,
-}
-
 impl SsaLocals {
     pub(super) fn new<'tcx>(
         tcx: TyCtxt<'tcx>,
@@ -152,38 +146,6 @@ impl SsaLocals {
         })
     }
 
-    pub(super) fn for_each_assignment_mut<'tcx>(
-        &self,
-        basic_blocks: &mut IndexSlice<BasicBlock, BasicBlockData<'tcx>>,
-        mut f: impl FnMut(Local, AssignedValue<'_, 'tcx>, Location),
-    ) {
-        for &local in &self.assignment_order {
-            match self.assignments[local] {
-                Set1::One(DefLocation::Argument) => f(
-                    local,
-                    AssignedValue::Arg,
-                    Location { block: START_BLOCK, statement_index: 0 },
-                ),
-                Set1::One(DefLocation::Assignment(loc)) => {
-                    let bb = &mut basic_blocks[loc.block];
-                    // `loc` must point to a direct assignment to `local`.
-                    let stmt = &mut bb.statements[loc.statement_index];
-                    let StatementKind::Assign(box (target, ref mut rvalue)) = stmt.kind else {
-                        bug!()
-                    };
-                    assert_eq!(target.as_local(), Some(local));
-                    f(local, AssignedValue::Rvalue(rvalue), loc)
-                }
-                Set1::One(DefLocation::CallReturn { call, .. }) => {
-                    let bb = &mut basic_blocks[call];
-                    let loc = Location { block: call, statement_index: bb.statements.len() };
-                    f(local, AssignedValue::Terminator, loc)
-                }
-                _ => {}
-            }
-        }
-    }
-
     /// Compute the equivalence classes for locals, based on copy statements.
     ///
     /// The returned vector maps each local to the one it copies. In the following case:
diff --git a/compiler/rustc_monomorphize/messages.ftl b/compiler/rustc_monomorphize/messages.ftl
index aae2d79c161..6b6653e7de0 100644
--- a/compiler/rustc_monomorphize/messages.ftl
+++ b/compiler/rustc_monomorphize/messages.ftl
@@ -48,7 +48,7 @@ monomorphize_large_assignments =
     .note = The current maximum size is {$limit}, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
 
 monomorphize_no_optimized_mir =
-    missing optimized MIR for an item in the crate `{$crate_name}`
+    missing optimized MIR for `{$instance}` in the crate `{$crate_name}`
     .note = missing optimized MIR for this item (was the crate `{$crate_name}` compiled with `--emit=metadata`?)
 
 monomorphize_recursion_limit =
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 2a1b20ba48b..6e676ac6b8d 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -989,6 +989,7 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) ->
         tcx.dcx().emit_fatal(NoOptimizedMir {
             span: tcx.def_span(def_id),
             crate_name: tcx.crate_name(def_id.krate),
+            instance: instance.to_string(),
         });
     }
 
diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs
index dffa372279f..adfe096f0cd 100644
--- a/compiler/rustc_monomorphize/src/errors.rs
+++ b/compiler/rustc_monomorphize/src/errors.rs
@@ -24,6 +24,7 @@ pub(crate) struct NoOptimizedMir {
     #[note]
     pub span: Span,
     pub crate_name: Symbol,
+    pub instance: String,
 }
 
 #[derive(LintDiagnostic)]
diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs
index 259b39e2b9e..c4b6b18c45d 100644
--- a/compiler/rustc_next_trait_solver/src/delegate.rs
+++ b/compiler/rustc_next_trait_solver/src/delegate.rs
@@ -62,14 +62,12 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
         universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
     ) -> <Self::Interner as Interner>::GenericArg;
 
-    // FIXME: Can we implement this in terms of `add` and `inject`?
-    fn insert_hidden_type(
+    fn register_hidden_type_in_storage(
         &self,
         opaque_type_key: ty::OpaqueTypeKey<Self::Interner>,
-        param_env: <Self::Interner as Interner>::ParamEnv,
         hidden_ty: <Self::Interner as Interner>::Ty,
-        goals: &mut Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
-    ) -> Result<(), NoSolution>;
+        span: <Self::Interner as Interner>::Span,
+    ) -> Option<<Self::Interner as Interner>::Ty>;
 
     fn add_item_bounds_for_hidden_type(
         &self,
@@ -79,14 +77,6 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
         hidden_ty: <Self::Interner as Interner>::Ty,
         goals: &mut Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
     );
-
-    fn inject_new_hidden_type_unchecked(
-        &self,
-        key: ty::OpaqueTypeKey<Self::Interner>,
-        hidden_ty: <Self::Interner as Interner>::Ty,
-        span: <Self::Interner as Interner>::Span,
-    );
-
     fn reset_opaque_types(&self);
 
     fn fetch_eligible_assoc_item(
diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
index a5142de2d39..c2fb592c3f3 100644
--- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
@@ -37,12 +37,16 @@ where
         | ty::Never
         | ty::Char => Ok(ty::Binder::dummy(vec![])),
 
+        // This branch is only for `experimental_default_bounds`.
+        // Other foreign types were rejected earlier in
+        // `disqualify_auto_trait_candidate_due_to_possible_impl`.
+        ty::Foreign(..) => Ok(ty::Binder::dummy(vec![])),
+
         // Treat `str` like it's defined as `struct str([u8]);`
         ty::Str => Ok(ty::Binder::dummy(vec![Ty::new_slice(cx, Ty::new_u8(cx))])),
 
         ty::Dynamic(..)
         | ty::Param(..)
-        | ty::Foreign(..)
         | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
         | ty::Placeholder(..)
         | ty::Bound(..)
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
index ac6b521f665..4edc293ad80 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
@@ -425,7 +425,8 @@ where
 
     fn register_new_opaque_types(&mut self, opaque_types: &[(ty::OpaqueTypeKey<I>, I::Ty)]) {
         for &(key, ty) in opaque_types {
-            self.delegate.inject_new_hidden_type_unchecked(key, ty, self.origin_span);
+            let prev = self.delegate.register_hidden_type_in_storage(key, ty, self.origin_span);
+            assert_eq!(prev, None);
         }
     }
 }
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 7ef36d0e9ae..148ba02252d 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
@@ -387,7 +387,8 @@ where
         };
 
         for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
-            ecx.delegate.inject_new_hidden_type_unchecked(key, ty, ecx.origin_span);
+            let prev = ecx.delegate.register_hidden_type_in_storage(key, ty, ecx.origin_span);
+            assert_eq!(prev, None);
         }
 
         if !ecx.nested_goals.is_empty() {
@@ -1070,16 +1071,12 @@ where
         self.delegate.fetch_eligible_assoc_item(goal_trait_ref, trait_assoc_def_id, impl_def_id)
     }
 
-    pub(super) fn insert_hidden_type(
+    pub(super) fn register_hidden_type_in_storage(
         &mut self,
         opaque_type_key: ty::OpaqueTypeKey<I>,
-        param_env: I::ParamEnv,
         hidden_ty: I::Ty,
-    ) -> Result<(), NoSolution> {
-        let mut goals = Vec::new();
-        self.delegate.insert_hidden_type(opaque_type_key, param_env, hidden_ty, &mut goals)?;
-        self.add_goals(GoalSource::Misc, goals);
-        Ok(())
+    ) -> Option<I::Ty> {
+        self.delegate.register_hidden_type_in_storage(opaque_type_key, hidden_ty, self.origin_span)
     }
 
     pub(super) fn add_item_bounds_for_hidden_type(
diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs
index 199f0c7512e..7641e9a16ee 100644
--- a/compiler/rustc_next_trait_solver/src/solve/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs
@@ -330,6 +330,7 @@ where
             // During analysis, opaques are rigid unless they may be defined by
             // the current body.
             TypingMode::Analysis { defining_opaque_types: non_rigid_opaques }
+            | TypingMode::Borrowck { defining_opaque_types: non_rigid_opaques }
             | TypingMode::PostBorrowckAnalysis { defined_opaque_types: non_rigid_opaques } => {
                 !def_id.as_local().is_some_and(|def_id| non_rigid_opaques.contains(&def_id))
             }
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
index 817dffa127b..aa89e77bb6f 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
@@ -86,8 +86,44 @@ where
                 }
 
                 // Otherwise, define a new opaque type
-                // FIXME: should we use `inject_hidden_type_unchecked` here?
-                self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?;
+                let prev = self.register_hidden_type_in_storage(opaque_type_key, expected);
+                assert_eq!(prev, None);
+                self.add_item_bounds_for_hidden_type(
+                    def_id.into(),
+                    opaque_ty.args,
+                    goal.param_env,
+                    expected,
+                );
+                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            }
+            // Very similar to `TypingMode::Analysis` with some notably differences:
+            // - we accept opaque types even if they have non-universal arguments
+            // - we do a structural lookup instead of semantically unifying regions
+            // - the hidden type starts out as the type from HIR typeck with fresh region
+            //   variables instead of a fully unconstrained inference variable
+            TypingMode::Borrowck { defining_opaque_types } => {
+                let Some(def_id) = opaque_ty
+                    .def_id
+                    .as_local()
+                    .filter(|&def_id| defining_opaque_types.contains(&def_id))
+                else {
+                    self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
+                    return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
+                };
+
+                let opaque_type_key = ty::OpaqueTypeKey { def_id, args: opaque_ty.args };
+                let actual = self
+                    .register_hidden_type_in_storage(opaque_type_key, expected)
+                    .unwrap_or_else(|| {
+                        let actual =
+                            cx.type_of_opaque_hir_typeck(def_id).instantiate(cx, opaque_ty.args);
+                        let actual = fold_regions(cx, actual, |re, _dbi| match re.kind() {
+                            ty::ReErased => self.next_region_var(),
+                            _ => re,
+                        });
+                        actual
+                    });
+                self.eq(goal.param_env, expected, actual)?;
                 self.add_item_bounds_for_hidden_type(
                     def_id.into(),
                     opaque_ty.args,
diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs
index eba496fa226..ecffbbff7a2 100644
--- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs
@@ -62,6 +62,7 @@ where
                     response_no_constraints(cx, input, Certainty::overflow(false))
                 }
                 TypingMode::Analysis { .. }
+                | TypingMode::Borrowck { .. }
                 | TypingMode::PostBorrowckAnalysis { .. }
                 | TypingMode::PostAnalysis => Err(NoSolution),
             },
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 b72f776e5cb..d42c9980f46 100644
--- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -72,6 +72,7 @@ where
             (ty::ImplPolarity::Reservation, _) => match ecx.typing_mode() {
                 TypingMode::Coherence => Certainty::AMBIGUOUS,
                 TypingMode::Analysis { .. }
+                | TypingMode::Borrowck { .. }
                 | TypingMode::PostBorrowckAnalysis { .. }
                 | TypingMode::PostAnalysis => return Err(NoSolution),
             },
@@ -1086,6 +1087,25 @@ where
         goal: Goal<I, TraitPredicate<I>>,
     ) -> Option<Result<Candidate<I>, NoSolution>> {
         let self_ty = goal.predicate.self_ty();
+        let check_impls = || {
+            let mut disqualifying_impl = None;
+            self.cx().for_each_relevant_impl(
+                goal.predicate.def_id(),
+                goal.predicate.self_ty(),
+                |impl_def_id| {
+                    disqualifying_impl = Some(impl_def_id);
+                },
+            );
+            if let Some(def_id) = disqualifying_impl {
+                trace!(?def_id, ?goal, "disqualified auto-trait implementation");
+                // No need to actually consider the candidate here,
+                // since we do that in `consider_impl_candidate`.
+                return Some(Err(NoSolution));
+            } else {
+                None
+            }
+        };
+
         match self_ty.kind() {
             // Stall int and float vars until they are resolved to a concrete
             // numerical type. That's because the check for impls below treats
@@ -1096,6 +1116,10 @@ where
                 Some(self.forced_ambiguity(MaybeCause::Ambiguity))
             }
 
+            // Backward compatibility for default auto traits.
+            // Test: ui/traits/default_auto_traits/extern-types.rs
+            ty::Foreign(..) if self.cx().is_default_trait(goal.predicate.def_id()) => check_impls(),
+
             // These types cannot be structurally decomposed into constituent
             // types, and therefore have no built-in auto impl.
             ty::Dynamic(..)
@@ -1156,24 +1180,7 @@ where
             | ty::Never
             | ty::Tuple(_)
             | ty::Adt(_, _)
-            | ty::UnsafeBinder(_) => {
-                let mut disqualifying_impl = None;
-                self.cx().for_each_relevant_impl(
-                    goal.predicate.def_id(),
-                    goal.predicate.self_ty(),
-                    |impl_def_id| {
-                        disqualifying_impl = Some(impl_def_id);
-                    },
-                );
-                if let Some(def_id) = disqualifying_impl {
-                    trace!(?def_id, ?goal, "disqualified auto-trait implementation");
-                    // No need to actually consider the candidate here,
-                    // since we do that in `consider_impl_candidate`.
-                    return Some(Err(NoSolution));
-                } else {
-                    None
-                }
-            }
+            | ty::UnsafeBinder(_) => check_impls(),
             ty::Error(_) => None,
         }
     }
@@ -1294,7 +1301,6 @@ where
                 .filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
                 .map(|c| c.result)
                 .collect();
-
             return if let Some(response) = self.try_merge_responses(&where_bounds) {
                 Ok((response, Some(TraitGoalProvenVia::ParamEnv)))
             } else {
@@ -1315,9 +1321,18 @@ where
             };
         }
 
+        // If there are *only* global where bounds, then make sure to return that this
+        // is still reported as being proven-via the param-env so that rigid projections
+        // operate correctly.
+        let proven_via =
+            if candidates.iter().all(|c| matches!(c.source, CandidateSource::ParamEnv(_))) {
+                TraitGoalProvenVia::ParamEnv
+            } else {
+                TraitGoalProvenVia::Misc
+            };
         let all_candidates: Vec<_> = candidates.into_iter().map(|c| c.result).collect();
         if let Some(response) = self.try_merge_responses(&all_candidates) {
-            Ok((response, Some(TraitGoalProvenVia::Misc)))
+            Ok((response, Some(proven_via)))
         } else {
             self.flounder(&all_candidates).map(|r| (r, None))
         }
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 3253222b8f2..93fa89b68b9 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -838,8 +838,6 @@ parse_unexpected_expr_in_pat_const_sugg = consider extracting the expression int
 
 parse_unexpected_expr_in_pat_create_guard_sugg = consider moving the expression to a match arm guard
 
-parse_unexpected_expr_in_pat_inline_const_sugg = consider wrapping the expression in an inline `const` (requires `{"#"}![feature(inline_const_pat)]`)
-
 parse_unexpected_expr_in_pat_update_guard_sugg = consider moving the expression to the match arm guard
 
 parse_unexpected_if_with_if = unexpected `if` in the condition expression
@@ -860,7 +858,7 @@ parse_unexpected_parentheses_in_match_arm_pattern = unexpected parentheses surro
 parse_unexpected_self_in_generic_parameters = unexpected keyword `Self` in generic parameters
     .note = you cannot use `Self` as a generic parameter because it is reserved for associated items
 
-parse_unexpected_token_after_dot = unexpected token: `{$actual}`
+parse_unexpected_token_after_dot = unexpected token: {$actual}
 
 parse_unexpected_token_after_label = expected `while`, `for`, `loop` or `{"{"}` after a label
     .suggestion_remove_label = consider removing the label
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index f813c3380fc..dfdef018bc3 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -810,16 +810,16 @@ pub(crate) enum WrapInParentheses {
 
 #[derive(Diagnostic)]
 #[diag(parse_array_brackets_instead_of_braces)]
-pub(crate) struct ArrayBracketsInsteadOfSpaces {
+pub(crate) struct ArrayBracketsInsteadOfBraces {
     #[primary_span]
     pub span: Span,
     #[subdiagnostic]
-    pub sub: ArrayBracketsInsteadOfSpacesSugg,
+    pub sub: ArrayBracketsInsteadOfBracesSugg,
 }
 
 #[derive(Subdiagnostic)]
 #[multipart_suggestion(parse_suggestion, applicability = "maybe-incorrect")]
-pub(crate) struct ArrayBracketsInsteadOfSpacesSugg {
+pub(crate) struct ArrayBracketsInsteadOfBracesSugg {
     #[suggestion_part(code = "[")]
     pub left: Span,
     #[suggestion_part(code = "]")]
@@ -1696,10 +1696,10 @@ pub(crate) struct SelfArgumentPointer {
 
 #[derive(Diagnostic)]
 #[diag(parse_unexpected_token_after_dot)]
-pub(crate) struct UnexpectedTokenAfterDot<'a> {
+pub(crate) struct UnexpectedTokenAfterDot {
     #[primary_span]
     pub span: Span,
-    pub actual: Cow<'a, str>,
+    pub actual: String,
 }
 
 #[derive(Diagnostic)]
@@ -2769,17 +2769,6 @@ pub(crate) enum UnexpectedExpressionInPatternSugg {
         /// The statement's block's indentation.
         indentation: String,
     },
-
-    #[multipart_suggestion(
-        parse_unexpected_expr_in_pat_inline_const_sugg,
-        applicability = "maybe-incorrect"
-    )]
-    InlineConst {
-        #[suggestion_part(code = "const {{ ")]
-        start_span: Span,
-        #[suggestion_part(code = " }}")]
-        end_span: Span,
-    },
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 92e83577f1b..841d967d934 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -4,22 +4,21 @@ use core::mem;
 use core::ops::{Bound, ControlFlow};
 
 use ast::mut_visit::{self, MutVisitor};
-use ast::token::{IdentIsRaw, MetaVarKind};
+use ast::token::IdentIsRaw;
 use ast::{CoroutineKind, ForLoopKind, GenBlockKind, MatchKind, Pat, Path, PathSegment, Recovered};
 use rustc_ast::ptr::P;
-use rustc_ast::token::{self, Delimiter, Token, TokenKind};
+use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, Token, TokenKind};
 use rustc_ast::tokenstream::TokenTree;
 use rustc_ast::util::case::Case;
 use rustc_ast::util::classify;
 use rustc_ast::util::parser::{AssocOp, ExprPrecedence, Fixity, prec_let_scrutinee_needs_par};
 use rustc_ast::visit::{Visitor, walk_expr};
 use rustc_ast::{
-    self as ast, AnonConst, Arm, AttrStyle, AttrVec, BinOp, BinOpKind, BlockCheckMode, CaptureBy,
-    ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl, FnRetTy, Label, MacCall,
-    MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind,
-    YieldKind,
+    self as ast, AnonConst, Arm, AssignOp, AssignOpKind, AttrStyle, AttrVec, BinOp, BinOpKind,
+    BlockCheckMode, CaptureBy, ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl,
+    FnRetTy, Label, MacCall, MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, TyKind,
+    UnOp, UnsafeBinderCastKind, YieldKind,
 };
-use rustc_ast_pretty::pprust;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::{Applicability, Diag, PResult, StashKey, Subdiagnostic};
 use rustc_lexer::unescape::unescape_char;
@@ -360,7 +359,7 @@ impl<'a> Parser<'a> {
             (
                 Some(
                     AssocOp::Binary(BinOpKind::Shr | BinOpKind::Gt | BinOpKind::Ge)
-                    | AssocOp::AssignOp(BinOpKind::Shr),
+                    | AssocOp::AssignOp(AssignOpKind::ShrAssign),
                 ),
                 _,
             ) if self.restrictions.contains(Restrictions::CONST_EXPR) => {
@@ -605,7 +604,7 @@ impl<'a> Parser<'a> {
             // can't continue an expression after an ident
             token::Ident(name, is_raw) => token::ident_can_begin_expr(name, t.span, is_raw),
             token::Literal(..) | token::Pound => true,
-            _ => t.is_whole_expr(),
+            _ => t.is_metavar_expr(),
         };
         self.token.is_ident_named(sym::not) && self.look_ahead(1, token_cannot_continue_expr)
     }
@@ -641,6 +640,13 @@ impl<'a> Parser<'a> {
             TokenKind::NtIdent(..) | TokenKind::NtLifetime(..) | TokenKind::Interpolated(..) => {
                 self.prev_token.span
             }
+            TokenKind::CloseDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(_))) => {
+                // `expr.span` is the interpolated span, because invisible open
+                // and close delims both get marked with the same span, one
+                // that covers the entire thing between them. (See
+                // `rustc_expand::mbe::transcribe::transcribe`.)
+                self.prev_token.span
+            }
             _ => expr.span,
         }
     }
@@ -979,12 +985,30 @@ impl<'a> Parser<'a> {
     }
 
     fn error_unexpected_after_dot(&self) {
-        let actual = pprust::token_to_string(&self.token);
+        let actual = super::token_descr(&self.token);
         let span = self.token.span;
         let sm = self.psess.source_map();
         let (span, actual) = match (&self.token.kind, self.subparser_name) {
-            (token::Eof, Some(_)) if let Ok(actual) = sm.span_to_snippet(sm.next_point(span)) => {
-                (span.shrink_to_hi(), actual.into())
+            (token::Eof, Some(_)) if let Ok(snippet) = sm.span_to_snippet(sm.next_point(span)) => {
+                (span.shrink_to_hi(), format!("`{}`", snippet))
+            }
+            (token::CloseDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(_))), _) => {
+                // No need to report an error. This case will only occur when parsing a pasted
+                // metavariable, and we should have emitted an error when parsing the macro call in
+                // the first place. E.g. in this code:
+                // ```
+                // macro_rules! m { ($e:expr) => { $e }; }
+                //
+                // fn main() {
+                //     let f = 1;
+                //     m!(f.);
+                // }
+                // ```
+                // we'll get an error "unexpected token: `)` when parsing the `m!(f.)`, so we don't
+                // want to issue a second error when parsing the expansion `«f.»` (where `«`/`»`
+                // represent the invisible delimiters).
+                self.dcx().span_delayed_bug(span, "bad dot expr in metavariable");
+                return;
             }
             _ => (span, actual),
         };
@@ -1294,7 +1318,7 @@ impl<'a> Parser<'a> {
 
     /// Assuming we have just parsed `.`, continue parsing into an expression.
     fn parse_dot_suffix(&mut self, self_arg: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> {
-        if self.token.uninterpolated_span().at_least_rust_2018() && self.eat_keyword(exp!(Await)) {
+        if self.token_uninterpolated_span().at_least_rust_2018() && self.eat_keyword(exp!(Await)) {
             return Ok(self.mk_await_expr(self_arg, lo));
         }
 
@@ -1364,17 +1388,33 @@ impl<'a> Parser<'a> {
         let span = self.token.span;
         if let token::Interpolated(nt) = &self.token.kind {
             match &**nt {
-                token::NtExpr(e) | token::NtLiteral(e) => {
-                    let e = e.clone();
-                    self.bump();
-                    return Ok(e);
-                }
                 token::NtBlock(block) => {
                     let block = block.clone();
                     self.bump();
                     return Ok(self.mk_expr(self.prev_token.span, ExprKind::Block(block, None)));
                 }
             };
+        } else if let Some(expr) = self.eat_metavar_seq_with_matcher(
+            |mv_kind| matches!(mv_kind, MetaVarKind::Expr { .. }),
+            |this| {
+                // Force collection (as opposed to just `parse_expr`) is required to avoid the
+                // attribute duplication seen in #138478.
+                let expr = this.parse_expr_force_collect();
+                // FIXME(nnethercote) Sometimes with expressions we get a trailing comma, possibly
+                // related to the FIXME in `collect_tokens_for_expr`. Examples are the multi-line
+                // `assert_eq!` calls involving arguments annotated with `#[rustfmt::skip]` in
+                // `compiler/rustc_index/src/bit_set/tests.rs`.
+                if this.token.kind == token::Comma {
+                    this.bump();
+                }
+                expr
+            },
+        ) {
+            return Ok(expr);
+        } else if let Some(lit) =
+            self.eat_metavar_seq(MetaVarKind::Literal, |this| this.parse_literal_maybe_minus())
+        {
+            return Ok(lit);
         } else if let Some(path) = self.eat_metavar_seq(MetaVarKind::Path, |this| {
             this.collect_tokens_no_attrs(|this| this.parse_path(PathStyle::Type))
         }) {
@@ -1471,9 +1511,9 @@ impl<'a> Parser<'a> {
                 this.parse_expr_let(restrictions)
             } else if this.eat_keyword(exp!(Underscore)) {
                 Ok(this.mk_expr(this.prev_token.span, ExprKind::Underscore))
-            } else if this.token.uninterpolated_span().at_least_rust_2018() {
+            } else if this.token_uninterpolated_span().at_least_rust_2018() {
                 // `Span::at_least_rust_2018()` is somewhat expensive; don't get it repeatedly.
-                if this.token.uninterpolated_span().at_least_rust_2024()
+                if this.token_uninterpolated_span().at_least_rust_2024()
                     // check for `gen {}` and `gen move {}`
                     // or `async gen {}` and `async gen move {}`
                     && (this.is_gen_block(kw::Gen, 0)
@@ -2062,87 +2102,107 @@ impl<'a> Parser<'a> {
             .or_else(|()| self.handle_missing_lit(Parser::mk_meta_item_lit_char))
     }
 
-    fn recover_after_dot(&mut self) -> Option<Token> {
-        let mut recovered = None;
+    fn recover_after_dot(&mut self) {
         if self.token == token::Dot {
             // Attempt to recover `.4` as `0.4`. We don't currently have any syntax where
             // dot would follow an optional literal, so we do this unconditionally.
-            recovered = self.look_ahead(1, |next_token| {
+            let recovered = self.look_ahead(1, |next_token| {
+                // If it's an integer that looks like a float, then recover as such.
+                //
+                // We will never encounter the exponent part of a floating
+                // point literal here, since there's no use of the exponent
+                // syntax that also constitutes a valid integer, so we need
+                // not check for that.
                 if let token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) =
                     next_token.kind
+                    && suffix.is_none_or(|s| s == sym::f32 || s == sym::f64)
+                    && symbol.as_str().chars().all(|c| c.is_numeric() || c == '_')
+                    && self.token.span.hi() == next_token.span.lo()
                 {
-                    // If this integer looks like a float, then recover as such.
-                    //
-                    // We will never encounter the exponent part of a floating
-                    // point literal here, since there's no use of the exponent
-                    // syntax that also constitutes a valid integer, so we need
-                    // not check for that.
-                    if suffix.is_none_or(|s| s == sym::f32 || s == sym::f64)
-                        && symbol.as_str().chars().all(|c| c.is_numeric() || c == '_')
-                        && self.token.span.hi() == next_token.span.lo()
-                    {
-                        let s = String::from("0.") + symbol.as_str();
-                        let kind = TokenKind::lit(token::Float, Symbol::intern(&s), suffix);
-                        return Some(Token::new(kind, self.token.span.to(next_token.span)));
-                    }
+                    let s = String::from("0.") + symbol.as_str();
+                    let kind = TokenKind::lit(token::Float, Symbol::intern(&s), suffix);
+                    Some(Token::new(kind, self.token.span.to(next_token.span)))
+                } else {
+                    None
                 }
-                None
             });
-            if let Some(token) = &recovered {
-                self.bump();
+            if let Some(recovered) = recovered {
                 self.dcx().emit_err(errors::FloatLiteralRequiresIntegerPart {
-                    span: token.span,
-                    suggestion: token.span.shrink_to_lo(),
+                    span: recovered.span,
+                    suggestion: recovered.span.shrink_to_lo(),
                 });
+                self.bump();
+                self.token = recovered;
             }
         }
+    }
 
-        recovered
+    /// Keep this in sync with `Token::can_begin_literal_maybe_minus` and
+    /// `Lit::from_token` (excluding unary negation).
+    fn eat_token_lit(&mut self) -> Option<token::Lit> {
+        match self.token.uninterpolate().kind {
+            token::Ident(name, IdentIsRaw::No) if name.is_bool_lit() => {
+                self.bump();
+                Some(token::Lit::new(token::Bool, name, None))
+            }
+            token::Literal(token_lit) => {
+                self.bump();
+                Some(token_lit)
+            }
+            token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
+                MetaVarKind::Literal,
+            ))) => {
+                let lit = self
+                    .eat_metavar_seq(MetaVarKind::Literal, |this| this.parse_literal_maybe_minus())
+                    .expect("metavar seq literal");
+                let ast::ExprKind::Lit(token_lit) = lit.kind else {
+                    panic!("didn't reparse a literal");
+                };
+                Some(token_lit)
+            }
+            token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
+                mv_kind @ MetaVarKind::Expr { can_begin_literal_maybe_minus: true, .. },
+            ))) => {
+                let expr = self
+                    .eat_metavar_seq(mv_kind, |this| this.parse_expr())
+                    .expect("metavar seq expr");
+                let ast::ExprKind::Lit(token_lit) = expr.kind else {
+                    panic!("didn't reparse an expr");
+                };
+                Some(token_lit)
+            }
+            _ => None,
+        }
     }
 
     /// Matches `lit = true | false | token_lit`.
     /// Returns `None` if the next token is not a literal.
-    pub(super) fn parse_opt_token_lit(&mut self) -> Option<(token::Lit, Span)> {
-        let recovered = self.recover_after_dot();
-        let token = recovered.as_ref().unwrap_or(&self.token);
-        let span = token.span;
-
-        token::Lit::from_token(token).map(|token_lit| {
-            self.bump();
-            (token_lit, span)
-        })
+    fn parse_opt_token_lit(&mut self) -> Option<(token::Lit, Span)> {
+        self.recover_after_dot();
+        let span = self.token.span;
+        self.eat_token_lit().map(|token_lit| (token_lit, span))
     }
 
     /// Matches `lit = true | false | token_lit`.
     /// Returns `None` if the next token is not a literal.
-    pub(super) fn parse_opt_meta_item_lit(&mut self) -> Option<MetaItemLit> {
-        let recovered = self.recover_after_dot();
-        let token = recovered.as_ref().unwrap_or(&self.token);
-        match token::Lit::from_token(token) {
-            Some(lit) => {
-                match MetaItemLit::from_token_lit(lit, token.span) {
-                    Ok(lit) => {
-                        self.bump();
-                        Some(lit)
-                    }
-                    Err(err) => {
-                        let span = token.uninterpolated_span();
-                        self.bump();
-                        let guar = report_lit_error(self.psess, err, lit, span);
-                        // Pack possible quotes and prefixes from the original literal into
-                        // the error literal's symbol so they can be pretty-printed faithfully.
-                        let suffixless_lit = token::Lit::new(lit.kind, lit.symbol, None);
-                        let symbol = Symbol::intern(&suffixless_lit.to_string());
-                        let lit = token::Lit::new(token::Err(guar), symbol, lit.suffix);
-                        Some(
-                            MetaItemLit::from_token_lit(lit, span)
-                                .unwrap_or_else(|_| unreachable!()),
-                        )
-                    }
+    fn parse_opt_meta_item_lit(&mut self) -> Option<MetaItemLit> {
+        self.recover_after_dot();
+        let span = self.token.span;
+        let uninterpolated_span = self.token_uninterpolated_span();
+        self.eat_token_lit().map(|token_lit| {
+            match MetaItemLit::from_token_lit(token_lit, span) {
+                Ok(lit) => lit,
+                Err(err) => {
+                    let guar = report_lit_error(&self.psess, err, token_lit, uninterpolated_span);
+                    // Pack possible quotes and prefixes from the original literal into
+                    // the error literal's symbol so they can be pretty-printed faithfully.
+                    let suffixless_lit = token::Lit::new(token_lit.kind, token_lit.symbol, None);
+                    let symbol = Symbol::intern(&suffixless_lit.to_string());
+                    let token_lit = token::Lit::new(token::Err(guar), symbol, token_lit.suffix);
+                    MetaItemLit::from_token_lit(token_lit, uninterpolated_span).unwrap()
                 }
             }
-            None => None,
-        }
+        })
     }
 
     pub(super) fn expect_no_tuple_index_suffix(&self, span: Span, suffix: Symbol) {
@@ -2166,9 +2226,10 @@ impl<'a> Parser<'a> {
     /// Matches `'-' lit | lit` (cf. `ast_validation::AstValidator::check_expr_within_pat`).
     /// Keep this in sync with `Token::can_begin_literal_maybe_minus`.
     pub fn parse_literal_maybe_minus(&mut self) -> PResult<'a, P<Expr>> {
-        if let token::Interpolated(nt) = &self.token.kind {
-            match &**nt {
-                // FIXME(nnethercote) The `NtExpr` case should only match if
+        if let Some(expr) = self.eat_metavar_seq_with_matcher(
+            |mv_kind| matches!(mv_kind, MetaVarKind::Expr { .. }),
+            |this| {
+                // FIXME(nnethercote) The `expr` case should only match if
                 // `e` is an `ExprKind::Lit` or an `ExprKind::Unary` containing
                 // an `UnOp::Neg` and an `ExprKind::Lit`, like how
                 // `can_begin_literal_maybe_minus` works. But this method has
@@ -2178,13 +2239,14 @@ impl<'a> Parser<'a> {
                 // `ExprKind::Path` must be accepted when parsing range
                 // patterns. That requires some care. So for now, we continue
                 // being less strict here than we should be.
-                token::NtExpr(e) | token::NtLiteral(e) => {
-                    let e = e.clone();
-                    self.bump();
-                    return Ok(e);
-                }
-                _ => {}
-            };
+                this.parse_expr()
+            },
+        ) {
+            return Ok(expr);
+        } else if let Some(lit) =
+            self.eat_metavar_seq(MetaVarKind::Literal, |this| this.parse_literal_maybe_minus())
+        {
+            return Ok(lit);
         }
 
         let lo = self.token.span;
@@ -2200,7 +2262,9 @@ impl<'a> Parser<'a> {
     }
 
     fn is_array_like_block(&mut self) -> bool {
-        self.look_ahead(1, |t| matches!(t.kind, TokenKind::Ident(..) | TokenKind::Literal(_)))
+        matches!(self.token.kind, TokenKind::OpenDelim(Delimiter::Brace))
+            && self
+                .look_ahead(1, |t| matches!(t.kind, TokenKind::Ident(..) | TokenKind::Literal(_)))
             && self.look_ahead(2, |t| t == &token::Comma)
             && self.look_ahead(3, |t| t.can_begin_expr())
     }
@@ -2212,9 +2276,9 @@ impl<'a> Parser<'a> {
         let mut snapshot = self.create_snapshot_for_diagnostic();
         match snapshot.parse_expr_array_or_repeat(exp!(CloseBrace)) {
             Ok(arr) => {
-                let guar = self.dcx().emit_err(errors::ArrayBracketsInsteadOfSpaces {
+                let guar = self.dcx().emit_err(errors::ArrayBracketsInsteadOfBraces {
                     span: arr.span,
-                    sub: errors::ArrayBracketsInsteadOfSpacesSugg {
+                    sub: errors::ArrayBracketsInsteadOfBracesSugg {
                         left: lo,
                         right: snapshot.prev_token.span,
                     },
@@ -2328,7 +2392,7 @@ impl<'a> Parser<'a> {
         let movability =
             if self.eat_keyword(exp!(Static)) { Movability::Static } else { Movability::Movable };
 
-        let coroutine_kind = if self.token.uninterpolated_span().at_least_rust_2018() {
+        let coroutine_kind = if self.token_uninterpolated_span().at_least_rust_2018() {
             self.parse_coroutine_kind(Case::Sensitive)
         } else {
             None
@@ -2337,7 +2401,8 @@ impl<'a> Parser<'a> {
         let capture_clause = self.parse_capture_clause()?;
         let (fn_decl, fn_arg_span) = self.parse_fn_block_decl()?;
         let decl_hi = self.prev_token.span;
-        let mut body = match fn_decl.output {
+        let mut body = match &fn_decl.output {
+            // No return type.
             FnRetTy::Default(_) => {
                 let restrictions =
                     self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET;
@@ -2349,11 +2414,8 @@ impl<'a> Parser<'a> {
                     Err(err) => self.recover_closure_body(err, before, prev, token, lo, decl_hi)?,
                 }
             }
-            _ => {
-                // If an explicit return type is given, require a block to appear (RFC 968).
-                let body_lo = self.token.span;
-                self.parse_expr_block(None, body_lo, BlockCheckMode::Default)?
-            }
+            // Explicit return type (`->`) needs block `-> T { }`.
+            FnRetTy::Ty(ty) => self.parse_closure_block_body(ty.span)?,
         };
 
         match coroutine_kind {
@@ -2405,6 +2467,49 @@ impl<'a> Parser<'a> {
         Ok(closure)
     }
 
+    /// If an explicit return type is given, require a block to appear (RFC 968).
+    fn parse_closure_block_body(&mut self, ret_span: Span) -> PResult<'a, P<Expr>> {
+        if self.may_recover()
+            && self.token.can_begin_expr()
+            && !matches!(self.token.kind, TokenKind::OpenDelim(Delimiter::Brace))
+            && !self.token.is_whole_block()
+        {
+            let snapshot = self.create_snapshot_for_diagnostic();
+            let restrictions =
+                self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET;
+            let tok = self.token.clone();
+            match self.parse_expr_res(restrictions, AttrWrapper::empty()) {
+                Ok((expr, _)) => {
+                    let descr = super::token_descr(&tok);
+                    let mut diag = self
+                        .dcx()
+                        .struct_span_err(tok.span, format!("expected `{{`, found {descr}"));
+                    diag.span_label(
+                        ret_span,
+                        "explicit return type requires closure body to be enclosed in braces",
+                    );
+                    diag.multipart_suggestion_verbose(
+                        "wrap the expression in curly braces",
+                        vec![
+                            (expr.span.shrink_to_lo(), "{ ".to_string()),
+                            (expr.span.shrink_to_hi(), " }".to_string()),
+                        ],
+                        Applicability::MachineApplicable,
+                    );
+                    diag.emit();
+                    return Ok(expr);
+                }
+                Err(diag) => {
+                    diag.cancel();
+                    self.restore_snapshot(snapshot);
+                }
+            }
+        }
+
+        let body_lo = self.token.span;
+        self.parse_expr_block(None, body_lo, BlockCheckMode::Default)
+    }
+
     /// Parses an optional `move` or `use` prefix to a closure-like construct.
     fn parse_capture_clause(&mut self) -> PResult<'a, CaptureBy> {
         if self.eat_keyword(exp!(Move)) {
@@ -2836,7 +2941,7 @@ impl<'a> Parser<'a> {
     /// Parses `for await? <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
     fn parse_expr_for(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> {
         let is_await =
-            self.token.uninterpolated_span().at_least_rust_2018() && self.eat_keyword(exp!(Await));
+            self.token_uninterpolated_span().at_least_rust_2018() && self.eat_keyword(exp!(Await));
 
         if is_await {
             self.psess.gated_spans.gate(sym::async_for_loop, self.prev_token.span);
@@ -3426,7 +3531,7 @@ impl<'a> Parser<'a> {
         self.token.is_keyword(kw::Try)
             && self
                 .look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block())
-            && self.token.uninterpolated_span().at_least_rust_2018()
+            && self.token_uninterpolated_span().at_least_rust_2018()
     }
 
     /// Parses an `async move? {...}` or `gen move? {...}` expression.
@@ -3809,8 +3914,8 @@ impl<'a> Parser<'a> {
         self.dcx().emit_err(errors::LeftArrowOperator { span });
     }
 
-    fn mk_assign_op(&self, binop: BinOp, lhs: P<Expr>, rhs: P<Expr>) -> ExprKind {
-        ExprKind::AssignOp(binop, lhs, rhs)
+    fn mk_assign_op(&self, assign_op: AssignOp, lhs: P<Expr>, rhs: P<Expr>) -> ExprKind {
+        ExprKind::AssignOp(assign_op, lhs, rhs)
     }
 
     fn mk_range(
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index aad18578375..9405b58ab3b 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -34,10 +34,10 @@ impl<'a> Parser<'a> {
     }
 
     /// Parses a `mod <foo> { ... }` or `mod <foo>;` item.
-    fn parse_item_mod(&mut self, attrs: &mut AttrVec) -> PResult<'a, ItemInfo> {
+    fn parse_item_mod(&mut self, attrs: &mut AttrVec) -> PResult<'a, ItemKind> {
         let safety = self.parse_safety(Case::Sensitive);
         self.expect_keyword(exp!(Mod))?;
-        let id = self.parse_ident()?;
+        let ident = self.parse_ident()?;
         let mod_kind = if self.eat(exp!(Semi)) {
             ModKind::Unloaded
         } else {
@@ -46,7 +46,7 @@ impl<'a> Parser<'a> {
             attrs.extend(inner_attrs);
             ModKind::Loaded(items, Inline::Yes, inner_span, Ok(()))
         };
-        Ok((id, ItemKind::Mod(safety, mod_kind)))
+        Ok(ItemKind::Mod(safety, ident, mod_kind))
     }
 
     /// Parses the contents of a module (inner attributes followed by module items).
@@ -115,8 +115,6 @@ impl<'a> Parser<'a> {
     }
 }
 
-pub(super) type ItemInfo = (Ident, ItemKind);
-
 impl<'a> Parser<'a> {
     pub fn parse_item(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<P<Item>>> {
         let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true };
@@ -163,11 +161,11 @@ impl<'a> Parser<'a> {
                 fn_parse_mode,
                 Case::Sensitive,
             )?;
-            if let Some((ident, kind)) = kind {
+            if let Some(kind) = kind {
                 this.error_on_unconsumed_default(def, &kind);
                 let span = lo.to(this.prev_token.span);
                 let id = DUMMY_NODE_ID;
-                let item = Item { ident, attrs, id, kind, vis, span, tokens: None };
+                let item = Item { attrs, id, kind, vis, span, tokens: None };
                 return Ok((Some(item), Trailing::No, UsePreAttrPos::No));
             }
 
@@ -208,7 +206,7 @@ impl<'a> Parser<'a> {
         def: &mut Defaultness,
         fn_parse_mode: FnParseMode,
         case: Case,
-    ) -> PResult<'a, Option<ItemInfo>> {
+    ) -> PResult<'a, Option<ItemKind>> {
         let check_pub = def == &Defaultness::Final;
         let mut def_ = || mem::replace(def, Defaultness::Final);
 
@@ -218,17 +216,15 @@ impl<'a> Parser<'a> {
             // FUNCTION ITEM
             let (ident, sig, generics, contract, body) =
                 self.parse_fn(attrs, fn_parse_mode, lo, vis, case)?;
-            (
+            ItemKind::Fn(Box::new(Fn {
+                defaultness: def_(),
                 ident,
-                ItemKind::Fn(Box::new(Fn {
-                    defaultness: def_(),
-                    sig,
-                    generics,
-                    contract,
-                    body,
-                    define_opaque: None,
-                })),
-            )
+                sig,
+                generics,
+                contract,
+                body,
+                define_opaque: None,
+            }))
         } else if self.eat_keyword(exp!(Extern)) {
             if self.eat_keyword(exp!(Crate)) {
                 // EXTERN CRATE
@@ -247,8 +243,7 @@ impl<'a> Parser<'a> {
             // STATIC ITEM
             self.bump(); // `static`
             let mutability = self.parse_mutability();
-            let (ident, item) = self.parse_static_item(safety, mutability)?;
-            (ident, ItemKind::Static(Box::new(item)))
+            self.parse_static_item(safety, mutability)?
         } else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) {
             // CONST ITEM
             if self.token.is_keyword(kw::Impl) {
@@ -258,16 +253,14 @@ impl<'a> Parser<'a> {
                 self.recover_const_mut(const_span);
                 self.recover_missing_kw_before_item()?;
                 let (ident, generics, ty, expr) = self.parse_const_item()?;
-                (
+                ItemKind::Const(Box::new(ConstItem {
+                    defaultness: def_(),
                     ident,
-                    ItemKind::Const(Box::new(ConstItem {
-                        defaultness: def_(),
-                        generics,
-                        ty,
-                        expr,
-                        define_opaque: None,
-                    })),
-                )
+                    generics,
+                    ty,
+                    expr,
+                    define_opaque: None,
+                }))
             }
         } else if self.check_keyword(exp!(Trait)) || self.check_auto_or_unsafe_trait_item() {
             // TRAIT ITEM
@@ -334,14 +327,14 @@ impl<'a> Parser<'a> {
                 self.recover_missing_kw_before_item()?;
             }
             // MACRO INVOCATION ITEM
-            (Ident::empty(), ItemKind::MacCall(P(self.parse_item_macro(vis)?)))
+            ItemKind::MacCall(P(self.parse_item_macro(vis)?))
         } else {
             return Ok(None);
         };
         Ok(Some(info))
     }
 
-    fn recover_import_as_use(&mut self) -> PResult<'a, Option<ItemInfo>> {
+    fn recover_import_as_use(&mut self) -> PResult<'a, Option<ItemKind>> {
         let span = self.token.span;
         let token_name = super::token_descr(&self.token);
         let snapshot = self.create_snapshot_for_diagnostic();
@@ -359,7 +352,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn parse_use_item(&mut self) -> PResult<'a, ItemInfo> {
+    fn parse_use_item(&mut self) -> PResult<'a, ItemKind> {
         let tree = self.parse_use_tree()?;
         if let Err(mut e) = self.expect_semi() {
             match tree.kind {
@@ -373,7 +366,7 @@ impl<'a> Parser<'a> {
             }
             return Err(e);
         }
-        Ok((Ident::empty(), ItemKind::Use(tree)))
+        Ok(ItemKind::Use(tree))
     }
 
     /// When parsing a statement, would the start of a path be an item?
@@ -483,7 +476,7 @@ impl<'a> Parser<'a> {
         if let Some(err) = err { Err(self.dcx().create_err(err)) } else { Ok(()) }
     }
 
-    fn parse_item_builtin(&mut self) -> PResult<'a, Option<ItemInfo>> {
+    fn parse_item_builtin(&mut self) -> PResult<'a, Option<ItemKind>> {
         // To be expanded
         Ok(None)
     }
@@ -577,7 +570,7 @@ impl<'a> Parser<'a> {
         &mut self,
         attrs: &mut AttrVec,
         defaultness: Defaultness,
-    ) -> PResult<'a, ItemInfo> {
+    ) -> PResult<'a, ItemKind> {
         let safety = self.parse_safety(Case::Sensitive);
         self.expect_keyword(exp!(Impl))?;
 
@@ -598,7 +591,7 @@ impl<'a> Parser<'a> {
         }
 
         // Parse stray `impl async Trait`
-        if (self.token.uninterpolated_span().at_least_rust_2018()
+        if (self.token_uninterpolated_span().at_least_rust_2018()
             && self.token.is_keyword(kw::Async))
             || self.is_kw_followed_by_ident(kw::Async)
         {
@@ -687,7 +680,7 @@ impl<'a> Parser<'a> {
             }
             None => (None, ty_first), // impl Type
         };
-        let item_kind = ItemKind::Impl(Box::new(Impl {
+        Ok(ItemKind::Impl(Box::new(Impl {
             safety,
             polarity,
             defaultness,
@@ -696,12 +689,10 @@ impl<'a> Parser<'a> {
             of_trait,
             self_ty,
             items: impl_items,
-        }));
-
-        Ok((Ident::empty(), item_kind))
+        })))
     }
 
-    fn parse_item_delegation(&mut self) -> PResult<'a, ItemInfo> {
+    fn parse_item_delegation(&mut self) -> PResult<'a, ItemKind> {
         let span = self.token.span;
         self.expect_keyword(exp!(Reuse))?;
 
@@ -724,7 +715,7 @@ impl<'a> Parser<'a> {
             })
         };
 
-        let (ident, item_kind) = if self.eat_path_sep() {
+        let item_kind = if self.eat_path_sep() {
             let suffixes = if self.eat(exp!(Star)) {
                 None
             } else {
@@ -732,7 +723,7 @@ impl<'a> Parser<'a> {
                 Some(self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), parse_suffix)?.0)
             };
             let deleg = DelegationMac { qself, prefix: path, suffixes, body: body(self)? };
-            (Ident::empty(), ItemKind::DelegationMac(Box::new(deleg)))
+            ItemKind::DelegationMac(Box::new(deleg))
         } else {
             let rename = rename(self)?;
             let ident = rename.unwrap_or_else(|| path.segments.last().unwrap().ident);
@@ -740,17 +731,18 @@ impl<'a> Parser<'a> {
                 id: DUMMY_NODE_ID,
                 qself,
                 path,
+                ident,
                 rename,
                 body: body(self)?,
                 from_glob: false,
             };
-            (ident, ItemKind::Delegation(Box::new(deleg)))
+            ItemKind::Delegation(Box::new(deleg))
         };
 
         let span = span.to(self.prev_token.span);
         self.psess.gated_spans.gate(sym::fn_delegation, span);
 
-        Ok((ident, item_kind))
+        Ok(item_kind)
     }
 
     fn parse_item_list<T>(
@@ -885,7 +877,7 @@ impl<'a> Parser<'a> {
             && self.look_ahead(1, |t| t.is_non_raw_ident_where(|i| i.name != kw::As))
         {
             self.bump(); // `default`
-            Defaultness::Default(self.prev_token.uninterpolated_span())
+            Defaultness::Default(self.prev_token_uninterpolated_span())
         } else {
             Defaultness::Final
         }
@@ -900,7 +892,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`.
-    fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemInfo> {
+    fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemKind> {
         let safety = self.parse_safety(Case::Sensitive);
         // Parse optional `auto` prefix.
         let is_auto = if self.eat_keyword(exp!(Auto)) {
@@ -941,15 +933,12 @@ impl<'a> Parser<'a> {
 
             self.psess.gated_spans.gate(sym::trait_alias, whole_span);
 
-            Ok((ident, ItemKind::TraitAlias(generics, bounds)))
+            Ok(ItemKind::TraitAlias(ident, generics, bounds))
         } else {
             // It's a normal trait.
             generics.where_clause = self.parse_where_clause()?;
             let items = self.parse_item_list(attrs, |p| p.parse_trait_item(ForceCollect::No))?;
-            Ok((
-                ident,
-                ItemKind::Trait(Box::new(Trait { is_auto, safety, generics, bounds, items })),
-            ))
+            Ok(ItemKind::Trait(Box::new(Trait { is_auto, safety, ident, generics, bounds, items })))
         }
     }
 
@@ -977,11 +966,12 @@ impl<'a> Parser<'a> {
         force_collect: ForceCollect,
     ) -> PResult<'a, Option<Option<P<AssocItem>>>> {
         Ok(self.parse_item_(fn_parse_mode, force_collect)?.map(
-            |Item { attrs, id, span, vis, ident, kind, tokens }| {
+            |Item { attrs, id, span, vis, kind, tokens }| {
                 let kind = match AssocItemKind::try_from(kind) {
                     Ok(kind) => kind,
                     Err(kind) => match kind {
                         ItemKind::Static(box StaticItem {
+                            ident,
                             ty,
                             safety: _,
                             mutability: _,
@@ -991,6 +981,7 @@ impl<'a> Parser<'a> {
                             self.dcx().emit_err(errors::AssociatedStaticItemNotAllowed { span });
                             AssocItemKind::Const(Box::new(ConstItem {
                                 defaultness: Defaultness::Final,
+                                ident,
                                 generics: Generics::default(),
                                 ty,
                                 expr,
@@ -1000,7 +991,7 @@ impl<'a> Parser<'a> {
                         _ => return self.error_bad_item_kind(span, &kind, "`trait`s or `impl`s"),
                     },
                 };
-                Some(P(Item { attrs, id, span, vis, ident, kind, tokens }))
+                Some(P(Item { attrs, id, span, vis, kind, tokens }))
             },
         ))
     }
@@ -1010,7 +1001,7 @@ impl<'a> Parser<'a> {
     /// TypeAlias = "type" Ident Generics (":" GenericBounds)? WhereClause ("=" Ty)? WhereClause ";" ;
     /// ```
     /// The `"type"` has already been eaten.
-    fn parse_type_alias(&mut self, defaultness: Defaultness) -> PResult<'a, ItemInfo> {
+    fn parse_type_alias(&mut self, defaultness: Defaultness) -> PResult<'a, ItemKind> {
         let ident = self.parse_ident()?;
         let mut generics = self.parse_generics()?;
 
@@ -1045,16 +1036,14 @@ impl<'a> Parser<'a> {
 
         self.expect_semi()?;
 
-        Ok((
+        Ok(ItemKind::TyAlias(Box::new(TyAlias {
+            defaultness,
             ident,
-            ItemKind::TyAlias(Box::new(TyAlias {
-                defaultness,
-                generics,
-                where_clauses,
-                bounds,
-                ty,
-            })),
-        ))
+            generics,
+            where_clauses,
+            bounds,
+            ty,
+        })))
     }
 
     /// Parses a `UseTree`.
@@ -1158,16 +1147,16 @@ impl<'a> Parser<'a> {
     /// extern crate foo;
     /// extern crate bar as foo;
     /// ```
-    fn parse_item_extern_crate(&mut self) -> PResult<'a, ItemInfo> {
+    fn parse_item_extern_crate(&mut self) -> PResult<'a, ItemKind> {
         // Accept `extern crate name-like-this` for better diagnostics
-        let orig_name = self.parse_crate_name_with_dashes()?;
-        let (item_name, orig_name) = if let Some(rename) = self.parse_rename()? {
-            (rename, Some(orig_name.name))
+        let orig_ident = self.parse_crate_name_with_dashes()?;
+        let (orig_name, item_ident) = if let Some(rename) = self.parse_rename()? {
+            (Some(orig_ident.name), rename)
         } else {
-            (orig_name, None)
+            (None, orig_ident)
         };
         self.expect_semi()?;
-        Ok((item_name, ItemKind::ExternCrate(orig_name)))
+        Ok(ItemKind::ExternCrate(orig_name, item_ident))
     }
 
     fn parse_crate_name_with_dashes(&mut self) -> PResult<'a, Ident> {
@@ -1218,8 +1207,8 @@ impl<'a> Parser<'a> {
         &mut self,
         attrs: &mut AttrVec,
         mut safety: Safety,
-    ) -> PResult<'a, ItemInfo> {
-        let extern_span = self.prev_token.uninterpolated_span();
+    ) -> PResult<'a, ItemKind> {
+        let extern_span = self.prev_token_uninterpolated_span();
         let abi = self.parse_abi(); // ABI?
         // FIXME: This recovery should be tested better.
         if safety == Safety::Default
@@ -1230,13 +1219,12 @@ impl<'a> Parser<'a> {
             safety = Safety::Unsafe(self.token.span);
             let _ = self.eat_keyword(exp!(Unsafe));
         }
-        let module = ast::ForeignMod {
+        Ok(ItemKind::ForeignMod(ast::ForeignMod {
             extern_span,
             safety,
             abi,
             items: self.parse_item_list(attrs, |p| p.parse_foreign_item(ForceCollect::No))?,
-        };
-        Ok((Ident::empty(), ItemKind::ForeignMod(module)))
+        }))
     }
 
     /// Parses a foreign item (one in an `extern { ... }` block).
@@ -1246,11 +1234,11 @@ impl<'a> Parser<'a> {
     ) -> PResult<'a, Option<Option<P<ForeignItem>>>> {
         let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: false };
         Ok(self.parse_item_(fn_parse_mode, force_collect)?.map(
-            |Item { attrs, id, span, vis, ident, kind, tokens }| {
+            |Item { attrs, id, span, vis, kind, tokens }| {
                 let kind = match ForeignItemKind::try_from(kind) {
                     Ok(kind) => kind,
                     Err(kind) => match kind {
-                        ItemKind::Const(box ConstItem { ty, expr, .. }) => {
+                        ItemKind::Const(box ConstItem { ident, ty, expr, .. }) => {
                             let const_span = Some(span.with_hi(ident.span.lo()))
                                 .filter(|span| span.can_be_used_for_suggestions());
                             self.dcx().emit_err(errors::ExternItemCannotBeConst {
@@ -1258,6 +1246,7 @@ impl<'a> Parser<'a> {
                                 const_span,
                             });
                             ForeignItemKind::Static(Box::new(StaticItem {
+                                ident,
                                 ty,
                                 mutability: Mutability::Not,
                                 expr,
@@ -1268,7 +1257,7 @@ impl<'a> Parser<'a> {
                         _ => return self.error_bad_item_kind(span, &kind, "`extern` blocks"),
                     },
                 };
-                Some(P(Item { attrs, id, span, vis, ident, kind, tokens }))
+                Some(P(Item { attrs, id, span, vis, kind, tokens }))
             },
         ))
     }
@@ -1301,12 +1290,24 @@ impl<'a> Parser<'a> {
     }
 
     fn is_unsafe_foreign_mod(&self) -> bool {
-        self.token.is_keyword(kw::Unsafe)
-            && self.is_keyword_ahead(1, &[kw::Extern])
-            && self.look_ahead(
-                2 + self.look_ahead(2, |t| t.can_begin_string_literal() as usize),
-                |t| *t == token::OpenDelim(Delimiter::Brace),
-            )
+        // Look for `unsafe`.
+        if !self.token.is_keyword(kw::Unsafe) {
+            return false;
+        }
+        // Look for `extern`.
+        if !self.is_keyword_ahead(1, &[kw::Extern]) {
+            return false;
+        }
+
+        // Look for the optional ABI string literal.
+        let n = if self.look_ahead(2, |t| t.can_begin_string_literal()) { 3 } else { 2 };
+
+        // Look for the `{`. Use `tree_look_ahead` because the ABI (if present)
+        // might be a metavariable i.e. an invisible-delimited sequence, and
+        // `tree_look_ahead` will consider that a single element when looking
+        // ahead.
+        self.tree_look_ahead(n, |t| matches!(t, TokenTree::Delimited(_, _, Delimiter::Brace, _)))
+            == Some(true)
     }
 
     fn is_static_global(&mut self) -> bool {
@@ -1343,13 +1344,13 @@ impl<'a> Parser<'a> {
         const_span: Span,
         attrs: &mut AttrVec,
         defaultness: Defaultness,
-    ) -> PResult<'a, ItemInfo> {
+    ) -> PResult<'a, ItemKind> {
         let impl_span = self.token.span;
         let err = self.expected_ident_found_err();
 
         // Only try to recover if this is implementing a trait for a type
-        let mut impl_info = match self.parse_item_impl(attrs, defaultness) {
-            Ok(impl_info) => impl_info,
+        let mut item_kind = match self.parse_item_impl(attrs, defaultness) {
+            Ok(item_kind) => item_kind,
             Err(recovery_error) => {
                 // Recovery failed, raise the "expected identifier" error
                 recovery_error.cancel();
@@ -1357,7 +1358,7 @@ impl<'a> Parser<'a> {
             }
         };
 
-        match &mut impl_info.1 {
+        match &mut item_kind {
             ItemKind::Impl(box Impl { of_trait: Some(trai), constness, .. }) => {
                 *constness = Const::Yes(const_span);
 
@@ -1374,10 +1375,11 @@ impl<'a> Parser<'a> {
             _ => unreachable!(),
         }
 
-        Ok(impl_info)
+        Ok(item_kind)
     }
 
-    /// Parse a static item with the prefix `"static" "mut"?` already parsed and stored in `mutability`.
+    /// Parse a static item with the prefix `"static" "mut"?` already parsed and stored in
+    /// `mutability`.
     ///
     /// ```ebnf
     /// Static = "static" "mut"? $ident ":" $ty (= $expr)? ";" ;
@@ -1386,7 +1388,7 @@ impl<'a> Parser<'a> {
         &mut self,
         safety: Safety,
         mutability: Mutability,
-    ) -> PResult<'a, (Ident, StaticItem)> {
+    ) -> PResult<'a, ItemKind> {
         let ident = self.parse_ident()?;
 
         if self.token == TokenKind::Lt && self.may_recover() {
@@ -1398,7 +1400,8 @@ impl<'a> Parser<'a> {
         // FIXME: This could maybe benefit from `.may_recover()`?
         let ty = match (self.eat(exp!(Colon)), self.check(exp!(Eq)) | self.check(exp!(Semi))) {
             (true, false) => self.parse_ty()?,
-            // If there wasn't a `:` or the colon was followed by a `=` or `;`, recover a missing type.
+            // If there wasn't a `:` or the colon was followed by a `=` or `;`, recover a missing
+            // type.
             (colon, _) => self.recover_missing_global_item_type(colon, Some(mutability)),
         };
 
@@ -1406,7 +1409,8 @@ impl<'a> Parser<'a> {
 
         self.expect_semi()?;
 
-        Ok((ident, StaticItem { ty, safety, mutability, expr, define_opaque: None }))
+        let item = StaticItem { ident, ty, safety, mutability, expr, define_opaque: None };
+        Ok(ItemKind::Static(Box::new(item)))
     }
 
     /// Parse a constant item with the prefix `"const"` already parsed.
@@ -1531,7 +1535,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parses an enum declaration.
-    fn parse_item_enum(&mut self) -> PResult<'a, ItemInfo> {
+    fn parse_item_enum(&mut self) -> PResult<'a, ItemKind> {
         if self.token.is_keyword(kw::Struct) {
             let span = self.prev_token.span.to(self.token.span);
             let err = errors::EnumStructMutuallyExclusive { span };
@@ -1544,7 +1548,7 @@ impl<'a> Parser<'a> {
         }
 
         let prev_span = self.prev_token.span;
-        let id = self.parse_ident()?;
+        let ident = self.parse_ident()?;
         let mut generics = self.parse_generics()?;
         generics.where_clause = self.parse_where_clause()?;
 
@@ -1555,10 +1559,10 @@ impl<'a> Parser<'a> {
             (thin_vec![], Trailing::No)
         } else {
             self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), |p| {
-                p.parse_enum_variant(id.span)
+                p.parse_enum_variant(ident.span)
             })
             .map_err(|mut err| {
-                err.span_label(id.span, "while parsing this enum");
+                err.span_label(ident.span, "while parsing this enum");
                 if self.token == token::Colon {
                     let snapshot = self.create_snapshot_for_diagnostic();
                     self.bump();
@@ -1584,7 +1588,7 @@ impl<'a> Parser<'a> {
         };
 
         let enum_definition = EnumDef { variants: variants.into_iter().flatten().collect() };
-        Ok((id, ItemKind::Enum(enum_definition, generics)))
+        Ok(ItemKind::Enum(ident, enum_definition, generics))
     }
 
     fn parse_enum_variant(&mut self, span: Span) -> PResult<'a, Option<Variant>> {
@@ -1676,8 +1680,8 @@ impl<'a> Parser<'a> {
     }
 
     /// Parses `struct Foo { ... }`.
-    fn parse_item_struct(&mut self) -> PResult<'a, ItemInfo> {
-        let class_name = self.parse_ident()?;
+    fn parse_item_struct(&mut self) -> PResult<'a, ItemKind> {
+        let ident = self.parse_ident()?;
 
         let mut generics = self.parse_generics()?;
 
@@ -1698,7 +1702,7 @@ impl<'a> Parser<'a> {
         let vdata = if self.token.is_keyword(kw::Where) {
             let tuple_struct_body;
             (generics.where_clause, tuple_struct_body) =
-                self.parse_struct_where_clause(class_name, generics.span)?;
+                self.parse_struct_where_clause(ident, generics.span)?;
 
             if let Some(body) = tuple_struct_body {
                 // If we see a misplaced tuple struct body: `struct Foo<T> where T: Copy, (T);`
@@ -1712,7 +1716,7 @@ impl<'a> Parser<'a> {
                 // If we see: `struct Foo<T> where T: Copy { ... }`
                 let (fields, recovered) = self.parse_record_struct_body(
                     "struct",
-                    class_name.span,
+                    ident.span,
                     generics.where_clause.has_where_token,
                 )?;
                 VariantData::Struct { fields, recovered }
@@ -1724,7 +1728,7 @@ impl<'a> Parser<'a> {
         } else if self.token == token::OpenDelim(Delimiter::Brace) {
             let (fields, recovered) = self.parse_record_struct_body(
                 "struct",
-                class_name.span,
+                ident.span,
                 generics.where_clause.has_where_token,
             )?;
             VariantData::Struct { fields, recovered }
@@ -1740,12 +1744,12 @@ impl<'a> Parser<'a> {
             return Err(self.dcx().create_err(err));
         };
 
-        Ok((class_name, ItemKind::Struct(vdata, generics)))
+        Ok(ItemKind::Struct(ident, vdata, generics))
     }
 
     /// Parses `union Foo { ... }`.
-    fn parse_item_union(&mut self) -> PResult<'a, ItemInfo> {
-        let class_name = self.parse_ident()?;
+    fn parse_item_union(&mut self) -> PResult<'a, ItemKind> {
+        let ident = self.parse_ident()?;
 
         let mut generics = self.parse_generics()?;
 
@@ -1753,14 +1757,14 @@ impl<'a> Parser<'a> {
             generics.where_clause = self.parse_where_clause()?;
             let (fields, recovered) = self.parse_record_struct_body(
                 "union",
-                class_name.span,
+                ident.span,
                 generics.where_clause.has_where_token,
             )?;
             VariantData::Struct { fields, recovered }
         } else if self.token == token::OpenDelim(Delimiter::Brace) {
             let (fields, recovered) = self.parse_record_struct_body(
                 "union",
-                class_name.span,
+                ident.span,
                 generics.where_clause.has_where_token,
             )?;
             VariantData::Struct { fields, recovered }
@@ -1772,7 +1776,7 @@ impl<'a> Parser<'a> {
             return Err(err);
         };
 
-        Ok((class_name, ItemKind::Union(vdata, generics)))
+        Ok(ItemKind::Union(ident, vdata, generics))
     }
 
     /// This function parses the fields of record structs:
@@ -2124,15 +2128,17 @@ impl<'a> Parser<'a> {
                 }
             } else if self.eat_keyword(exp!(Struct)) {
                 match self.parse_item_struct() {
-                    Ok((ident, _)) => self
-                        .dcx()
-                        .struct_span_err(
-                            lo.with_hi(ident.span.hi()),
-                            format!("structs are not allowed in {adt_ty} definitions"),
-                        )
-                        .with_help(
-                            "consider creating a new `struct` definition instead of nesting",
-                        ),
+                    Ok(item) => {
+                        let ItemKind::Struct(ident, ..) = item else { unreachable!() };
+                        self.dcx()
+                            .struct_span_err(
+                                lo.with_hi(ident.span.hi()),
+                                format!("structs are not allowed in {adt_ty} definitions"),
+                            )
+                            .with_help(
+                                "consider creating a new `struct` definition instead of nesting",
+                            )
+                    }
                     Err(err) => {
                         err.cancel();
                         self.restore_snapshot(snapshot);
@@ -2177,7 +2183,7 @@ impl<'a> Parser<'a> {
     /// MacParams = "(" TOKEN_STREAM ")" ;
     /// DeclMac = "macro" Ident MacParams? MacBody ;
     /// ```
-    fn parse_item_decl_macro(&mut self, lo: Span) -> PResult<'a, ItemInfo> {
+    fn parse_item_decl_macro(&mut self, lo: Span) -> PResult<'a, ItemKind> {
         let ident = self.parse_ident()?;
         let body = if self.check(exp!(OpenBrace)) {
             self.parse_delim_args()? // `MacBody`
@@ -2199,7 +2205,7 @@ impl<'a> Parser<'a> {
         };
 
         self.psess.gated_spans.gate(sym::decl_macro, lo.to(self.prev_token.span));
-        Ok((ident, ItemKind::MacroDef(ast::MacroDef { body, macro_rules: false })))
+        Ok(ItemKind::MacroDef(ident, ast::MacroDef { body, macro_rules: false }))
     }
 
     /// Is this a possibly malformed start of a `macro_rules! foo` item definition?
@@ -2228,7 +2234,7 @@ impl<'a> Parser<'a> {
         &mut self,
         vis: &Visibility,
         has_bang: bool,
-    ) -> PResult<'a, ItemInfo> {
+    ) -> PResult<'a, ItemKind> {
         self.expect_keyword(exp!(MacroRules))?; // `macro_rules`
 
         if has_bang {
@@ -2246,7 +2252,7 @@ impl<'a> Parser<'a> {
         self.eat_semi_for_macro_if_needed(&body);
         self.complain_if_pub_macro(vis, true);
 
-        Ok((ident, ItemKind::MacroDef(ast::MacroDef { body, macro_rules: true })))
+        Ok(ItemKind::MacroDef(ident, ast::MacroDef { body, macro_rules: true }))
     }
 
     /// Item macro invocations or `macro_rules!` definitions need inherited visibility.
@@ -2610,13 +2616,36 @@ impl<'a> Parser<'a> {
                 })
             // `extern ABI fn`
             || self.check_keyword_case(exp!(Extern), case)
+                // Use `tree_look_ahead` because `ABI` might be a metavariable,
+                // i.e. an invisible-delimited sequence, and `tree_look_ahead`
+                // will consider that a single element when looking ahead.
                 && self.look_ahead(1, |t| t.can_begin_string_literal())
-                && (self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, case)) ||
+                && (self.tree_look_ahead(2, |tt| {
+                    match tt {
+                        TokenTree::Token(t, _) => t.is_keyword_case(kw::Fn, case),
+                        TokenTree::Delimited(..) => false,
+                    }
+                }) == Some(true) ||
                     // This branch is only for better diagnostics; `pub`, `unsafe`, etc. are not
                     // allowed here.
                     (self.may_recover()
-                        && self.look_ahead(2, |t| ALL_QUALS.iter().any(|exp| t.is_keyword(exp.kw)))
-                        && self.look_ahead(3, |t| t.is_keyword_case(kw::Fn, case))))
+                        && self.tree_look_ahead(2, |tt| {
+                            match tt {
+                                TokenTree::Token(t, _) =>
+                                    ALL_QUALS.iter().any(|exp| {
+                                        t.is_keyword(exp.kw)
+                                    }),
+                                TokenTree::Delimited(..) => false,
+                            }
+                        }) == Some(true)
+                        && self.tree_look_ahead(3, |tt| {
+                            match tt {
+                                TokenTree::Token(t, _) => t.is_keyword_case(kw::Fn, case),
+                                TokenTree::Delimited(..) => false,
+                            }
+                        }) == Some(true)
+                    )
+                )
     }
 
     /// Parses all the "front matter" (or "qualifiers") for a `fn` declaration,
@@ -2752,7 +2781,7 @@ impl<'a> Parser<'a> {
                             .expect("Span extracted directly from keyword should always work");
 
                         err.span_suggestion(
-                            self.token.uninterpolated_span(),
+                            self.token_uninterpolated_span(),
                             format!("`{original_kw}` already used earlier, remove this one"),
                             "",
                             Applicability::MachineApplicable,
@@ -2763,7 +2792,7 @@ impl<'a> Parser<'a> {
                     else if let Some(WrongKw::Misplaced(correct_pos_sp)) = wrong_kw {
                         let correct_pos_sp = correct_pos_sp.to(self.prev_token.span);
                         if let Ok(current_qual) = self.span_to_snippet(correct_pos_sp) {
-                            let misplaced_qual_sp = self.token.uninterpolated_span();
+                            let misplaced_qual_sp = self.token_uninterpolated_span();
                             let misplaced_qual = self.span_to_snippet(misplaced_qual_sp).unwrap();
 
                             err.span_suggestion(
@@ -2931,13 +2960,30 @@ impl<'a> Parser<'a> {
                 let parser_snapshot_before_ty = this.create_snapshot_for_diagnostic();
                 this.eat_incorrect_doc_comment_for_param_type();
                 let mut ty = this.parse_ty_for_param();
-                if ty.is_ok()
-                    && this.token != token::Comma
-                    && this.token != token::CloseDelim(Delimiter::Parenthesis)
-                {
-                    // This wasn't actually a type, but a pattern looking like a type,
-                    // so we are going to rollback and re-parse for recovery.
-                    ty = this.unexpected_any();
+
+                if let Ok(t) = &ty {
+                    // Check for trailing angle brackets
+                    if let TyKind::Path(_, Path { segments, .. }) = &t.kind {
+                        if let Some(segment) = segments.last() {
+                            if let Some(guar) =
+                                this.check_trailing_angle_brackets(segment, &[exp!(CloseParen)])
+                            {
+                                return Ok((
+                                    dummy_arg(segment.ident, guar),
+                                    Trailing::No,
+                                    UsePreAttrPos::No,
+                                ));
+                            }
+                        }
+                    }
+
+                    if this.token != token::Comma
+                        && this.token != token::CloseDelim(Delimiter::Parenthesis)
+                    {
+                        // This wasn't actually a type, but a pattern looking like a type,
+                        // so we are going to rollback and re-parse for recovery.
+                        ty = this.unexpected_any();
+                    }
                 }
                 match ty {
                     Ok(ty) => {
@@ -2948,6 +2994,7 @@ impl<'a> Parser<'a> {
                     }
                     // If this is a C-variadic argument and we hit an error, return the error.
                     Err(err) if this.token == token::DotDotDot => return Err(err),
+                    Err(err) if this.unmatched_angle_bracket_count > 0 => return Err(err),
                     // Recover from attempting to parse the argument as a type without pattern.
                     Err(err) => {
                         err.cancel();
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index a79b4048288..3b0861a9942 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -24,8 +24,8 @@ pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma};
 use path::PathStyle;
 use rustc_ast::ptr::P;
 use rustc_ast::token::{
-    self, Delimiter, IdentIsRaw, InvisibleOrigin, MetaVarKind, Nonterminal, NtPatKind, Token,
-    TokenKind,
+    self, Delimiter, IdentIsRaw, InvisibleOrigin, MetaVarKind, Nonterminal, NtExprKind, NtPatKind,
+    Token, TokenKind,
 };
 use rustc_ast::tokenstream::{AttrsTarget, Spacing, TokenStream, TokenTree};
 use rustc_ast::util::case::Case;
@@ -101,6 +101,7 @@ pub enum ForceCollect {
 #[macro_export]
 macro_rules! maybe_whole {
     ($p:expr, $constructor:ident, |$x:ident| $e:expr) => {
+        #[allow(irrefutable_let_patterns)] // FIXME: temporary
         if let token::Interpolated(nt) = &$p.token.kind
             && let token::$constructor(x) = &**nt
         {
@@ -299,6 +300,10 @@ impl TokenTreeCursor {
         self.stream.get(self.index)
     }
 
+    fn look_ahead(&self, n: usize) -> Option<&TokenTree> {
+        self.stream.get(self.index + n)
+    }
+
     #[inline]
     fn bump(&mut self) {
         self.index += 1;
@@ -507,6 +512,14 @@ impl<'a> Parser<'a> {
         self
     }
 
+    #[inline]
+    fn with_recovery<T>(&mut self, recovery: Recovery, f: impl FnOnce(&mut Self) -> T) -> T {
+        let old = mem::replace(&mut self.recovery, recovery);
+        let res = f(self);
+        self.recovery = old;
+        res
+    }
+
     /// Whether the parser is allowed to recover from broken code.
     ///
     /// If this returns false, recovering broken code into valid code (especially if this recovery does lookahead)
@@ -765,7 +778,14 @@ impl<'a> Parser<'a> {
             && match_mv_kind(mv_kind)
         {
             self.bump();
-            let res = f(self).expect("failed to reparse {mv_kind:?}");
+
+            // Recovery is disabled when parsing macro arguments, so it must
+            // also be disabled when reparsing pasted macro arguments,
+            // otherwise we get inconsistent results (e.g. #137874).
+            let res = self.with_recovery(Recovery::Forbidden, |this| {
+                f(this).expect("failed to reparse {mv_kind:?}")
+            });
+
             if let token::CloseDelim(delim) = self.token.kind
                 && let Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind)) = delim
                 && match_mv_kind(mv_kind)
@@ -1290,6 +1310,17 @@ impl<'a> Parser<'a> {
         looker(&token)
     }
 
+    /// Like `lookahead`, but skips over token trees rather than tokens. Useful
+    /// when looking past possible metavariable pasting sites.
+    pub fn tree_look_ahead<R>(
+        &self,
+        dist: usize,
+        looker: impl FnOnce(&TokenTree) -> R,
+    ) -> Option<R> {
+        assert_ne!(dist, 0);
+        self.token_cursor.curr.look_ahead(dist - 1).map(looker)
+    }
+
     /// Returns whether any of the given keywords are `dist` tokens ahead of the current one.
     pub(crate) fn is_keyword_ahead(&self, dist: usize, kws: &[Symbol]) -> bool {
         self.look_ahead(dist, |t| kws.iter().any(|&kw| t.is_keyword(kw)))
@@ -1297,14 +1328,14 @@ impl<'a> Parser<'a> {
 
     /// Parses asyncness: `async` or nothing.
     fn parse_coroutine_kind(&mut self, case: Case) -> Option<CoroutineKind> {
-        let span = self.token.uninterpolated_span();
+        let span = self.token_uninterpolated_span();
         if self.eat_keyword_case(exp!(Async), case) {
             // FIXME(gen_blocks): Do we want to unconditionally parse `gen` and then
             // error if edition <= 2024, like we do with async and edition <= 2018?
-            if self.token.uninterpolated_span().at_least_rust_2024()
+            if self.token_uninterpolated_span().at_least_rust_2024()
                 && self.eat_keyword_case(exp!(Gen), case)
             {
-                let gen_span = self.prev_token.uninterpolated_span();
+                let gen_span = self.prev_token_uninterpolated_span();
                 Some(CoroutineKind::AsyncGen {
                     span: span.to(gen_span),
                     closure_id: DUMMY_NODE_ID,
@@ -1317,7 +1348,7 @@ impl<'a> Parser<'a> {
                     return_impl_trait_id: DUMMY_NODE_ID,
                 })
             }
-        } else if self.token.uninterpolated_span().at_least_rust_2024()
+        } else if self.token_uninterpolated_span().at_least_rust_2024()
             && self.eat_keyword_case(exp!(Gen), case)
         {
             Some(CoroutineKind::Gen {
@@ -1333,9 +1364,9 @@ impl<'a> Parser<'a> {
     /// Parses fn unsafety: `unsafe`, `safe` or nothing.
     fn parse_safety(&mut self, case: Case) -> Safety {
         if self.eat_keyword_case(exp!(Unsafe), case) {
-            Safety::Unsafe(self.prev_token.uninterpolated_span())
+            Safety::Unsafe(self.prev_token_uninterpolated_span())
         } else if self.eat_keyword_case(exp!(Safe), case) {
-            Safety::Safe(self.prev_token.uninterpolated_span())
+            Safety::Safe(self.prev_token_uninterpolated_span())
         } else {
             Safety::Default
         }
@@ -1362,7 +1393,7 @@ impl<'a> Parser<'a> {
                 .look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block())
             && self.eat_keyword_case(exp!(Const), case)
         {
-            Const::Yes(self.prev_token.uninterpolated_span())
+            Const::Yes(self.prev_token_uninterpolated_span())
         } else {
             Const::No
         }
@@ -1370,9 +1401,6 @@ impl<'a> Parser<'a> {
 
     /// Parses inline const expressions.
     fn parse_const_block(&mut self, span: Span, pat: bool) -> PResult<'a, P<Expr>> {
-        if pat {
-            self.psess.gated_spans.gate(sym::inline_const_pat, span);
-        }
         self.expect_keyword(exp!(Const))?;
         let (attrs, blk) = self.parse_inner_attrs_and_block(None)?;
         let anon_const = AnonConst {
@@ -1380,7 +1408,17 @@ impl<'a> Parser<'a> {
             value: self.mk_expr(blk.span, ExprKind::Block(blk, None)),
         };
         let blk_span = anon_const.value.span;
-        Ok(self.mk_expr_with_attrs(span.to(blk_span), ExprKind::ConstBlock(anon_const), attrs))
+        let kind = if pat {
+            let guar = self
+                .dcx()
+                .struct_span_err(blk_span, "`inline_const_pat` has been removed")
+                .with_help("use a named `const`-item or an `if`-guard instead")
+                .emit();
+            ExprKind::Err(guar)
+        } else {
+            ExprKind::ConstBlock(anon_const)
+        };
+        Ok(self.mk_expr_with_attrs(span.to(blk_span), kind, attrs))
     }
 
     /// Parses mutability (`mut` or nothing).
@@ -1699,6 +1737,35 @@ impl<'a> Parser<'a> {
     pub fn approx_token_stream_pos(&self) -> u32 {
         self.num_bump_calls
     }
+
+    /// For interpolated `self.token`, returns a span of the fragment to which
+    /// the interpolated token refers. For all other tokens this is just a
+    /// regular span. It is particularly important to use this for identifiers
+    /// and lifetimes for which spans affect name resolution and edition
+    /// checks. Note that keywords are also identifiers, so they should use
+    /// this if they keep spans or perform edition checks.
+    pub fn token_uninterpolated_span(&self) -> Span {
+        match &self.token.kind {
+            token::NtIdent(ident, _) | token::NtLifetime(ident, _) => ident.span,
+            token::Interpolated(nt) => nt.use_span(),
+            token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(_))) => {
+                self.look_ahead(1, |t| t.span)
+            }
+            _ => self.token.span,
+        }
+    }
+
+    /// Like `token_uninterpolated_span`, but works on `self.prev_token`.
+    pub fn prev_token_uninterpolated_span(&self) -> Span {
+        match &self.prev_token.kind {
+            token::NtIdent(ident, _) | token::NtLifetime(ident, _) => ident.span,
+            token::Interpolated(nt) => nt.use_span(),
+            token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(_))) => {
+                self.look_ahead(0, |t| t.span)
+            }
+            _ => self.prev_token.span,
+        }
+    }
 }
 
 pub(crate) fn make_unclosed_delims_error(
@@ -1751,6 +1818,8 @@ pub enum ParseNtResult {
     Item(P<ast::Item>),
     Stmt(P<ast::Stmt>),
     Pat(P<ast::Pat>, NtPatKind),
+    Expr(P<ast::Expr>, NtExprKind),
+    Literal(P<ast::Expr>),
     Ty(P<ast::Ty>),
     Meta(P<ast::AttrItem>),
     Path(P<ast::Path>),
diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs
index 1123755ce00..b4e540d670d 100644
--- a/compiler/rustc_parse/src/parser/nonterminal.rs
+++ b/compiler/rustc_parse/src/parser/nonterminal.rs
@@ -48,10 +48,6 @@ impl<'a> Parser<'a> {
         /// Old variant of `may_be_ident`. Being phased out.
         fn nt_may_be_ident(nt: &Nonterminal) -> bool {
             match nt {
-                NtExpr(_)
-                | NtLiteral(_) // `true`, `false`
-                => true,
-
                 NtBlock(_) => false,
             }
         }
@@ -95,7 +91,7 @@ impl<'a> Parser<'a> {
                 token::OpenDelim(Delimiter::Brace) => true,
                 token::NtLifetime(..) => true,
                 token::Interpolated(nt) => match &**nt {
-                    NtBlock(_) | NtExpr(_) | NtLiteral(_) => true,
+                    NtBlock(_) => true,
                 },
                 token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(k))) => match k {
                     MetaVarKind::Block
@@ -179,10 +175,14 @@ impl<'a> Parser<'a> {
                     pat_kind,
                 ));
             }
-            NonterminalKind::Expr(_) => NtExpr(self.parse_expr_force_collect()?),
+            NonterminalKind::Expr(expr_kind) => {
+                return Ok(ParseNtResult::Expr(self.parse_expr_force_collect()?, expr_kind));
+            }
             NonterminalKind::Literal => {
-                // The `:literal` matcher does not support attributes
-                NtLiteral(self.collect_tokens_no_attrs(|this| this.parse_literal_maybe_minus())?)
+                // The `:literal` matcher does not support attributes.
+                return Ok(ParseNtResult::Literal(
+                    self.collect_tokens_no_attrs(|this| this.parse_literal_maybe_minus())?,
+                ));
             }
             NonterminalKind::Ty => {
                 return Ok(ParseNtResult::Ty(
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index ec14c5718da..9612f71b2af 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -631,15 +631,6 @@ impl<'a> Parser<'a> {
                             ident,
                             indentation,
                         });
-
-                        // help: wrap the expr in a `const { expr }`
-                        // FIXME(inline_const_pat): once stabilized, remove this check and remove the `(requires #[feature(inline_const_pat)])` note from the message
-                        if self.parser.psess.unstable_features.is_nightly_build() {
-                            err.subdiagnostic(UnexpectedExpressionInPatternSugg::InlineConst {
-                                start_span: expr_span.shrink_to_lo(),
-                                end_span: expr_span.shrink_to_hi(),
-                            });
-                        }
                     },
                 );
             }
@@ -1261,7 +1252,7 @@ impl<'a> Parser<'a> {
                 || *t == token::Dot // e.g. `.5` for recovery;
                 || matches!(t.kind, token::Literal(..) | token::Minus)
                 || t.is_bool_lit()
-                || t.is_whole_expr()
+                || t.is_metavar_expr()
                 || t.is_lifetime() // recover `'a` instead of `'a'`
                 || (self.may_recover() // recover leading `(`
                     && *t == token::OpenDelim(Delimiter::Parenthesis)
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 97cd4d2117f..2cd09aa8959 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -73,7 +73,20 @@ impl<'a> Parser<'a> {
             });
         }
 
-        let stmt = if self.token.is_keyword(kw::Let) {
+        let stmt = if self.token.is_keyword(kw::Super) && self.is_keyword_ahead(1, &[kw::Let]) {
+            self.collect_tokens(None, attrs, force_collect, |this, attrs| {
+                this.expect_keyword(exp!(Super))?;
+                this.psess.gated_spans.gate(sym::super_let, this.prev_token.span);
+                this.expect_keyword(exp!(Let))?;
+                let local = this.parse_local(attrs)?; // FIXME(mara): implement super let
+                let trailing = Trailing::from(capture_semi && this.token == token::Semi);
+                Ok((
+                    this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
+                    trailing,
+                    UsePreAttrPos::No,
+                ))
+            })?
+        } else if self.token.is_keyword(kw::Let) {
             self.collect_tokens(None, attrs, force_collect, |this, attrs| {
                 this.expect_keyword(exp!(Let))?;
                 let local = this.parse_local(attrs)?;
diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs
index 471966d086d..49ae6cb9b72 100644
--- a/compiler/rustc_parse/src/parser/tests.rs
+++ b/compiler/rustc_parse/src/parser/tests.rs
@@ -2922,7 +2922,7 @@ fn out_of_line_mod() {
         .unwrap()
         .unwrap();
 
-        let ast::ItemKind::Mod(_, mod_kind) = &item.kind else { panic!() };
+        let ast::ItemKind::Mod(_, _, mod_kind) = &item.kind else { panic!() };
         assert_matches!(mod_kind, ast::ModKind::Loaded(items, ..) if items.len() == 2);
     });
 }
diff --git a/compiler/rustc_parse/src/parser/token_type.rs b/compiler/rustc_parse/src/parser/token_type.rs
index 886438fd583..add3c970201 100644
--- a/compiler/rustc_parse/src/parser/token_type.rs
+++ b/compiler/rustc_parse/src/parser/token_type.rs
@@ -114,6 +114,7 @@ pub enum TokenType {
     KwSelfUpper,
     KwStatic,
     KwStruct,
+    KwSuper,
     KwTrait,
     KwTry,
     KwType,
@@ -250,6 +251,7 @@ impl TokenType {
             KwSelfUpper,
             KwStatic,
             KwStruct,
+            KwSuper,
             KwTrait,
             KwTry,
             KwType,
@@ -324,6 +326,7 @@ impl TokenType {
             TokenType::KwSelfUpper => Some(kw::SelfUpper),
             TokenType::KwStatic => Some(kw::Static),
             TokenType::KwStruct => Some(kw::Struct),
+            TokenType::KwSuper => Some(kw::Super),
             TokenType::KwTrait => Some(kw::Trait),
             TokenType::KwTry => Some(kw::Try),
             TokenType::KwType => Some(kw::Type),
@@ -549,6 +552,7 @@ macro_rules! exp {
     (SelfUpper)      => { exp!(@kw, SelfUpper,  KwSelfUpper) };
     (Static)         => { exp!(@kw, Static,     KwStatic) };
     (Struct)         => { exp!(@kw, Struct,     KwStruct) };
+    (Super)          => { exp!(@kw, Super,      KwSuper) };
     (Trait)          => { exp!(@kw, Trait,      KwTrait) };
     (Try)            => { exp!(@kw, Try,        KwTry) };
     (Type)           => { exp!(@kw, Type,       KwType) };
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index b45ebae079c..93705da22c4 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -775,7 +775,7 @@ impl<'a> Parser<'a> {
     /// Is a `dyn B0 + ... + Bn` type allowed here?
     fn is_explicit_dyn_type(&mut self) -> bool {
         self.check_keyword(exp!(Dyn))
-            && (self.token.uninterpolated_span().at_least_rust_2018()
+            && (self.token_uninterpolated_span().at_least_rust_2018()
                 || self.look_ahead(1, |t| {
                     (can_begin_dyn_bound_in_edition_2015(t) || *t == TokenKind::Star)
                         && !can_continue_type_after_non_fn_ident(t)
@@ -998,13 +998,13 @@ impl<'a> Parser<'a> {
             BoundConstness::Never
         };
 
-        let asyncness = if self.token.uninterpolated_span().at_least_rust_2018()
+        let asyncness = if self.token_uninterpolated_span().at_least_rust_2018()
             && self.eat_keyword(exp!(Async))
         {
             self.psess.gated_spans.gate(sym::async_trait_bounds, self.prev_token.span);
             BoundAsyncness::Async(self.prev_token.span)
         } else if self.may_recover()
-            && self.token.uninterpolated_span().is_rust_2015()
+            && self.token_uninterpolated_span().is_rust_2015()
             && self.is_kw_followed_by_ident(kw::Async)
         {
             self.bump(); // eat `async`
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 06398dd4f72..bea86801ed7 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -383,6 +383,10 @@ passes_inline_ignored_constants =
     .warn = {-passes_previously_accepted}
     .note = {-passes_see_issue(issue: "65833")}
 
+passes_inline_ignored_for_exported =
+    `#[inline]` is ignored on externally exported functions
+    .help = externally exported functions are functions with `#[no_mangle]`, `#[export_name]`, or `#[linkage]`
+
 passes_inline_ignored_function_prototype =
     `#[inline]` is ignored on function prototypes
 
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 1e1fb42a48f..669349f3380 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -35,7 +35,7 @@ use rustc_session::lint::builtin::{
     UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES,
 };
 use rustc_session::parse::feature_err;
-use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, kw, sym};
+use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, edition, kw, sym};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs};
 use rustc_trait_selection::traits::ObligationCtxt;
@@ -451,6 +451,23 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 });
             }
         }
+
+        // `#[inline]` is ignored if the symbol must be codegened upstream because it's exported.
+        if let Some(did) = hir_id.as_owner()
+            && self.tcx.def_kind(did).has_codegen_attrs()
+            && !matches!(attr.meta_item_list().as_deref(), Some([item]) if item.has_name(sym::never))
+        {
+            let attrs = self.tcx.codegen_fn_attrs(did);
+            // Not checking naked as `#[inline]` is forbidden for naked functions anyways.
+            if attrs.contains_extern_indicator() {
+                self.tcx.emit_node_span_lint(
+                    UNUSED_ATTRIBUTES,
+                    hir_id,
+                    attr.span(),
+                    errors::InlineIgnoredForExported {},
+                );
+            }
+        }
     }
 
     /// Checks that `#[coverage(..)]` is applied to a function/closure/method,
@@ -1021,14 +1038,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             // FIXME: Once rustdoc can handle URL conflicts on case insensitive file systems, we
             // can remove the `SelfTy` case here, remove `sym::SelfTy`, and update the
             // `#[doc(keyword = "SelfTy")` attribute in `library/std/src/keyword_docs.rs`.
-            s <= kw::Union || s == sym::SelfTy
+            s.is_reserved(|| edition::LATEST_STABLE_EDITION) || s.is_weak() || s == sym::SelfTy
         }
 
-        let doc_keyword = meta.value_str().unwrap_or(kw::Empty);
-        if doc_keyword == kw::Empty {
-            self.doc_attr_str_error(meta, "keyword");
-            return;
-        }
+        let doc_keyword = match meta.value_str() {
+            Some(value) if value != kw::Empty => value,
+            _ => return self.doc_attr_str_error(meta, "keyword"),
+        };
+
         let item_kind = match self.tcx.hir_node(hir_id) {
             hir::Node::Item(item) => Some(&item.kind),
             _ => None,
@@ -1140,7 +1157,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                     errors::DocInlineOnlyUse {
                         attr_span: meta.span(),
                         item_span: (attr.style() == AttrStyle::Outer)
-                            .then(|| self.tcx.hir().span(hir_id)),
+                            .then(|| self.tcx.hir_span(hir_id)),
                     },
                 );
             }
@@ -1162,7 +1179,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 errors::DocMaskedOnlyExternCrate {
                     attr_span: meta.span(),
                     item_span: (attr.style() == AttrStyle::Outer)
-                        .then(|| self.tcx.hir().span(hir_id)),
+                        .then(|| self.tcx.hir_span(hir_id)),
                 },
             );
             return;
@@ -1176,7 +1193,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 errors::DocMaskedNotExternCrateSelf {
                     attr_span: meta.span(),
                     item_span: (attr.style() == AttrStyle::Outer)
-                        .then(|| self.tcx.hir().span(hir_id)),
+                        .then(|| self.tcx.hir_span(hir_id)),
                 },
             );
         }
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index f77e1db42d4..0060e726a8e 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -19,8 +19,8 @@ use rustc_middle::middle::privacy::Level;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_middle::{bug, span_bug};
-use rustc_session::lint;
 use rustc_session::lint::builtin::DEAD_CODE;
+use rustc_session::lint::{self, LintExpectationId};
 use rustc_span::{Symbol, sym};
 
 use crate::errors::{
@@ -421,10 +421,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
                 }
                 hir::ItemKind::ForeignMod { .. } => {}
                 hir::ItemKind::Trait(..) => {
-                    for impl_def_id in self.tcx.all_impls(item.owner_id.to_def_id()) {
-                        if let Some(local_def_id) = impl_def_id.as_local()
-                            && let ItemKind::Impl(impl_ref) =
-                                self.tcx.hir_expect_item(local_def_id).kind
+                    for &impl_def_id in self.tcx.local_trait_impls(item.owner_id.def_id) {
+                        if let ItemKind::Impl(impl_ref) = self.tcx.hir_expect_item(impl_def_id).kind
                         {
                             // skip items
                             // mark dependent traits live
@@ -698,8 +696,8 @@ fn has_allow_dead_code_or_lang_attr(
 
     fn has_allow_expect_dead_code(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
         let hir_id = tcx.local_def_id_to_hir_id(def_id);
-        let lint_level = tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).0;
-        matches!(lint_level, lint::Allow | lint::Expect(_))
+        let lint_level = tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).level;
+        matches!(lint_level, lint::Allow | lint::Expect)
     }
 
     fn has_used_like_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
@@ -917,7 +915,7 @@ fn live_symbols_and_ignored_derived_traits(
 struct DeadItem {
     def_id: LocalDefId,
     name: Symbol,
-    level: lint::Level,
+    level: (lint::Level, Option<LintExpectationId>),
 }
 
 struct DeadVisitor<'tcx> {
@@ -961,9 +959,10 @@ impl<'tcx> DeadVisitor<'tcx> {
         ShouldWarnAboutField::Yes
     }
 
-    fn def_lint_level(&self, id: LocalDefId) -> lint::Level {
+    fn def_lint_level(&self, id: LocalDefId) -> (lint::Level, Option<LintExpectationId>) {
         let hir_id = self.tcx.local_def_id_to_hir_id(id);
-        self.tcx.lint_level_at_node(DEAD_CODE, hir_id).0
+        let level = self.tcx.lint_level_at_node(DEAD_CODE, hir_id);
+        (level.level, level.lint_id)
     }
 
     // # Panics
@@ -1131,7 +1130,8 @@ impl<'tcx> DeadVisitor<'tcx> {
         if dead_codes.is_empty() {
             return;
         }
-        dead_codes.sort_by_key(|v| v.level);
+        // FIXME: `dead_codes` should probably be morally equivalent to `IndexMap<(Level, LintExpectationId), (DefId, Symbol)>`
+        dead_codes.sort_by_key(|v| v.level.0);
         for group in dead_codes.chunk_by(|a, b| a.level == b.level) {
             self.lint_at_single_level(&group, participle, Some(def_id), report_on);
         }
diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs
index e13d94c1031..17a729f422a 100644
--- a/compiler/rustc_passes/src/diagnostic_items.rs
+++ b/compiler/rustc_passes/src/diagnostic_items.rs
@@ -41,8 +41,8 @@ fn report_duplicate_item(
     original_def_id: DefId,
     item_def_id: DefId,
 ) {
-    let orig_span = tcx.hir().span_if_local(original_def_id);
-    let duplicate_span = tcx.hir().span_if_local(item_def_id);
+    let orig_span = tcx.hir_span_if_local(original_def_id);
+    let duplicate_span = tcx.hir_span_if_local(item_def_id);
     tcx.dcx().emit_err(DuplicateDiagnosticItemInCrate {
         duplicate_span,
         orig_span,
diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs
index d2729876ebb..2a435c4b2e0 100644
--- a/compiler/rustc_passes/src/entry.rs
+++ b/compiler/rustc_passes/src/entry.rs
@@ -24,7 +24,7 @@ struct EntryContext<'tcx> {
 }
 
 fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> {
-    let any_exe = tcx.crate_types().iter().any(|ty| *ty == CrateType::Executable);
+    let any_exe = tcx.crate_types().contains(&CrateType::Executable);
     if !any_exe {
         // No need to find a main function.
         return None;
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 0ee17430aab..4e3e0324205 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -1441,6 +1441,11 @@ pub(crate) struct OnlyHasEffectOn {
     pub target_name: String,
 }
 
+#[derive(LintDiagnostic)]
+#[diag(passes_inline_ignored_for_exported)]
+#[help]
+pub(crate) struct InlineIgnoredForExported {}
+
 #[derive(Diagnostic)]
 #[diag(passes_object_lifetime_err)]
 pub(crate) struct ObjectLifetimeErr {
diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs
index 9d4b46cd306..664bd4ad0a2 100644
--- a/compiler/rustc_passes/src/lang_items.rs
+++ b/compiler/rustc_passes/src/lang_items.rs
@@ -268,22 +268,22 @@ fn get_lang_items(tcx: TyCtxt<'_>, (): ()) -> LanguageItems {
 impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> {
     fn visit_item(&mut self, i: &'ast ast::Item) {
         let target = match &i.kind {
-            ast::ItemKind::ExternCrate(_) => Target::ExternCrate,
+            ast::ItemKind::ExternCrate(..) => Target::ExternCrate,
             ast::ItemKind::Use(_) => Target::Use,
             ast::ItemKind::Static(_) => Target::Static,
             ast::ItemKind::Const(_) => Target::Const,
             ast::ItemKind::Fn(_) | ast::ItemKind::Delegation(..) => Target::Fn,
-            ast::ItemKind::Mod(_, _) => Target::Mod,
+            ast::ItemKind::Mod(..) => Target::Mod,
             ast::ItemKind::ForeignMod(_) => Target::ForeignFn,
             ast::ItemKind::GlobalAsm(_) => Target::GlobalAsm,
             ast::ItemKind::TyAlias(_) => Target::TyAlias,
-            ast::ItemKind::Enum(_, _) => Target::Enum,
-            ast::ItemKind::Struct(_, _) => Target::Struct,
-            ast::ItemKind::Union(_, _) => Target::Union,
+            ast::ItemKind::Enum(..) => Target::Enum,
+            ast::ItemKind::Struct(..) => Target::Struct,
+            ast::ItemKind::Union(..) => Target::Union,
             ast::ItemKind::Trait(_) => Target::Trait,
-            ast::ItemKind::TraitAlias(_, _) => Target::TraitAlias,
+            ast::ItemKind::TraitAlias(..) => Target::TraitAlias,
             ast::ItemKind::Impl(_) => Target::Impl,
-            ast::ItemKind::MacroDef(_) => Target::MacroDef,
+            ast::ItemKind::MacroDef(..) => Target::MacroDef,
             ast::ItemKind::MacCall(_) | ast::ItemKind::DelegationMac(_) => {
                 unreachable!("macros should have been expanded")
             }
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 9e55914f8f2..d7baad69c78 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -726,7 +726,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index {
 
         annotator.annotate(
             CRATE_DEF_ID,
-            tcx.hir().span(CRATE_HIR_ID),
+            tcx.hir_span(CRATE_HIR_ID),
             None,
             AnnotationKind::Required,
             InheritDeprecation::Yes,
@@ -980,7 +980,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
                                     // Calculating message for lint involves calling `self.def_path_str`,
                                     // which will by default invoke the expensive `visible_parent_map` query.
                                     // Skip all that work if the lint is allowed anyway.
-                                    if self.tcx.lint_level_at_node(DEPRECATED, id).0
+                                    if self.tcx.lint_level_at_node(DEPRECATED, id).level
                                         == lint::Level::Allow
                                     {
                                         return;
@@ -1099,7 +1099,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
     if is_staged_api {
         let effective_visibilities = &tcx.effective_visibilities(());
         let mut missing = MissingStabilityAnnotations { tcx, effective_visibilities };
-        missing.check_missing_stability(CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID));
+        missing.check_missing_stability(CRATE_DEF_ID, tcx.hir_span(CRATE_HIR_ID));
         tcx.hir_walk_toplevel_module(&mut missing);
         tcx.hir_visit_all_item_likes_in_crate(&mut missing);
     }
diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs
index 585cda1d24b..3da744dc8c0 100644
--- a/compiler/rustc_pattern_analysis/src/lints.rs
+++ b/compiler/rustc_pattern_analysis/src/lints.rs
@@ -1,3 +1,4 @@
+use rustc_middle::lint::LevelAndSource;
 use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
 use rustc_span::ErrorGuaranteed;
 use tracing::instrument;
@@ -64,7 +65,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>(
     scrut_ty: RevealedTy<'tcx>,
 ) -> Result<(), ErrorGuaranteed> {
     if !matches!(
-        rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level).0,
+        rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level).level,
         rustc_session::lint::Level::Allow
     ) {
         let witnesses = collect_nonexhaustive_missing_variants(rcx, pat_column)?;
@@ -88,13 +89,13 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>(
         // arm. This no longer makes sense so we warn users, to avoid silently breaking their
         // usage of the lint.
         for arm in arms {
-            let (lint_level, lint_level_source) =
+            let LevelAndSource { level, src, .. } =
                 rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data);
-            if !matches!(lint_level, rustc_session::lint::Level::Allow) {
+            if !matches!(level, rustc_session::lint::Level::Allow) {
                 let decorator = NonExhaustiveOmittedPatternLintOnArm {
-                    lint_span: lint_level_source.span(),
+                    lint_span: src.span(),
                     suggest_lint_on_match: rcx.whole_match_span.map(|span| span.shrink_to_lo()),
-                    lint_level: lint_level.as_str(),
+                    lint_level: level.as_str(),
                     lint_name: "non_exhaustive_omitted_patterns",
                 };
 
diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs
index a25a80cd45f..31c4ee0fa0b 100644
--- a/compiler/rustc_pattern_analysis/src/rustc.rs
+++ b/compiler/rustc_pattern_analysis/src/rustc.rs
@@ -135,7 +135,10 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
     /// Returns the hidden type corresponding to this key if the body under analysis is allowed to
     /// know it.
     fn reveal_opaque_key(&self, key: OpaqueTypeKey<'tcx>) -> Option<Ty<'tcx>> {
-        self.typeck_results.concrete_opaque_types.get(&key).map(|x| x.ty)
+        self.typeck_results
+            .concrete_opaque_types
+            .get(&key.def_id)
+            .map(|x| ty::EarlyBinder::bind(x.ty).instantiate(self.tcx, key.args))
     }
     // This can take a non-revealed `Ty` because it reveals opaques itself.
     pub fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
index 3238c7a0912..19ccc5587d6 100644
--- a/compiler/rustc_query_impl/src/plumbing.rs
+++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -870,6 +870,17 @@ macro_rules! define_queries {
                 }
             }
 
+            pub(crate) fn AnonZeroDeps<'tcx>() -> DepKindStruct<'tcx> {
+                DepKindStruct {
+                    is_anon: true,
+                    is_eval_always: false,
+                    fingerprint_style: FingerprintStyle::Opaque,
+                    force_from_dep_node: Some(|_, _, _| bug!("cannot force an anon node")),
+                    try_load_from_on_disk_cache: None,
+                    name: &"AnonZeroDeps",
+                }
+            }
+
             pub(crate) fn TraitSelect<'tcx>() -> DepKindStruct<'tcx> {
                 DepKindStruct {
                     is_anon: true,
diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs
index 495f34733f7..9f34417973e 100644
--- a/compiler/rustc_query_system/src/dep_graph/graph.rs
+++ b/compiler/rustc_query_system/src/dep_graph/graph.rs
@@ -50,7 +50,7 @@ rustc_index::newtype_index! {
 rustc_data_structures::static_assert_size!(Option<DepNodeIndex>, 4);
 
 impl DepNodeIndex {
-    const SINGLETON_DEPENDENCYLESS_ANON_NODE: DepNodeIndex = DepNodeIndex::ZERO;
+    const SINGLETON_ZERO_DEPS_ANON_NODE: DepNodeIndex = DepNodeIndex::ZERO;
     pub const FOREVER_RED_NODE: DepNodeIndex = DepNodeIndex::from_u32(1);
 }
 
@@ -66,6 +66,7 @@ pub struct MarkFrame<'a> {
     parent: Option<&'a MarkFrame<'a>>,
 }
 
+#[derive(Debug)]
 pub(super) enum DepNodeColor {
     Red,
     Green(DepNodeIndex),
@@ -139,13 +140,13 @@ impl<D: Deps> DepGraph<D> {
 
         let colors = DepNodeColorMap::new(prev_graph_node_count);
 
-        // Instantiate a dependy-less node only once for anonymous queries.
+        // Instantiate a node with zero dependencies only once for anonymous queries.
         let _green_node_index = current.alloc_node(
-            DepNode { kind: D::DEP_KIND_NULL, hash: current.anon_id_seed.into() },
+            DepNode { kind: D::DEP_KIND_ANON_ZERO_DEPS, hash: current.anon_id_seed.into() },
             EdgesVec::new(),
             Fingerprint::ZERO,
         );
-        assert_eq!(_green_node_index, DepNodeIndex::SINGLETON_DEPENDENCYLESS_ANON_NODE);
+        assert_eq!(_green_node_index, DepNodeIndex::SINGLETON_ZERO_DEPS_ANON_NODE);
 
         // Instantiate a dependy-less red node only once for anonymous queries.
         let red_node_index = current.alloc_node(
@@ -406,7 +407,7 @@ impl<D: Deps> DepGraphData<D> {
                 // going to be (i.e. equal to the precomputed
                 // `SINGLETON_DEPENDENCYLESS_ANON_NODE`). As a consequence we can skip creating
                 // a `StableHasher` and sending the node through interning.
-                DepNodeIndex::SINGLETON_DEPENDENCYLESS_ANON_NODE
+                DepNodeIndex::SINGLETON_ZERO_DEPS_ANON_NODE
             }
             1 => {
                 // When there is only one dependency, don't bother creating a node.
@@ -909,7 +910,7 @@ impl<D: Deps> DepGraphData<D> {
                 self.try_mark_previous_green(qcx, parent_dep_node_index, dep_dep_node, frame);
 
             if node_index.is_some() {
-                debug!("managed to MARK dependency {dep_dep_node:?} as green",);
+                debug!("managed to MARK dependency {dep_dep_node:?} as green");
                 return Some(());
             }
         }
@@ -930,7 +931,7 @@ impl<D: Deps> DepGraphData<D> {
                 return Some(());
             }
             Some(DepNodeColor::Red) => {
-                debug!("dependency {dep_dep_node:?} was red after forcing",);
+                debug!("dependency {dep_dep_node:?} was red after forcing");
                 return None;
             }
             None => {}
@@ -950,7 +951,7 @@ impl<D: Deps> DepGraphData<D> {
         // invalid state will not be persisted to the
         // incremental compilation cache because of
         // compilation errors being present.
-        debug!("dependency {dep_dep_node:?} resulted in compilation error",);
+        debug!("dependency {dep_dep_node:?} resulted in compilation error");
         return None;
     }
 
diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs
index 4eeb6078d14..3a80835afad 100644
--- a/compiler/rustc_query_system/src/dep_graph/mod.rs
+++ b/compiler/rustc_query_system/src/dep_graph/mod.rs
@@ -111,6 +111,9 @@ pub trait Deps {
     /// We use this to create a side effect node.
     const DEP_KIND_SIDE_EFFECT: DepKind;
 
+    /// We use this to create the anon node with zero dependencies.
+    const DEP_KIND_ANON_ZERO_DEPS: DepKind;
+
     /// This is the highest value a `DepKind` can have. It's used during encoding to
     /// pack information into the unused bits.
     const DEP_KIND_MAX: u16;
diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml
index f4771f1af2c..9ea9c58cfd1 100644
--- a/compiler/rustc_resolve/Cargo.toml
+++ b/compiler/rustc_resolve/Cargo.toml
@@ -6,6 +6,7 @@ edition = "2024"
 [dependencies]
 # tidy-alphabetical-start
 bitflags = "2.4.1"
+itertools = "0.12"
 pulldown-cmark = { version = "0.11", features = ["html"], default-features = false }
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 97fbf7e378a..4368f7882ff 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -10,8 +10,8 @@ use std::sync::Arc;
 
 use rustc_ast::visit::{self, AssocCtxt, Visitor, WalkItemKind};
 use rustc_ast::{
-    self as ast, AssocItem, AssocItemKind, Block, ForeignItem, ForeignItemKind, Impl, Item,
-    ItemKind, MetaItemKind, NodeId, StmtKind,
+    self as ast, AssocItem, AssocItemKind, Block, ConstItem, Delegation, Fn, ForeignItem,
+    ForeignItemKind, Impl, Item, ItemKind, MetaItemKind, NodeId, StaticItem, StmtKind, TyAlias,
 };
 use rustc_attr_parsing as attr;
 use rustc_expand::base::ResolverExpand;
@@ -735,7 +735,6 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
         let parent_scope = &self.parent_scope;
         let parent = parent_scope.module;
         let expansion = parent_scope.expansion;
-        let ident = item.ident;
         let sp = item.span;
         let vis = self.resolve_visibility(&item.vis);
         let feed = self.r.feed(item.id);
@@ -762,17 +761,18 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
                 );
             }
 
-            ItemKind::ExternCrate(orig_name) => {
+            ItemKind::ExternCrate(orig_name, ident) => {
                 self.build_reduced_graph_for_extern_crate(
                     orig_name,
                     item,
+                    ident,
                     local_def_id,
                     vis,
                     parent,
                 );
             }
 
-            ItemKind::Mod(.., ref mod_kind) => {
+            ItemKind::Mod(_, ident, ref mod_kind) => {
                 let module = self.r.new_module(
                     Some(parent),
                     ModuleKind::Def(def_kind, def_id, Some(ident.name)),
@@ -792,10 +792,12 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
             }
 
             // These items live in the value namespace.
-            ItemKind::Const(..) | ItemKind::Delegation(..) | ItemKind::Static(..) => {
+            ItemKind::Const(box ConstItem { ident, .. })
+            | ItemKind::Delegation(box Delegation { ident, .. })
+            | ItemKind::Static(box StaticItem { ident, .. }) => {
                 self.r.define(parent, ident, ValueNS, (res, vis, sp, expansion));
             }
-            ItemKind::Fn(..) => {
+            ItemKind::Fn(box Fn { ident, .. }) => {
                 self.r.define(parent, ident, ValueNS, (res, vis, sp, expansion));
 
                 // Functions introducing procedural macros reserve a slot
@@ -804,11 +806,11 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
             }
 
             // These items live in the type namespace.
-            ItemKind::TyAlias(..) | ItemKind::TraitAlias(..) => {
+            ItemKind::TyAlias(box TyAlias { ident, .. }) | ItemKind::TraitAlias(ident, ..) => {
                 self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion));
             }
 
-            ItemKind::Enum(_, _) | ItemKind::Trait(..) => {
+            ItemKind::Enum(ident, _, _) | ItemKind::Trait(box ast::Trait { ident, .. }) => {
                 let module = self.r.new_module(
                     Some(parent),
                     ModuleKind::Def(def_kind, def_id, Some(ident.name)),
@@ -821,7 +823,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
             }
 
             // These items live in both the type and value namespaces.
-            ItemKind::Struct(ref vdata, _) => {
+            ItemKind::Struct(ident, ref vdata, _) => {
                 self.build_reduced_graph_for_struct_variant(
                     vdata.fields(),
                     ident,
@@ -872,7 +874,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
                 }
             }
 
-            ItemKind::Union(ref vdata, _) => {
+            ItemKind::Union(ident, ref vdata, _) => {
                 self.build_reduced_graph_for_struct_variant(
                     vdata.fields(),
                     ident,
@@ -899,11 +901,11 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
         &mut self,
         orig_name: Option<Symbol>,
         item: &Item,
+        ident: Ident,
         local_def_id: LocalDefId,
         vis: ty::Visibility,
         parent: Module<'ra>,
     ) {
-        let ident = item.ident;
         let sp = item.span;
         let parent_scope = self.parent_scope;
         let expansion = parent_scope.expansion;
@@ -987,7 +989,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
     }
 
     /// Constructs the reduced graph for one foreign item.
-    fn build_reduced_graph_for_foreign_item(&mut self, item: &ForeignItem) {
+    fn build_reduced_graph_for_foreign_item(&mut self, item: &ForeignItem, ident: Ident) {
         let feed = self.r.feed(item.id);
         let local_def_id = feed.key();
         let def_id = local_def_id.to_def_id();
@@ -1000,7 +1002,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
         let parent = self.parent_scope.module;
         let expansion = self.parent_scope.expansion;
         let vis = self.resolve_visibility(&item.vis);
-        self.r.define(parent, item.ident, ns, (self.res(def_id), vis, item.span, expansion));
+        self.r.define(parent, ident, ns, (self.res(def_id), vis, item.span, expansion));
         self.r.feed_visibility(feed, vis);
     }
 
@@ -1043,7 +1045,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
                         span: item.span,
                     });
                 }
-                if let ItemKind::ExternCrate(Some(orig_name)) = item.kind
+                if let ItemKind::ExternCrate(Some(orig_name), _) = item.kind
                     && orig_name == kw::SelfLower
                 {
                     self.r.dcx().emit_err(errors::MacroUseExternCrateSelf { span: attr.span });
@@ -1177,11 +1179,15 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
         self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Invocation(invoc_id))
     }
 
-    fn proc_macro_stub(&self, item: &ast::Item) -> Option<(MacroKind, Ident, Span)> {
+    fn proc_macro_stub(
+        &self,
+        item: &ast::Item,
+        fn_ident: Ident,
+    ) -> Option<(MacroKind, Ident, Span)> {
         if ast::attr::contains_name(&item.attrs, sym::proc_macro) {
-            return Some((MacroKind::Bang, item.ident, item.span));
+            return Some((MacroKind::Bang, fn_ident, item.span));
         } else if ast::attr::contains_name(&item.attrs, sym::proc_macro_attribute) {
-            return Some((MacroKind::Attr, item.ident, item.span));
+            return Some((MacroKind::Attr, fn_ident, item.span));
         } else if let Some(attr) = ast::attr::find_by_name(&item.attrs, sym::proc_macro_derive)
             && let Some(meta_item_inner) =
                 attr.meta_item_list().and_then(|list| list.get(0).cloned())
@@ -1214,17 +1220,21 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
         let feed = self.r.feed(item.id);
         let def_id = feed.key();
         let (res, ident, span, macro_rules) = match &item.kind {
-            ItemKind::MacroDef(def) => (self.res(def_id), item.ident, item.span, def.macro_rules),
-            ItemKind::Fn(..) => match self.proc_macro_stub(item) {
-                Some((macro_kind, ident, span)) => {
-                    let res = Res::Def(DefKind::Macro(macro_kind), def_id.to_def_id());
-                    let macro_data = MacroData::new(self.r.dummy_ext(macro_kind));
-                    self.r.macro_map.insert(def_id.to_def_id(), macro_data);
-                    self.r.proc_macro_stubs.insert(def_id);
-                    (res, ident, span, false)
+            ItemKind::MacroDef(ident, def) => {
+                (self.res(def_id), *ident, item.span, def.macro_rules)
+            }
+            ItemKind::Fn(box ast::Fn { ident: fn_ident, .. }) => {
+                match self.proc_macro_stub(item, *fn_ident) {
+                    Some((macro_kind, ident, span)) => {
+                        let res = Res::Def(DefKind::Macro(macro_kind), def_id.to_def_id());
+                        let macro_data = MacroData::new(self.r.dummy_ext(macro_kind));
+                        self.r.macro_map.insert(def_id.to_def_id(), macro_data);
+                        self.r.proc_macro_stubs.insert(def_id);
+                        (res, ident, span, false)
+                    }
+                    None => return parent_scope.macro_rules,
                 }
-                None => return parent_scope.macro_rules,
-            },
+            }
             _ => unreachable!(),
         };
 
@@ -1327,8 +1337,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
                         // Visit attributes after items for backward compatibility.
                         // This way they can use `macro_rules` defined later.
                         self.visit_vis(&item.vis);
-                        self.visit_ident(&item.ident);
-                        item.kind.walk(item.span, item.id, &item.ident, &item.vis, (), self);
+                        item.kind.walk(item.span, item.id, &item.vis, (), self);
                         visit::walk_list!(self, visit_attribute, &item.attrs);
                     }
                     _ => visit::walk_item(self, item),
@@ -1353,12 +1362,17 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
     }
 
     fn visit_foreign_item(&mut self, foreign_item: &'a ForeignItem) {
-        if let ForeignItemKind::MacCall(_) = foreign_item.kind {
-            self.visit_invoc_in_module(foreign_item.id);
-            return;
-        }
+        let ident = match foreign_item.kind {
+            ForeignItemKind::Static(box StaticItem { ident, .. })
+            | ForeignItemKind::Fn(box Fn { ident, .. })
+            | ForeignItemKind::TyAlias(box TyAlias { ident, .. }) => ident,
+            ForeignItemKind::MacCall(_) => {
+                self.visit_invoc_in_module(foreign_item.id);
+                return;
+            }
+        };
 
-        self.build_reduced_graph_for_foreign_item(foreign_item);
+        self.build_reduced_graph_for_foreign_item(foreign_item, ident);
         visit::walk_item(self, foreign_item);
     }
 
@@ -1372,26 +1386,35 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
     }
 
     fn visit_assoc_item(&mut self, item: &'a AssocItem, ctxt: AssocCtxt) {
-        if let AssocItemKind::MacCall(_) = item.kind {
-            match ctxt {
-                AssocCtxt::Trait => {
-                    self.visit_invoc_in_module(item.id);
-                }
-                AssocCtxt::Impl { .. } => {
-                    let invoc_id = item.id.placeholder_to_expn_id();
-                    if !self.r.glob_delegation_invoc_ids.contains(&invoc_id) {
-                        self.r
-                            .impl_unexpanded_invocations
-                            .entry(self.r.invocation_parent(invoc_id))
-                            .or_default()
-                            .insert(invoc_id);
+        let (ident, ns) = match item.kind {
+            AssocItemKind::Const(box ConstItem { ident, .. })
+            | AssocItemKind::Fn(box Fn { ident, .. })
+            | AssocItemKind::Delegation(box Delegation { ident, .. }) => (ident, ValueNS),
+
+            AssocItemKind::Type(box TyAlias { ident, .. }) => (ident, TypeNS),
+
+            AssocItemKind::MacCall(_) => {
+                match ctxt {
+                    AssocCtxt::Trait => {
+                        self.visit_invoc_in_module(item.id);
+                    }
+                    AssocCtxt::Impl { .. } => {
+                        let invoc_id = item.id.placeholder_to_expn_id();
+                        if !self.r.glob_delegation_invoc_ids.contains(&invoc_id) {
+                            self.r
+                                .impl_unexpanded_invocations
+                                .entry(self.r.invocation_parent(invoc_id))
+                                .or_default()
+                                .insert(invoc_id);
+                        }
+                        self.visit_invoc(item.id);
                     }
-                    self.visit_invoc(item.id);
                 }
+                return;
             }
-            return;
-        }
 
+            AssocItemKind::DelegationMac(..) => bug!(),
+        };
         let vis = self.resolve_visibility(&item.vis);
         let feed = self.r.feed(item.id);
         let local_def_id = feed.key();
@@ -1406,20 +1429,13 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
             self.r.feed_visibility(feed, vis);
         }
 
-        let ns = match item.kind {
-            AssocItemKind::Const(..) | AssocItemKind::Delegation(..) | AssocItemKind::Fn(..) => {
-                ValueNS
-            }
-            AssocItemKind::Type(..) => TypeNS,
-            AssocItemKind::MacCall(_) | AssocItemKind::DelegationMac(..) => bug!(), // handled above
-        };
         if ctxt == AssocCtxt::Trait {
             let parent = self.parent_scope.module;
             let expansion = self.parent_scope.expansion;
-            self.r.define(parent, item.ident, ns, (self.res(def_id), vis, item.span, expansion));
+            self.r.define(parent, ident, ns, (self.res(def_id), vis, item.span, expansion));
         } else if !matches!(&item.kind, AssocItemKind::Delegation(deleg) if deleg.from_glob) {
             let impl_def_id = self.r.tcx.local_parent(local_def_id);
-            let key = BindingKey::new(item.ident.normalize_to_macros_2_0(), ns);
+            let key = BindingKey::new(ident.normalize_to_macros_2_0(), ns);
             self.r.impl_binding_keys.entry(impl_def_id).or_default().insert(key);
         }
 
diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs
index 51ff4aa834b..e97233e97ce 100644
--- a/compiler/rustc_resolve/src/check_unused.rs
+++ b/compiler/rustc_resolve/src/check_unused.rs
@@ -219,14 +219,14 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'ra, 'tcx> {
             // because this means that they were generated in some fashion by the
             // compiler and we don't need to consider them.
             ast::ItemKind::Use(..) if item.span.is_dummy() => return,
-            ast::ItemKind::ExternCrate(orig_name) => {
+            ast::ItemKind::ExternCrate(orig_name, ident) => {
                 self.extern_crate_items.push(ExternCrateToLint {
                     id: item.id,
                     span: item.span,
                     vis_span: item.vis.span,
                     span_with_attributes: item.span_with_attributes(),
                     has_attrs: !item.attrs.is_empty(),
-                    ident: item.ident,
+                    ident,
                     renames: orig_name.is_some(),
                 });
             }
diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs
index 6f48a75d617..13dfb59f27f 100644
--- a/compiler/rustc_resolve/src/def_collector.rs
+++ b/compiler/rustc_resolve/src/def_collector.rs
@@ -122,7 +122,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
             },
             ItemKind::Const(..) => DefKind::Const,
             ItemKind::Fn(..) | ItemKind::Delegation(..) => DefKind::Fn,
-            ItemKind::MacroDef(def) => {
+            ItemKind::MacroDef(ident, def) => {
                 let edition = i.span.edition();
 
                 // FIXME(jdonszelmann) make one of these in the resolver?
@@ -141,7 +141,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
                 );
 
                 let macro_data =
-                    self.resolver.compile_macro(def, i.ident, &attrs, i.span, i.id, edition);
+                    self.resolver.compile_macro(def, *ident, &attrs, i.span, i.id, edition);
                 let macro_kind = macro_data.ext.macro_kind();
                 opt_macro_data = Some(macro_data);
                 DefKind::Macro(macro_kind)
@@ -152,7 +152,8 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
                 return self.visit_macro_invoc(i.id);
             }
         };
-        let def_id = self.create_def(i.id, Some(i.ident.name), def_kind, i.span);
+        let def_id =
+            self.create_def(i.id, i.kind.ident().map(|ident| ident.name), def_kind, i.span);
 
         if let Some(macro_data) = opt_macro_data {
             self.resolver.macro_map.insert(def_id.to_def_id(), macro_data);
@@ -161,7 +162,8 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
         self.with_parent(def_id, |this| {
             this.with_impl_trait(ImplTraitContext::Existential, |this| {
                 match i.kind {
-                    ItemKind::Struct(ref struct_def, _) | ItemKind::Union(ref struct_def, _) => {
+                    ItemKind::Struct(_, ref struct_def, _)
+                    | ItemKind::Union(_, ref struct_def, _) => {
                         // If this is a unit or tuple-like struct, register the constructor.
                         if let Some((ctor_kind, ctor_node_id)) = CtorKind::from_ast(struct_def) {
                             this.create_def(
@@ -183,10 +185,12 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
         match fn_kind {
             FnKind::Fn(
                 _ctxt,
-                _ident,
                 _vis,
-                Fn { sig: FnSig { header, decl, span: _ }, generics, contract, body, .. },
+                Fn {
+                    sig: FnSig { header, decl, span: _ }, ident, generics, contract, body, ..
+                },
             ) if let Some(coroutine_kind) = header.coroutine_kind => {
+                self.visit_ident(ident);
                 self.visit_fn_header(header);
                 self.visit_generics(generics);
                 if let Some(contract) = contract {
@@ -234,8 +238,9 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
     }
 
     fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
-        let def_kind = match fi.kind {
+        let (ident, def_kind) = match fi.kind {
             ForeignItemKind::Static(box StaticItem {
+                ident,
                 ty: _,
                 mutability,
                 expr: _,
@@ -247,14 +252,14 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
                     ast::Safety::Safe(_) => hir::Safety::Safe,
                 };
 
-                DefKind::Static { safety, mutability, nested: false }
+                (ident, DefKind::Static { safety, mutability, nested: false })
             }
-            ForeignItemKind::Fn(_) => DefKind::Fn,
-            ForeignItemKind::TyAlias(_) => DefKind::ForeignTy,
+            ForeignItemKind::Fn(box Fn { ident, .. }) => (ident, DefKind::Fn),
+            ForeignItemKind::TyAlias(box TyAlias { ident, .. }) => (ident, DefKind::ForeignTy),
             ForeignItemKind::MacCall(_) => return self.visit_macro_invoc(fi.id),
         };
 
-        let def = self.create_def(fi.id, Some(fi.ident.name), def_kind, fi.span);
+        let def = self.create_def(fi.id, Some(ident.name), def_kind, fi.span);
 
         self.with_parent(def, |this| visit::walk_item(this, fi));
     }
@@ -318,16 +323,17 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
     }
 
     fn visit_assoc_item(&mut self, i: &'a AssocItem, ctxt: visit::AssocCtxt) {
-        let def_kind = match &i.kind {
-            AssocItemKind::Fn(..) | AssocItemKind::Delegation(..) => DefKind::AssocFn,
-            AssocItemKind::Const(..) => DefKind::AssocConst,
-            AssocItemKind::Type(..) => DefKind::AssocTy,
+        let (ident, def_kind) = match &i.kind {
+            AssocItemKind::Fn(box Fn { ident, .. })
+            | AssocItemKind::Delegation(box Delegation { ident, .. }) => (*ident, DefKind::AssocFn),
+            AssocItemKind::Const(box ConstItem { ident, .. }) => (*ident, DefKind::AssocConst),
+            AssocItemKind::Type(box TyAlias { ident, .. }) => (*ident, DefKind::AssocTy),
             AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
                 return self.visit_macro_invoc(i.id);
             }
         };
 
-        let def = self.create_def(i.id, Some(i.ident.name), def_kind, i.span);
+        let def = self.create_def(i.id, Some(ident.name), def_kind, i.span);
         self.with_parent(def, |this| visit::walk_assoc_item(this, i, ctxt));
     }
 
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 63ea8c4ced1..6dc854758da 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -1091,7 +1091,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                     ));
                 }
                 Scope::BuiltinAttrs => {
-                    let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin(kw::Empty));
+                    let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin(sym::dummy));
                     if filter_fn(res) {
                         suggestions.extend(
                             BUILTIN_ATTRIBUTES
@@ -1325,11 +1325,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             })
         }
 
-        // If only some candidates are accessible, take just them
-        if !candidates.iter().all(|v: &ImportSuggestion| !v.accessible) {
-            candidates.retain(|x| x.accessible)
-        }
-
         candidates
     }
 
@@ -1793,7 +1788,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                 &import_suggestions,
                 Instead::Yes,
                 FoundUse::Yes,
-                DiagMode::Import { append: single_nested },
+                DiagMode::Import { append: single_nested, unresolved_import: false },
                 vec![],
                 "",
             );
@@ -2750,6 +2745,8 @@ pub(crate) enum DiagMode {
     Pattern,
     /// The binding is part of a use statement
     Import {
+        /// `true` means diagnostics is for unresolved import
+        unresolved_import: bool,
         /// `true` mean add the tips afterward for case `use a::{b,c}`,
         /// rather than replacing within.
         append: bool,
@@ -2800,6 +2797,7 @@ fn show_candidates(
         return false;
     }
 
+    let mut showed = false;
     let mut accessible_path_strings: Vec<PathString<'_>> = Vec::new();
     let mut inaccessible_path_strings: Vec<PathString<'_>> = Vec::new();
 
@@ -2958,8 +2956,11 @@ fn show_candidates(
             append_candidates(&mut msg, accessible_path_strings);
             err.help(msg);
         }
-        true
-    } else if !(inaccessible_path_strings.is_empty() || matches!(mode, DiagMode::Import { .. })) {
+        showed = true;
+    }
+    if !inaccessible_path_strings.is_empty()
+        && (!matches!(mode, DiagMode::Import { unresolved_import: false, .. }))
+    {
         let prefix =
             if let DiagMode::Pattern = mode { "you might have meant to match on " } else { "" };
         if let [(name, descr, source_span, note, _)] = &inaccessible_path_strings[..] {
@@ -3022,10 +3023,9 @@ fn show_candidates(
 
             err.span_note(multi_span, msg);
         }
-        true
-    } else {
-        false
+        showed = true;
     }
+    showed
 }
 
 #[derive(Debug)]
@@ -3063,7 +3063,7 @@ impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder {
 
     fn visit_item(&mut self, item: &'tcx ast::Item) {
         if self.target_module == item.id {
-            if let ItemKind::Mod(_, ModKind::Loaded(items, _inline, mod_spans, _)) = &item.kind {
+            if let ItemKind::Mod(_, _, ModKind::Loaded(items, _inline, mod_spans, _)) = &item.kind {
                 let inject = mod_spans.inject_use_span;
                 if is_span_suitable_for_use_injection(inject) {
                     self.first_legal_span = Some(inject);
diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs
index 6ef4aa40725..a5ca4565d7b 100644
--- a/compiler/rustc_resolve/src/effective_visibilities.rs
+++ b/compiler/rustc_resolve/src/effective_visibilities.rs
@@ -252,7 +252,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx>
                 self.current_private_vis = prev_private_vis;
             }
 
-            ast::ItemKind::Enum(EnumDef { ref variants }, _) => {
+            ast::ItemKind::Enum(_, EnumDef { ref variants }, _) => {
                 self.set_bindings_effective_visibilities(def_id);
                 for variant in variants {
                     let variant_def_id = self.r.local_def_id(variant.id);
@@ -262,7 +262,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx>
                 }
             }
 
-            ast::ItemKind::Struct(ref def, _) | ast::ItemKind::Union(ref def, _) => {
+            ast::ItemKind::Struct(_, ref def, _) | ast::ItemKind::Union(_, ref def, _) => {
                 for field in def.fields() {
                     self.update_field(self.r.local_def_id(field.id), def_id);
                 }
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 454460e10dc..0b3633a452c 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -734,7 +734,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                         &mut diag,
                         Some(err.span),
                         candidates,
-                        DiagMode::Import { append: false },
+                        DiagMode::Import { append: false, unresolved_import: true },
                         (source != target)
                             .then(|| format!(" as {target}"))
                             .as_deref()
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 533e216ddb2..20e19caf909 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -1025,9 +1025,10 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
         match fn_kind {
             // Bail if the function is foreign, and thus cannot validly have
             // a body, or if there's no body for some other reason.
-            FnKind::Fn(FnCtxt::Foreign, _, _, Fn { sig, generics, .. })
-            | FnKind::Fn(_, _, _, Fn { sig, generics, body: None, .. }) => {
+            FnKind::Fn(FnCtxt::Foreign, _, Fn { sig, ident, generics, .. })
+            | FnKind::Fn(_, _, Fn { sig, ident, generics, body: None, .. }) => {
                 self.visit_fn_header(&sig.header);
+                self.visit_ident(ident);
                 self.visit_generics(generics);
                 self.with_lifetime_rib(
                     LifetimeRibKind::AnonymousCreateParameter {
@@ -1058,7 +1059,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
             // Create a label rib for the function.
             this.with_label_rib(RibKind::FnOrCoroutine, |this| {
                 match fn_kind {
-                    FnKind::Fn(_, _, _, Fn { sig, generics, contract, body, .. }) => {
+                    FnKind::Fn(_, _, Fn { sig, generics, contract, body, .. }) => {
                         this.visit_generics(generics);
 
                         let declaration = &sig.decl;
@@ -1833,7 +1834,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 }
                 LifetimeRibKind::StaticIfNoLifetimeInScope { lint_id: node_id, emit_lint } => {
                     let mut lifetimes_in_scope = vec![];
-                    for rib in &self.lifetime_ribs[..i] {
+                    for rib in self.lifetime_ribs[..i].iter().rev() {
                         lifetimes_in_scope.extend(rib.bindings.iter().map(|(ident, _)| ident.span));
                         // Consider any anonymous lifetimes, too
                         if let LifetimeRibKind::AnonymousCreateParameter { binder, .. } = rib.kind
@@ -1841,6 +1842,9 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                         {
                             lifetimes_in_scope.extend(extra.iter().map(|(ident, _, _)| ident.span));
                         }
+                        if let LifetimeRibKind::Item = rib.kind {
+                            break;
+                        }
                     }
                     if lifetimes_in_scope.is_empty() {
                         self.record_lifetime_res(
@@ -2629,8 +2633,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
             self.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id));
         }
 
-        let name = item.ident.name;
-        debug!("(resolving item) resolving {} ({:?})", name, item.kind);
+        debug!("(resolving item) resolving {:?} ({:?})", item.kind.ident(), item.kind);
 
         let def_kind = self.r.local_def_kind(item.id);
         match item.kind {
@@ -2661,9 +2664,9 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 self.resolve_define_opaques(define_opaque);
             }
 
-            ItemKind::Enum(_, ref generics)
-            | ItemKind::Struct(_, ref generics)
-            | ItemKind::Union(_, ref generics) => {
+            ItemKind::Enum(_, _, ref generics)
+            | ItemKind::Struct(_, _, ref generics)
+            | ItemKind::Union(_, _, ref generics) => {
                 self.resolve_adt(item, generics);
             }
 
@@ -2707,7 +2710,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 );
             }
 
-            ItemKind::TraitAlias(ref generics, ref bounds) => {
+            ItemKind::TraitAlias(_, ref generics, ref bounds) => {
                 // Create a new rib for the trait-wide type parameters.
                 self.with_generic_param_rib(
                     &generics.params,
@@ -2745,7 +2748,11 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
             }
 
             ItemKind::Static(box ast::StaticItem {
-                ref ty, ref expr, ref define_opaque, ..
+                ident,
+                ref ty,
+                ref expr,
+                ref define_opaque,
+                ..
             }) => {
                 self.with_static_rib(def_kind, |this| {
                     this.with_lifetime_rib(
@@ -2759,13 +2766,14 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                     if let Some(expr) = expr {
                         // We already forbid generic params because of the above item rib,
                         // so it doesn't matter whether this is a trivial constant.
-                        this.resolve_const_body(expr, Some((item.ident, ConstantItemKind::Static)));
+                        this.resolve_const_body(expr, Some((ident, ConstantItemKind::Static)));
                     }
                 });
                 self.resolve_define_opaques(define_opaque);
             }
 
             ItemKind::Const(box ast::ConstItem {
+                ident,
                 ref generics,
                 ref ty,
                 ref expr,
@@ -2798,10 +2806,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                         );
 
                         if let Some(expr) = expr {
-                            this.resolve_const_body(
-                                expr,
-                                Some((item.ident, ConstantItemKind::Const)),
-                            );
+                            this.resolve_const_body(expr, Some((ident, ConstantItemKind::Const)));
                         }
                     },
                 );
@@ -2818,7 +2823,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 self.future_proof_import(use_tree);
             }
 
-            ItemKind::MacroDef(ref macro_def) => {
+            ItemKind::MacroDef(_, ref macro_def) => {
                 // Maintain macro_rules scopes in the same way as during early resolution
                 // for diagnostics and doc links.
                 if macro_def.macro_rules {
@@ -3316,7 +3321,12 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
         self.resolve_doc_links(&item.attrs, MaybeExported::ImplItem(trait_id.ok_or(&item.vis)));
         match &item.kind {
             AssocItemKind::Const(box ast::ConstItem {
-                generics, ty, expr, define_opaque, ..
+                ident,
+                generics,
+                ty,
+                expr,
+                define_opaque,
+                ..
             }) => {
                 debug!("resolve_implementation AssocItemKind::Const");
                 self.with_generic_param_rib(
@@ -3347,7 +3357,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                                         // exists in trait
                                         this.check_trait_item(
                                             item.id,
-                                            item.ident,
+                                            *ident,
                                             &item.kind,
                                             ValueNS,
                                             item.span,
@@ -3373,7 +3383,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 );
                 self.resolve_define_opaques(define_opaque);
             }
-            AssocItemKind::Fn(box Fn { generics, define_opaque, .. }) => {
+            AssocItemKind::Fn(box Fn { ident, generics, define_opaque, .. }) => {
                 debug!("resolve_implementation AssocItemKind::Fn");
                 // We also need a new scope for the impl item type parameters.
                 self.with_generic_param_rib(
@@ -3389,7 +3399,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                         // exists in trait
                         this.check_trait_item(
                             item.id,
-                            item.ident,
+                            *ident,
                             &item.kind,
                             ValueNS,
                             item.span,
@@ -3403,7 +3413,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
 
                 self.resolve_define_opaques(define_opaque);
             }
-            AssocItemKind::Type(box TyAlias { generics, .. }) => {
+            AssocItemKind::Type(box TyAlias { ident, generics, .. }) => {
                 self.diag_metadata.in_non_gat_assoc_type = Some(generics.params.is_empty());
                 debug!("resolve_implementation AssocItemKind::Type");
                 // We also need a new scope for the impl item type parameters.
@@ -3421,7 +3431,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                             // exists in trait
                             this.check_trait_item(
                                 item.id,
-                                item.ident,
+                                *ident,
                                 &item.kind,
                                 TypeNS,
                                 item.span,
@@ -3448,7 +3458,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                     |this| {
                         this.check_trait_item(
                             item.id,
-                            item.ident,
+                            delegation.ident,
                             &item.kind,
                             ValueNS,
                             item.span,
@@ -4334,7 +4344,13 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 if let Some(items) = self.diag_metadata.current_trait_assoc_items
                     && let [Segment { ident, .. }] = path
                     && items.iter().any(|item| {
-                        item.ident == *ident && matches!(item.kind, AssocItemKind::Type(_))
+                        if let AssocItemKind::Type(alias) = &item.kind
+                            && alias.ident == *ident
+                        {
+                            true
+                        } else {
+                            false
+                        }
                     })
                 {
                     let mut diag = self.r.tcx.dcx().struct_allow("");
@@ -5156,12 +5172,12 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> {
             ItemKind::TyAlias(box TyAlias { generics, .. })
             | ItemKind::Const(box ConstItem { generics, .. })
             | ItemKind::Fn(box Fn { generics, .. })
-            | ItemKind::Enum(_, generics)
-            | ItemKind::Struct(_, generics)
-            | ItemKind::Union(_, generics)
+            | ItemKind::Enum(_, _, generics)
+            | ItemKind::Struct(_, _, generics)
+            | ItemKind::Union(_, _, generics)
             | ItemKind::Impl(box Impl { generics, .. })
             | ItemKind::Trait(box Trait { generics, .. })
-            | ItemKind::TraitAlias(generics, _) => {
+            | ItemKind::TraitAlias(_, generics, _) => {
                 if let ItemKind::Fn(box Fn { sig, .. }) = &item.kind {
                     self.collect_fn_info(sig.header, &sig.decl, item.id, &item.attrs);
                 }
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index e40f84e7e59..b62bc6c45e0 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -224,12 +224,13 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                 let suggestion = if self.current_trait_ref.is_none()
                     && let Some((fn_kind, _)) = self.diag_metadata.current_function
                     && let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt()
-                    && let FnKind::Fn(_, _, _, ast::Fn { sig, .. }) = fn_kind
+                    && let FnKind::Fn(_, _, ast::Fn { sig, .. }) = fn_kind
                     && let Some(items) = self.diag_metadata.current_impl_items
                     && let Some(item) = items.iter().find(|i| {
-                        i.ident.name == item_str.name
+                        i.kind.ident().is_some_and(|ident| {
                             // Don't suggest if the item is in Fn signature arguments (#112590).
-                            && !sig.span.contains(item_span)
+                            ident.name == item_str.name && !sig.span.contains(item_span)
+                        })
                     }) {
                     let sp = item_span.shrink_to_lo();
 
@@ -268,14 +269,14 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                             // you can't call `fn foo(&self)` from `fn bar()` (#115992).
                             // We also want to mention that the method exists.
                             span_label = Some((
-                                item.ident.span,
+                                fn_.ident.span,
                                 "a method by that name is available on `Self` here",
                             ));
                             None
                         }
                         AssocItemKind::Fn(fn_) if !fn_.sig.decl.has_self() && !is_call => {
                             span_label = Some((
-                                item.ident.span,
+                                fn_.ident.span,
                                 "an associated function by that name is available on `Self` here",
                             ));
                             None
@@ -604,7 +605,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                 Applicability::MaybeIncorrect,
             );
             if !self.self_value_is_available(path[0].ident.span) {
-                if let Some((FnKind::Fn(_, _, _, ast::Fn { sig, .. }), fn_span)) =
+                if let Some((FnKind::Fn(_, _, ast::Fn { sig, .. }), fn_span)) =
                     &self.diag_metadata.current_function
                 {
                     let (span, sugg) = if let Some(param) = sig.decl.inputs.get(0) {
@@ -1064,15 +1065,11 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
         }
         err.code(E0411);
         err.span_label(span, "`Self` is only available in impls, traits, and type definitions");
-        if let Some(item_kind) = self.diag_metadata.current_item {
-            if !item_kind.ident.span.is_dummy() {
+        if let Some(item) = self.diag_metadata.current_item {
+            if let Some(ident) = item.kind.ident() {
                 err.span_label(
-                    item_kind.ident.span,
-                    format!(
-                        "`Self` not allowed in {} {}",
-                        item_kind.kind.article(),
-                        item_kind.kind.descr()
-                    ),
+                    ident.span,
+                    format!("`Self` not allowed in {} {}", item.kind.article(), item.kind.descr()),
                 );
             }
         }
@@ -1150,17 +1147,14 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                     );
                 }
             }
-        } else if let Some(item_kind) = self.diag_metadata.current_item {
-            if matches!(item_kind.kind, ItemKind::Delegation(..)) {
-                err.span_label(item_kind.span, format!("delegation supports {self_from_macro}"));
+        } else if let Some(item) = self.diag_metadata.current_item {
+            if matches!(item.kind, ItemKind::Delegation(..)) {
+                err.span_label(item.span, format!("delegation supports {self_from_macro}"));
             } else {
+                let span = if let Some(ident) = item.kind.ident() { ident.span } else { item.span };
                 err.span_label(
-                    item_kind.ident.span,
-                    format!(
-                        "`self` not allowed in {} {}",
-                        item_kind.kind.article(),
-                        item_kind.kind.descr()
-                    ),
+                    span,
+                    format!("`self` not allowed in {} {}", item.kind.article(), item.kind.descr()),
                 );
             }
         }
@@ -2196,7 +2190,9 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
 
         if let Some(items) = self.diag_metadata.current_trait_assoc_items {
             for assoc_item in items {
-                if assoc_item.ident == ident {
+                if let Some(assoc_ident) = assoc_item.kind.ident()
+                    && assoc_ident == ident
+                {
                     return Some(match &assoc_item.kind {
                         ast::AssocItemKind::Const(..) => AssocSuggestion::AssocConst,
                         ast::AssocItemKind::Fn(box ast::Fn { sig, .. }) if sig.decl.has_self() => {
@@ -2735,7 +2731,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
             return None;
         }
         match (self.diag_metadata.current_item, single_uppercase_char, self.diag_metadata.currently_processing_generic_args) {
-            (Some(Item { kind: ItemKind::Fn(..), ident, .. }), _, _) if ident.name == sym::main => {
+            (Some(Item { kind: ItemKind::Fn(fn_), .. }), _, _) if fn_.ident.name == sym::main => {
                 // Ignore `fn main()` as we don't want to suggest `fn main<T>()`
             }
             (
@@ -3400,7 +3396,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                     {
                         let pre = if lt.kind == MissingLifetimeKind::Ampersand
                             && let Some((kind, _span)) = self.diag_metadata.current_function
-                            && let FnKind::Fn(_, _, _, ast::Fn { sig, .. }) = kind
+                            && let FnKind::Fn(_, _, ast::Fn { sig, .. }) = kind
                             && !sig.decl.inputs.is_empty()
                             && let sugg = sig
                                 .decl
@@ -3441,7 +3437,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                         } else if (lt.kind == MissingLifetimeKind::Ampersand
                             || lt.kind == MissingLifetimeKind::Underscore)
                             && let Some((kind, _span)) = self.diag_metadata.current_function
-                            && let FnKind::Fn(_, _, _, ast::Fn { sig, .. }) = kind
+                            && let FnKind::Fn(_, _, ast::Fn { sig, .. }) = kind
                             && let ast::FnRetTy::Ty(ret_ty) = &sig.decl.output
                             && !sig.decl.inputs.is_empty()
                             && let arg_refs = sig
@@ -3501,7 +3497,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                         let mut owned_sugg = lt.kind == MissingLifetimeKind::Ampersand;
                         let mut sugg = vec![(lt.span, String::new())];
                         if let Some((kind, _span)) = self.diag_metadata.current_function
-                            && let FnKind::Fn(_, _, _, ast::Fn { sig, .. }) = kind
+                            && let FnKind::Fn(_, _, ast::Fn { sig, .. }) = kind
                             && let ast::FnRetTy::Ty(ty) = &sig.decl.output
                         {
                             let mut lt_finder =
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 612e091770f..3ac66840d87 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -88,6 +88,8 @@ mod late;
 mod macros;
 pub mod rustdoc;
 
+pub use macros::registered_tools_ast;
+
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 
 #[derive(Debug)]
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index d0ffef798cf..9d6ae0aa9d1 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -10,7 +10,7 @@ use rustc_ast::{self as ast, Crate, NodeId, attr};
 use rustc_ast_pretty::pprust;
 use rustc_attr_parsing::{AttributeKind, StabilityLevel, find_attr};
 use rustc_data_structures::intern::Interned;
-use rustc_errors::{Applicability, StashKey};
+use rustc_errors::{Applicability, DiagCtxtHandle, StashKey};
 use rustc_expand::base::{
     DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension, SyntaxExtensionKind,
 };
@@ -124,14 +124,21 @@ fn fast_print_path(path: &ast::Path) -> Symbol {
 }
 
 pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools {
-    let mut registered_tools = RegisteredTools::default();
     let (_, pre_configured_attrs) = &*tcx.crate_for_resolver(()).borrow();
+    registered_tools_ast(tcx.dcx(), pre_configured_attrs)
+}
+
+pub fn registered_tools_ast(
+    dcx: DiagCtxtHandle<'_>,
+    pre_configured_attrs: &[ast::Attribute],
+) -> RegisteredTools {
+    let mut registered_tools = RegisteredTools::default();
     for attr in attr::filter_by_name(pre_configured_attrs, sym::register_tool) {
         for meta_item_inner in attr.meta_item_list().unwrap_or_default() {
             match meta_item_inner.ident() {
                 Some(ident) => {
                     if let Some(old_ident) = registered_tools.replace(ident) {
-                        tcx.dcx().emit_err(errors::ToolWasAlreadyRegistered {
+                        dcx.emit_err(errors::ToolWasAlreadyRegistered {
                             span: ident.span,
                             tool: ident,
                             old_ident_span: old_ident.span,
@@ -139,7 +146,7 @@ pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools {
                     }
                 }
                 None => {
-                    tcx.dcx().emit_err(errors::ToolOnlyAcceptsIdentifiers {
+                    dcx.emit_err(errors::ToolOnlyAcceptsIdentifiers {
                         span: meta_item_inner.span(),
                         tool: sym::register_tool,
                     });
diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs
index 52aaab77ebe..a32fe699016 100644
--- a/compiler/rustc_resolve/src/rustdoc.rs
+++ b/compiler/rustc_resolve/src/rustdoc.rs
@@ -1,13 +1,15 @@
 use std::mem;
 use std::ops::Range;
 
+use itertools::Itertools;
 use pulldown_cmark::{
     BrokenLink, BrokenLinkCallback, CowStr, Event, LinkType, Options, Parser, Tag,
 };
 use rustc_ast as ast;
 use rustc_ast::attr::AttributeExt;
 use rustc_ast::util::comments::beautify_doc_string;
-use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
+use rustc_data_structures::fx::FxIndexMap;
+use rustc_data_structures::unord::UnordSet;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::DefId;
 use rustc_span::{DUMMY_SP, InnerSpan, Span, Symbol, kw, sym};
@@ -422,7 +424,7 @@ fn parse_links<'md>(doc: &'md str) -> Vec<Box<str>> {
     );
     let mut links = Vec::new();
 
-    let mut refids = FxHashSet::default();
+    let mut refids = UnordSet::default();
 
     while let Some(event) = event_iter.next() {
         match event {
@@ -454,7 +456,7 @@ fn parse_links<'md>(doc: &'md str) -> Vec<Box<str>> {
         }
     }
 
-    for (label, refdef) in event_iter.reference_definitions().iter() {
+    for (label, refdef) in event_iter.reference_definitions().iter().sorted_by_key(|x| x.0) {
         if !refids.contains(label) {
             links.push(preprocess_link(&refdef.dest));
         }
diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
index e088417d72e..d56ca9c2453 100644
--- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
+++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
@@ -716,6 +716,7 @@ fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
             hir::definitions::DefPathData::Ctor => "c",
             hir::definitions::DefPathData::AnonConst => "k",
             hir::definitions::DefPathData::OpaqueTy => "i",
+            hir::definitions::DefPathData::SyntheticCoroutineBody => "s",
             hir::definitions::DefPathData::CrateRoot
             | hir::definitions::DefPathData::Use
             | hir::definitions::DefPathData::GlobalAsm
diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl
index 74b8087e077..528c52eace7 100644
--- a/compiler/rustc_session/messages.ftl
+++ b/compiler/rustc_session/messages.ftl
@@ -1,3 +1,9 @@
+session_apple_deployment_target_invalid =
+    failed to parse deployment target specified in {$env_var}: {$error}
+
+session_apple_deployment_target_too_low =
+    deployment target in {$env_var} was set to {$version}, but the minimum supported by `rustc` is {$os_min}
+
 session_binary_float_literal_not_supported = binary float literal is not supported
 session_branch_protection_requires_aarch64 = `-Zbranch-protection` is only supported on aarch64
 
@@ -94,6 +100,8 @@ session_sanitizer_cfi_requires_lto = `-Zsanitizer=cfi` requires `-Clto` or `-Cli
 
 session_sanitizer_cfi_requires_single_codegen_unit = `-Zsanitizer=cfi` with `-Clto` requires `-Ccodegen-units=1`
 
+session_sanitizer_kcfi_arity_requires_kcfi = `-Zsanitizer-kcfi-arity` requires `-Zsanitizer=kcfi`
+
 session_sanitizer_kcfi_requires_panic_abort = `-Z sanitizer=kcfi` requires `-C panic=abort`
 
 session_sanitizer_not_supported = {$us} sanitizer is not supported for this target
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index ed336cc5596..56b3fe2ab4c 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -50,6 +50,7 @@ pub const PRINT_KINDS: &[(&str, PrintKind)] = &[
     ("check-cfg", PrintKind::CheckCfg),
     ("code-models", PrintKind::CodeModels),
     ("crate-name", PrintKind::CrateName),
+    ("crate-root-lint-levels", PrintKind::CrateRootLintLevels),
     ("deployment-target", PrintKind::DeploymentTarget),
     ("file-names", PrintKind::FileNames),
     ("host-tuple", PrintKind::HostTuple),
@@ -236,10 +237,12 @@ pub enum AutoDiff {
     PrintPerf,
     /// Print intermediate IR generation steps
     PrintSteps,
-    /// Print the whole module, before running opts.
+    /// Print the module, before running autodiff.
     PrintModBefore,
-    /// Print the module after Enzyme differentiated everything.
+    /// Print the module after running autodiff.
     PrintModAfter,
+    /// Print the module after running autodiff and optimizations.
+    PrintModFinal,
 
     /// Enzyme's loose type debug helper (can cause incorrect gradients!!)
     /// Usable in cases where Enzyme errors with `can not deduce type of X`.
@@ -881,6 +884,7 @@ pub enum PrintKind {
     CheckCfg,
     CodeModels,
     CrateName,
+    CrateRootLintLevels,
     DeploymentTarget,
     FileNames,
     HostTuple,
@@ -1423,10 +1427,12 @@ pub fn build_target_config(
             }
             target
         }
-        Err(e) => early_dcx.early_fatal(format!(
-            "Error loading target specification: {e}. \
-                     Run `rustc --print target-list` for a list of built-in targets"
-        )),
+        Err(e) => {
+            let mut err =
+                early_dcx.early_struct_fatal(format!("error loading target specification: {e}"));
+            err.help("run `rustc --print target-list` for a list of built-in targets");
+            err.emit();
+        }
     }
 }
 
@@ -1698,7 +1704,7 @@ pub fn get_cmd_lint_options(
     let mut lint_opts_with_position = vec![];
     let mut describe_lints = false;
 
-    for level in [lint::Allow, lint::Warn, lint::ForceWarn(None), lint::Deny, lint::Forbid] {
+    for level in [lint::Allow, lint::Warn, lint::ForceWarn, lint::Deny, lint::Forbid] {
         for (arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) {
             if lint_name == "help" {
                 describe_lints = true;
@@ -2067,6 +2073,7 @@ fn check_print_request_stability(
     match print_kind {
         PrintKind::AllTargetSpecsJson
         | PrintKind::CheckCfg
+        | PrintKind::CrateRootLintLevels
         | PrintKind::SupportedCrateTypes
         | PrintKind::TargetSpecJson
             if !unstable_opts.unstable_options =>
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index 71d8dbe44fe..bf95014843d 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -1,4 +1,4 @@
-use std::num::NonZero;
+use std::num::{NonZero, ParseIntError};
 
 use rustc_ast::token;
 use rustc_ast::util::literal::LitError;
@@ -14,6 +14,14 @@ use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTuple};
 use crate::config::CrateType;
 use crate::parse::ParseSess;
 
+#[derive(Diagnostic)]
+pub(crate) enum AppleDeploymentTarget {
+    #[diag(session_apple_deployment_target_invalid)]
+    Invalid { env_var: &'static str, error: ParseIntError },
+    #[diag(session_apple_deployment_target_too_low)]
+    TooLow { env_var: &'static str, version: String, os_min: String },
+}
+
 pub(crate) struct FeatureGateError {
     pub(crate) span: MultiSpan,
     pub(crate) explain: DiagMessage,
@@ -148,6 +156,10 @@ pub(crate) struct SanitizerCfiGeneralizePointersRequiresCfi;
 pub(crate) struct SanitizerCfiNormalizeIntegersRequiresCfi;
 
 #[derive(Diagnostic)]
+#[diag(session_sanitizer_kcfi_arity_requires_kcfi)]
+pub(crate) struct SanitizerKcfiArityRequiresKcfi;
+
+#[derive(Diagnostic)]
 #[diag(session_sanitizer_kcfi_requires_panic_abort)]
 pub(crate) struct SanitizerKcfiRequiresPanicAbort;
 
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 4cc666b3e37..c70f1500d39 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -711,7 +711,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`, `LooseTypes`, `Inline`";
+    pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Enable`, `PrintSteps`, `PrintTA`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfter`, `PrintModFinal`, `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";
@@ -1359,6 +1359,7 @@ pub mod parse {
                 "PrintSteps" => AutoDiff::PrintSteps,
                 "PrintModBefore" => AutoDiff::PrintModBefore,
                 "PrintModAfter" => AutoDiff::PrintModAfter,
+                "PrintModFinal" => AutoDiff::PrintModFinal,
                 "LooseTypes" => AutoDiff::LooseTypes,
                 "Inline" => AutoDiff::Inline,
                 _ => {
@@ -2093,6 +2094,7 @@ options! {
         `=PrintSteps`
         `=PrintModBefore`
         `=PrintModAfter`
+        `=PrintModFinal`
         `=LooseTypes`
         `=Inline`
         Multiple options can be combined with commas."),
@@ -2175,6 +2177,8 @@ options! {
         them only if an error has not been emitted"),
     ehcont_guard: bool = (false, parse_bool, [TRACKED],
         "generate Windows EHCont Guard tables"),
+    embed_metadata: bool = (true, parse_bool, [TRACKED],
+        "embed metadata in rlibs and dylibs (default: yes)"),
     embed_source: bool = (false, parse_bool, [TRACKED],
         "embed source text in DWARF debug sections (default: no)"),
     emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED],
@@ -2185,6 +2189,8 @@ options! {
         "Use WebAssembly error handling for wasm32-unknown-emscripten"),
     enforce_type_length_limit: bool = (false, parse_bool, [TRACKED],
         "enforce the type length limit when monomorphizing instances in codegen"),
+    experimental_default_bounds: bool = (false, parse_bool, [TRACKED],
+        "enable default bounds for experimental group of auto traits"),
     export_executable_symbols: bool = (false, parse_bool, [TRACKED],
         "export symbols from executables, as if they were dynamic libraries"),
     external_clangrt: bool = (false, parse_bool, [UNTRACKED],
@@ -2439,6 +2445,8 @@ written to standard error output)"),
         "enable normalizing integer types (default: no)"),
     sanitizer_dataflow_abilist: Vec<String> = (Vec::new(), parse_comma_list, [TRACKED],
         "additional ABI list files that control how shadow parameters are passed (comma separated)"),
+    sanitizer_kcfi_arity: Option<bool> = (None, parse_opt_bool, [TRACKED],
+        "enable KCFI arity indicator (default: no)"),
     sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED],
         "enable origins tracking in MemorySanitizer"),
     sanitizer_recover: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],
@@ -2551,6 +2559,9 @@ written to standard error output)"),
         "in diagnostics, use heuristics to shorten paths referring to items"),
     tune_cpu: Option<String> = (None, parse_opt_string, [TRACKED],
         "select processor to schedule for (`rustc --print target-cpus` for details)"),
+    #[rustc_lint_opt_deny_field_access("use `TyCtxt::use_typing_mode_borrowck` instead of this field")]
+    typing_mode_borrowck: bool = (false, parse_bool, [TRACKED],
+        "enable `TypingMode::Borrowck`, changing the way opaque types are handled during MIR borrowck"),
     #[rustc_lint_opt_deny_field_access("use `Session::ub_checks` instead of this field")]
     ub_checks: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "emit runtime checks for Undefined Behavior (default: -Cdebug-assertions)"),
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index a87b1961a99..fb4a437a487 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -29,7 +29,7 @@ use rustc_target::asm::InlineAsmArch;
 use rustc_target::spec::{
     CodeModel, DebuginfoKind, PanicStrategy, RelocModel, RelroLevel, SanitizerSet,
     SmallDataThresholdSupport, SplitDebuginfo, StackProtector, SymbolVisibility, Target,
-    TargetTuple, TlsModel,
+    TargetTuple, TlsModel, apple,
 };
 
 use crate::code_stats::CodeStats;
@@ -381,6 +381,10 @@ impl Session {
         self.opts.unstable_opts.sanitizer_cfi_normalize_integers == Some(true)
     }
 
+    pub fn is_sanitizer_kcfi_arity_enabled(&self) -> bool {
+        self.opts.unstable_opts.sanitizer_kcfi_arity == Some(true)
+    }
+
     pub fn is_sanitizer_kcfi_enabled(&self) -> bool {
         self.opts.unstable_opts.sanitizer.contains(SanitizerSet::KCFI)
     }
@@ -891,6 +895,45 @@ impl Session {
             FileNameDisplayPreference::Local
         }
     }
+
+    /// Get the deployment target on Apple platforms based on the standard environment variables,
+    /// or fall back to the minimum version supported by `rustc`.
+    ///
+    /// This should be guarded behind `if sess.target.is_like_darwin`.
+    pub fn apple_deployment_target(&self) -> apple::OSVersion {
+        let min = apple::OSVersion::minimum_deployment_target(&self.target);
+        let env_var = apple::deployment_target_env_var(&self.target.os);
+
+        // FIXME(madsmtm): Track changes to this.
+        if let Ok(deployment_target) = env::var(env_var) {
+            match apple::OSVersion::from_str(&deployment_target) {
+                Ok(version) => {
+                    let os_min = apple::OSVersion::os_minimum_deployment_target(&self.target.os);
+                    // It is common that the deployment target is set a bit too low, for example on
+                    // macOS Aarch64 to also target older x86_64. So we only want to warn when variable
+                    // is lower than the minimum OS supported by rustc, not when the variable is lower
+                    // than the minimum for a specific target.
+                    if version < os_min {
+                        self.dcx().emit_warn(errors::AppleDeploymentTarget::TooLow {
+                            env_var,
+                            version: version.fmt_pretty().to_string(),
+                            os_min: os_min.fmt_pretty().to_string(),
+                        });
+                    }
+
+                    // Raise the deployment target to the minimum supported.
+                    version.max(min)
+                }
+                Err(error) => {
+                    self.dcx().emit_err(errors::AppleDeploymentTarget::Invalid { env_var, error });
+                    min
+                }
+            }
+        } else {
+            // If no deployment target variable is set, default to the minimum found above.
+            min
+        }
+    }
 }
 
 // JUSTIFICATION: part of session construction
@@ -1211,6 +1254,11 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
         }
     }
 
+    // KCFI arity indicator requires KCFI.
+    if sess.is_sanitizer_kcfi_arity_enabled() && !sess.is_sanitizer_kcfi_enabled() {
+        sess.dcx().emit_err(errors::SanitizerKcfiArityRequiresKcfi);
+    }
+
     // LLVM CFI pointer generalization requires CFI or KCFI.
     if sess.is_sanitizer_cfi_generalize_pointers_enabled() {
         if !(sess.is_sanitizer_cfi_enabled() || sess.is_sanitizer_kcfi_enabled()) {
diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs
index fcede379b89..2243e831b66 100644
--- a/compiler/rustc_session/src/utils.rs
+++ b/compiler/rustc_session/src/utils.rs
@@ -149,14 +149,15 @@ pub fn extra_compiler_flags() -> Option<(Vec<String>, bool)> {
                 arg[a.len()..].to_string()
             };
             let option = content.split_once('=').map(|s| s.0).unwrap_or(&content);
-            if ICE_REPORT_COMPILER_FLAGS_EXCLUDE.iter().any(|exc| option == *exc) {
+            if ICE_REPORT_COMPILER_FLAGS_EXCLUDE.contains(&option) {
                 excluded_cargo_defaults = true;
             } else {
                 result.push(a.to_string());
-                match ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.iter().find(|s| option == **s) {
-                    Some(s) => result.push(format!("{s}=[REDACTED]")),
-                    None => result.push(content),
-                }
+                result.push(if ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.contains(&option) {
+                    format!("{option}=[REDACTED]")
+                } else {
+                    content
+                });
             }
         }
     }
diff --git a/compiler/rustc_smir/Cargo.toml b/compiler/rustc_smir/Cargo.toml
index b2149a03a8e..fc9f411ac3c 100644
--- a/compiler/rustc_smir/Cargo.toml
+++ b/compiler/rustc_smir/Cargo.toml
@@ -14,6 +14,6 @@ rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 scoped-tls = "1.0"
-stable_mir = {path = "../stable_mir" }
+serde = { version = "1.0.125", features = [ "derive" ] }
 tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_smir/src/lib.rs b/compiler/rustc_smir/src/lib.rs
index eaba14bbf30..5d465bca4ee 100644
--- a/compiler/rustc_smir/src/lib.rs
+++ b/compiler/rustc_smir/src/lib.rs
@@ -22,3 +22,5 @@ pub mod rustc_internal;
 
 // Make this module private for now since external users should not call these directly.
 mod rustc_smir;
+
+pub mod stable_mir;
diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs
index bb2b2dea2f3..36b68cc1398 100644
--- a/compiler/rustc_smir/src/rustc_internal/internal.rs
+++ b/compiler/rustc_smir/src/rustc_internal/internal.rs
@@ -21,6 +21,7 @@ use stable_mir::{CrateItem, CrateNum, DefId};
 
 use super::RustcInternal;
 use crate::rustc_smir::Tables;
+use crate::stable_mir;
 
 impl RustcInternal for CrateItem {
     type T<'tcx> = rustc_span::def_id::DefId;
diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs
index ad38ea228bf..a546a44c870 100644
--- a/compiler/rustc_smir/src/rustc_internal/mod.rs
+++ b/compiler/rustc_smir/src/rustc_internal/mod.rs
@@ -22,6 +22,7 @@ use stable_mir::ty::IndexedVal;
 
 use crate::rustc_smir::context::TablesWrapper;
 use crate::rustc_smir::{Stable, Tables};
+use crate::stable_mir;
 
 mod internal;
 pub mod pretty;
@@ -147,6 +148,14 @@ impl<'tcx> Tables<'tcx> {
         stable_mir::ty::CoroutineWitnessDef(self.create_def_id(did))
     }
 
+    pub fn assoc_def(&mut self, did: DefId) -> stable_mir::ty::AssocDef {
+        stable_mir::ty::AssocDef(self.create_def_id(did))
+    }
+
+    pub fn opaque_def(&mut self, did: DefId) -> stable_mir::ty::OpaqueDef {
+        stable_mir::ty::OpaqueDef(self.create_def_id(did))
+    }
+
     pub fn prov(&mut self, aid: AllocId) -> stable_mir::ty::Prov {
         stable_mir::ty::Prov(self.create_alloc_id(aid))
     }
diff --git a/compiler/rustc_smir/src/rustc_internal/pretty.rs b/compiler/rustc_smir/src/rustc_internal/pretty.rs
index b752ad71ecc..0710c18746a 100644
--- a/compiler/rustc_smir/src/rustc_internal/pretty.rs
+++ b/compiler/rustc_smir/src/rustc_internal/pretty.rs
@@ -3,6 +3,7 @@ use std::io;
 use rustc_middle::ty::TyCtxt;
 
 use super::run;
+use crate::stable_mir;
 
 pub fn write_smir_pretty<'tcx, W: io::Write>(tcx: TyCtxt<'tcx>, w: &mut W) -> io::Result<()> {
     writeln!(
diff --git a/compiler/rustc_smir/src/rustc_smir/alloc.rs b/compiler/rustc_smir/src/rustc_smir/alloc.rs
index 52c5b425c14..9cb89634c52 100644
--- a/compiler/rustc_smir/src/rustc_smir/alloc.rs
+++ b/compiler/rustc_smir/src/rustc_smir/alloc.rs
@@ -6,6 +6,7 @@ use stable_mir::mir::Mutability;
 use stable_mir::ty::{Allocation, ProvenanceMap};
 
 use crate::rustc_smir::{Stable, Tables};
+use crate::stable_mir;
 
 /// Creates new empty `Allocation` from given `Align`.
 fn new_empty_allocation(align: Align) -> Allocation {
@@ -27,7 +28,7 @@ pub(crate) fn new_allocation<'tcx>(
     tables: &mut Tables<'tcx>,
 ) -> Allocation {
     try_new_allocation(ty, const_value, tables)
-        .expect(&format!("Failed to convert: {const_value:?} to {ty:?}"))
+        .unwrap_or_else(|_| panic!("Failed to convert: {const_value:?} to {ty:?}"))
 }
 
 #[allow(rustc::usage_of_qualified_ty)]
@@ -36,39 +37,30 @@ pub(crate) fn try_new_allocation<'tcx>(
     const_value: ConstValue<'tcx>,
     tables: &mut Tables<'tcx>,
 ) -> Result<Allocation, Error> {
+    let layout = tables
+        .tcx
+        .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty))
+        .map_err(|e| e.stable(tables))?;
     Ok(match const_value {
         ConstValue::Scalar(scalar) => {
             let size = scalar.size();
-            let align = tables
-                .tcx
-                .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty))
-                .map_err(|e| e.stable(tables))?
-                .align;
-            let mut allocation =
-                rustc_middle::mir::interpret::Allocation::new(size, align.abi, AllocInit::Uninit);
+            let mut allocation = rustc_middle::mir::interpret::Allocation::new(
+                size,
+                layout.align.abi,
+                AllocInit::Uninit,
+            );
             allocation
                 .write_scalar(&tables.tcx, alloc_range(Size::ZERO, size), scalar)
                 .map_err(|e| e.stable(tables))?;
             allocation.stable(tables)
         }
-        ConstValue::ZeroSized => {
-            let align = tables
-                .tcx
-                .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty))
-                .map_err(|e| e.stable(tables))?
-                .align;
-            new_empty_allocation(align.abi)
-        }
+        ConstValue::ZeroSized => new_empty_allocation(layout.align.abi),
         ConstValue::Slice { data, meta } => {
             let alloc_id = tables.tcx.reserve_and_set_memory_alloc(data);
             let ptr = Pointer::new(alloc_id.into(), Size::ZERO);
             let scalar_ptr = rustc_middle::mir::interpret::Scalar::from_pointer(ptr, &tables.tcx);
             let scalar_meta =
                 rustc_middle::mir::interpret::Scalar::from_target_usize(meta, &tables.tcx);
-            let layout = tables
-                .tcx
-                .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty))
-                .map_err(|e| e.stable(tables))?;
             let mut allocation = rustc_middle::mir::interpret::Allocation::new(
                 layout.size,
                 layout.align.abi,
@@ -92,12 +84,7 @@ pub(crate) fn try_new_allocation<'tcx>(
         }
         ConstValue::Indirect { alloc_id, offset } => {
             let alloc = tables.tcx.global_alloc(alloc_id).unwrap_memory();
-            let ty_size = tables
-                .tcx
-                .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty))
-                .map_err(|e| e.stable(tables))?
-                .size;
-            allocation_filter(&alloc.0, alloc_range(offset, ty_size), tables)
+            allocation_filter(&alloc.0, alloc_range(offset, layout.size), tables)
         }
     })
 }
diff --git a/compiler/rustc_smir/src/rustc_smir/builder.rs b/compiler/rustc_smir/src/rustc_smir/builder.rs
index 2eb0cea0e85..64763b71d30 100644
--- a/compiler/rustc_smir/src/rustc_smir/builder.rs
+++ b/compiler/rustc_smir/src/rustc_smir/builder.rs
@@ -10,6 +10,7 @@ use rustc_middle::mir::visit::MutVisitor;
 use rustc_middle::ty::{self, TyCtxt};
 
 use crate::rustc_smir::{Stable, Tables};
+use crate::stable_mir;
 
 /// Builds a monomorphic body for a given instance.
 pub(crate) struct BodyBuilder<'tcx> {
diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs
index aa1921fc8e7..240f6f7fe45 100644
--- a/compiler/rustc_smir/src/rustc_smir/context.rs
+++ b/compiler/rustc_smir/src/rustc_smir/context.rs
@@ -35,6 +35,7 @@ use stable_mir::{Crate, CrateDef, CrateItem, CrateNum, DefId, Error, Filename, I
 use crate::rustc_internal::RustcInternal;
 use crate::rustc_smir::builder::BodyBuilder;
 use crate::rustc_smir::{Stable, Tables, alloc, filter_def_ids, new_item_kind, smir_crate};
+use crate::stable_mir;
 
 impl<'tcx> Context for TablesWrapper<'tcx> {
     fn target_info(&self) -> MachineInfo {
@@ -822,6 +823,21 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
         let ty = un_op.internal(&mut *tables, tcx).ty(tcx, arg_internal);
         ty.stable(&mut *tables)
     }
+
+    fn associated_items(&self, def_id: stable_mir::DefId) -> stable_mir::AssocItems {
+        let mut tables = self.0.borrow_mut();
+        let tcx = tables.tcx;
+        let def_id = tables[def_id];
+        let assoc_items = if tcx.is_trait_alias(def_id) {
+            Vec::new()
+        } else {
+            tcx.associated_item_def_ids(def_id)
+                .iter()
+                .map(|did| tcx.associated_item(*did).stable(&mut *tables))
+                .collect()
+        };
+        assoc_items
+    }
 }
 
 pub(crate) struct TablesWrapper<'tcx>(pub RefCell<Tables<'tcx>>);
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs
index 62cbab9b723..7ccc785a400 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs
@@ -14,6 +14,7 @@ use stable_mir::target::MachineSize as Size;
 use stable_mir::ty::{Align, IndexedVal, VariantIdx};
 
 use crate::rustc_smir::{Stable, Tables};
+use crate::stable_mir;
 
 impl<'tcx> Stable<'tcx> for rustc_abi::VariantIdx {
     type T = VariantIdx;
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/error.rs b/compiler/rustc_smir/src/rustc_smir/convert/error.rs
index 82ecfa630dd..2cde5542483 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/error.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/error.rs
@@ -6,6 +6,7 @@ use rustc_middle::mir::interpret::AllocError;
 use rustc_middle::ty::layout::LayoutError;
 
 use crate::rustc_smir::{Stable, Tables};
+use crate::stable_mir;
 
 impl<'tcx> Stable<'tcx> for LayoutError<'tcx> {
     type T = stable_mir::Error;
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
index bdd6e16a7c1..61b769bce08 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
@@ -9,6 +9,7 @@ use stable_mir::ty::{Allocation, ConstantKind, MirConst};
 use stable_mir::{Error, opaque};
 
 use crate::rustc_smir::{Stable, Tables, alloc};
+use crate::stable_mir;
 
 impl<'tcx> Stable<'tcx> for mir::Body<'tcx> {
     type T = stable_mir::mir::Body;
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs
index a3da563af50..3494de62d83 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs
@@ -3,6 +3,7 @@
 use rustc_abi::FieldIdx;
 
 use crate::rustc_smir::{Stable, Tables};
+use crate::stable_mir;
 
 mod abi;
 mod error;
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index aa0eac628dd..1ba25aa0e97 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -7,6 +7,7 @@ use stable_mir::ty::{
 };
 
 use crate::rustc_smir::{Stable, Tables, alloc};
+use crate::stable_mir;
 
 impl<'tcx> Stable<'tcx> for ty::AliasTyKind {
     type T = stable_mir::ty::AliasKind;
@@ -890,3 +891,63 @@ impl<'tcx> Stable<'tcx> for rustc_session::cstore::ForeignModule {
         }
     }
 }
+
+impl<'tcx> Stable<'tcx> for ty::AssocKind {
+    type T = stable_mir::ty::AssocKind;
+
+    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
+        use stable_mir::ty::AssocKind;
+        match self {
+            ty::AssocKind::Const => AssocKind::Const,
+            ty::AssocKind::Fn => AssocKind::Fn,
+            ty::AssocKind::Type => AssocKind::Type,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::AssocItemContainer {
+    type T = stable_mir::ty::AssocItemContainer;
+
+    fn stable(&self, _tables: &mut Tables<'_>) -> Self::T {
+        use stable_mir::ty::AssocItemContainer;
+        match self {
+            ty::AssocItemContainer::Trait => AssocItemContainer::Trait,
+            ty::AssocItemContainer::Impl => AssocItemContainer::Impl,
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::AssocItem {
+    type T = stable_mir::ty::AssocItem;
+
+    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+        stable_mir::ty::AssocItem {
+            def_id: tables.assoc_def(self.def_id),
+            name: self.name.to_string(),
+            kind: self.kind.stable(tables),
+            container: self.container.stable(tables),
+            trait_item_def_id: self.trait_item_def_id.map(|did| tables.assoc_def(did)),
+            fn_has_self_parameter: self.fn_has_self_parameter,
+            opt_rpitit_info: self.opt_rpitit_info.map(|rpitit| rpitit.stable(tables)),
+        }
+    }
+}
+
+impl<'tcx> Stable<'tcx> for ty::ImplTraitInTraitData {
+    type T = stable_mir::ty::ImplTraitInTraitData;
+
+    fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
+        use stable_mir::ty::ImplTraitInTraitData;
+        match self {
+            ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id } => {
+                ImplTraitInTraitData::Trait {
+                    fn_def_id: tables.fn_def(*fn_def_id),
+                    opaque_def_id: tables.opaque_def(*opaque_def_id),
+                }
+            }
+            ty::ImplTraitInTraitData::Impl { fn_def_id } => {
+                ImplTraitInTraitData::Impl { fn_def_id: tables.fn_def(*fn_def_id) }
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs
index c5d33f090a0..dea8f54a670 100644
--- a/compiler/rustc_smir/src/rustc_smir/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/mod.rs
@@ -21,6 +21,7 @@ use stable_mir::{CtorKind, ItemKind};
 use tracing::debug;
 
 use crate::rustc_internal::IndexMap;
+use crate::stable_mir;
 
 mod alloc;
 mod builder;
diff --git a/compiler/stable_mir/src/abi.rs b/compiler/rustc_smir/src/stable_mir/abi.rs
index 091f3e1a95e..3842cb7e653 100644
--- a/compiler/stable_mir/src/abi.rs
+++ b/compiler/rustc_smir/src/stable_mir/abi.rs
@@ -3,12 +3,13 @@ use std::num::NonZero;
 use std::ops::RangeInclusive;
 
 use serde::Serialize;
+use stable_mir::compiler_interface::with;
+use stable_mir::mir::FieldIdx;
+use stable_mir::target::{MachineInfo, MachineSize as Size};
+use stable_mir::ty::{Align, IndexedVal, Ty, VariantIdx};
+use stable_mir::{Error, Opaque, error};
 
-use crate::compiler_interface::with;
-use crate::mir::FieldIdx;
-use crate::target::{MachineInfo, MachineSize as Size};
-use crate::ty::{Align, IndexedVal, Ty, VariantIdx};
-use crate::{Error, Opaque, error};
+use crate::stable_mir;
 
 /// A function ABI definition.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
@@ -149,7 +150,7 @@ pub enum FieldsShape {
     Arbitrary {
         /// Offsets for the first byte of each field,
         /// ordered to match the source definition order.
-        /// I.e.: It follows the same order as [crate::ty::VariantDef::fields()].
+        /// I.e.: It follows the same order as [super::ty::VariantDef::fields()].
         /// This vector does not go in increasing order.
         offsets: Vec<Size>,
     },
diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/rustc_smir/src/stable_mir/compiler_interface.rs
index e82c957c34e..33d950bb951 100644
--- a/compiler/stable_mir/src/compiler_interface.rs
+++ b/compiler/rustc_smir/src/stable_mir/compiler_interface.rs
@@ -5,23 +5,25 @@
 
 use std::cell::Cell;
 
-use crate::abi::{FnAbi, Layout, LayoutShape};
-use crate::crate_def::Attribute;
-use crate::mir::alloc::{AllocId, GlobalAlloc};
-use crate::mir::mono::{Instance, InstanceDef, StaticDef};
-use crate::mir::{BinOp, Body, Place, UnOp};
-use crate::target::MachineInfo;
-use crate::ty::{
+use stable_mir::abi::{FnAbi, Layout, LayoutShape};
+use stable_mir::crate_def::Attribute;
+use stable_mir::mir::alloc::{AllocId, GlobalAlloc};
+use stable_mir::mir::mono::{Instance, InstanceDef, StaticDef};
+use stable_mir::mir::{BinOp, Body, Place, UnOp};
+use stable_mir::target::MachineInfo;
+use stable_mir::ty::{
     AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, FieldDef, FnDef, ForeignDef,
     ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates, Generics,
     ImplDef, ImplTrait, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, Span, TraitDecl,
     TraitDef, Ty, TyConst, TyConstId, TyKind, UintTy, VariantDef,
 };
-use crate::{
-    Crate, CrateItem, CrateItems, CrateNum, DefId, Error, Filename, ImplTraitDecls, ItemKind,
-    Symbol, TraitDecls, mir,
+use stable_mir::{
+    AssocItems, Crate, CrateItem, CrateItems, CrateNum, DefId, Error, Filename, ImplTraitDecls,
+    ItemKind, Symbol, TraitDecls, mir,
 };
 
+use crate::stable_mir;
+
 /// This trait defines the interface between stable_mir and the Rust compiler.
 /// Do not use this directly.
 pub trait Context {
@@ -251,6 +253,9 @@ pub trait Context {
 
     /// Get the resulting type of unary operation.
     fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty;
+
+    /// Get all associated items of a definition.
+    fn associated_items(&self, def_id: DefId) -> AssocItems;
 }
 
 // A thread local variable that stores a pointer to the tables mapping between TyCtxt
diff --git a/compiler/stable_mir/src/crate_def.rs b/compiler/rustc_smir/src/stable_mir/crate_def.rs
index 2577c281ca4..64f7ef9b314 100644
--- a/compiler/stable_mir/src/crate_def.rs
+++ b/compiler/rustc_smir/src/stable_mir/crate_def.rs
@@ -2,9 +2,10 @@
 //! such as, a function, a trait, an enum, and any other definitions.
 
 use serde::Serialize;
+use stable_mir::ty::{GenericArgs, Span, Ty};
+use stable_mir::{AssocItems, Crate, Symbol, with};
 
-use crate::ty::{GenericArgs, Span, Ty};
-use crate::{Crate, Symbol, with};
+use crate::stable_mir;
 
 /// A unique identification number for each item accessible for the current compilation unit.
 #[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize)]
@@ -103,6 +104,14 @@ pub trait CrateDefType: CrateDef {
     }
 }
 
+/// A trait for retrieving all items from a definition within a crate.
+pub trait CrateDefItems: CrateDef {
+    /// Retrieve all associated items from a definition.
+    fn associated_items(&self) -> AssocItems {
+        with(|cx| cx.associated_items(self.def_id()))
+    }
+}
+
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub struct Attribute {
     value: String,
@@ -158,3 +167,9 @@ macro_rules! crate_def_with_ty {
         impl CrateDefType for $name {}
     };
 }
+
+macro_rules! impl_crate_def_items {
+    ( $name:ident $(;)? ) => {
+        impl CrateDefItems for $name {}
+    };
+}
diff --git a/compiler/stable_mir/src/error.rs b/compiler/rustc_smir/src/stable_mir/error.rs
index 050752e41eb..050752e41eb 100644
--- a/compiler/stable_mir/src/error.rs
+++ b/compiler/rustc_smir/src/stable_mir/error.rs
diff --git a/compiler/stable_mir/src/mir.rs b/compiler/rustc_smir/src/stable_mir/mir.rs
index 413b5152bb3..413b5152bb3 100644
--- a/compiler/stable_mir/src/mir.rs
+++ b/compiler/rustc_smir/src/stable_mir/mir.rs
diff --git a/compiler/stable_mir/src/mir/alloc.rs b/compiler/rustc_smir/src/stable_mir/mir/alloc.rs
index 023807b76ae..782f52888b7 100644
--- a/compiler/stable_mir/src/mir/alloc.rs
+++ b/compiler/rustc_smir/src/stable_mir/mir/alloc.rs
@@ -3,11 +3,12 @@
 use std::io::Read;
 
 use serde::Serialize;
+use stable_mir::mir::mono::{Instance, StaticDef};
+use stable_mir::target::{Endian, MachineInfo};
+use stable_mir::ty::{Allocation, Binder, ExistentialTraitRef, IndexedVal, Ty};
+use stable_mir::{Error, with};
 
-use crate::mir::mono::{Instance, StaticDef};
-use crate::target::{Endian, MachineInfo};
-use crate::ty::{Allocation, Binder, ExistentialTraitRef, IndexedVal, Ty};
-use crate::{Error, with};
+use crate::stable_mir;
 
 /// An allocation in the SMIR global memory can be either a function pointer,
 /// a static, or a "real" allocation with some data in it.
diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/rustc_smir/src/stable_mir/mir/body.rs
index b1bf7bce828..b2360545424 100644
--- a/compiler/stable_mir/src/mir/body.rs
+++ b/compiler/rustc_smir/src/stable_mir/mir/body.rs
@@ -1,14 +1,15 @@
 use std::io;
 
 use serde::Serialize;
-
-use crate::compiler_interface::with;
-use crate::mir::pretty::function_body;
-use crate::ty::{
+use stable_mir::compiler_interface::with;
+use stable_mir::mir::pretty::function_body;
+use stable_mir::ty::{
     AdtDef, ClosureDef, CoroutineClosureDef, CoroutineDef, GenericArgs, MirConst, Movability,
     Region, RigidTy, Ty, TyConst, TyKind, VariantIdx,
 };
-use crate::{Error, Opaque, Span, Symbol};
+use stable_mir::{Error, Opaque, Span, Symbol};
+
+use crate::stable_mir;
 
 /// The SMIR representation of a single function.
 #[derive(Clone, Debug, Serialize)]
@@ -565,7 +566,7 @@ pub enum Rvalue {
     ///
     /// **Needs clarification**: Are there weird additional semantics here related to the runtime
     /// nature of this operation?
-    ThreadLocalRef(crate::CrateItem),
+    ThreadLocalRef(stable_mir::CrateItem),
 
     /// Computes a value as described by the operation.
     NullaryOp(NullOp, Ty),
@@ -1057,8 +1058,7 @@ impl Place {
     /// In order to retrieve the correct type, the `locals` argument must match the list of all
     /// locals from the function body where this place originates from.
     pub fn ty(&self, locals: &[LocalDecl]) -> Result<Ty, Error> {
-        let start_ty = locals[self.local].ty;
-        self.projection.iter().fold(Ok(start_ty), |place_ty, elem| elem.ty(place_ty?))
+        self.projection.iter().try_fold(locals[self.local].ty, |place_ty, elem| elem.ty(place_ty))
     }
 }
 
diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/rustc_smir/src/stable_mir/mir/mono.rs
index 22507a49411..0c3e4fb7c90 100644
--- a/compiler/stable_mir/src/mir/mono.rs
+++ b/compiler/rustc_smir/src/stable_mir/mir/mono.rs
@@ -2,12 +2,13 @@ use std::fmt::{Debug, Formatter};
 use std::io;
 
 use serde::Serialize;
+use stable_mir::abi::FnAbi;
+use stable_mir::crate_def::CrateDef;
+use stable_mir::mir::Body;
+use stable_mir::ty::{Allocation, ClosureDef, ClosureKind, FnDef, GenericArgs, IndexedVal, Ty};
+use stable_mir::{CrateItem, DefId, Error, ItemKind, Opaque, Symbol, with};
 
-use crate::abi::FnAbi;
-use crate::crate_def::CrateDef;
-use crate::mir::Body;
-use crate::ty::{Allocation, ClosureDef, ClosureKind, FnDef, GenericArgs, IndexedVal, Ty};
-use crate::{CrateItem, DefId, Error, ItemKind, Opaque, Symbol, with};
+use crate::stable_mir;
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
 pub enum MonoItem {
@@ -117,11 +118,11 @@ impl Instance {
     }
 
     /// Resolve an instance starting from a function definition and generic arguments.
-    pub fn resolve(def: FnDef, args: &GenericArgs) -> Result<Instance, crate::Error> {
+    pub fn resolve(def: FnDef, args: &GenericArgs) -> Result<Instance, Error> {
         with(|context| {
-            context.resolve_instance(def, args).ok_or_else(|| {
-                crate::Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`"))
-            })
+            context
+                .resolve_instance(def, args)
+                .ok_or_else(|| Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")))
         })
     }
 
@@ -131,11 +132,11 @@ impl Instance {
     }
 
     /// Resolve an instance for a given function pointer.
-    pub fn resolve_for_fn_ptr(def: FnDef, args: &GenericArgs) -> Result<Instance, crate::Error> {
+    pub fn resolve_for_fn_ptr(def: FnDef, args: &GenericArgs) -> Result<Instance, Error> {
         with(|context| {
-            context.resolve_for_fn_ptr(def, args).ok_or_else(|| {
-                crate::Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`"))
-            })
+            context
+                .resolve_for_fn_ptr(def, args)
+                .ok_or_else(|| Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")))
         })
     }
 
@@ -144,11 +145,11 @@ impl Instance {
         def: ClosureDef,
         args: &GenericArgs,
         kind: ClosureKind,
-    ) -> Result<Instance, crate::Error> {
+    ) -> Result<Instance, Error> {
         with(|context| {
-            context.resolve_closure(def, args, kind).ok_or_else(|| {
-                crate::Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`"))
-            })
+            context
+                .resolve_closure(def, args, kind)
+                .ok_or_else(|| Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")))
         })
     }
 
@@ -195,7 +196,7 @@ impl Debug for Instance {
 /// Try to convert a crate item into an instance.
 /// The item cannot be generic in order to be converted into an instance.
 impl TryFrom<CrateItem> for Instance {
-    type Error = crate::Error;
+    type Error = stable_mir::Error;
 
     fn try_from(item: CrateItem) -> Result<Self, Self::Error> {
         with(|context| {
@@ -212,7 +213,7 @@ impl TryFrom<CrateItem> for Instance {
 /// Try to convert an instance into a crate item.
 /// Only user defined instances can be converted.
 impl TryFrom<Instance> for CrateItem {
-    type Error = crate::Error;
+    type Error = stable_mir::Error;
 
     fn try_from(value: Instance) -> Result<Self, Self::Error> {
         with(|context| {
@@ -259,7 +260,7 @@ crate_def! {
 }
 
 impl TryFrom<CrateItem> for StaticDef {
-    type Error = crate::Error;
+    type Error = stable_mir::Error;
 
     fn try_from(value: CrateItem) -> Result<Self, Self::Error> {
         if matches!(value.kind(), ItemKind::Static) {
@@ -271,7 +272,7 @@ impl TryFrom<CrateItem> for StaticDef {
 }
 
 impl TryFrom<Instance> for StaticDef {
-    type Error = crate::Error;
+    type Error = stable_mir::Error;
 
     fn try_from(value: Instance) -> Result<Self, Self::Error> {
         StaticDef::try_from(CrateItem::try_from(value)?)
diff --git a/compiler/stable_mir/src/mir/pretty.rs b/compiler/rustc_smir/src/stable_mir/mir/pretty.rs
index 8278afb7a2f..439ebe978e5 100644
--- a/compiler/stable_mir/src/mir/pretty.rs
+++ b/compiler/rustc_smir/src/stable_mir/mir/pretty.rs
@@ -4,13 +4,14 @@ use std::io::Write;
 use std::{fmt, io, iter};
 
 use fmt::{Display, Formatter};
-
-use super::{AggregateKind, AssertMessage, BinOp, BorrowKind, FakeBorrowKind, TerminatorKind};
-use crate::mir::{
+use stable_mir::mir::{
     Operand, Place, RawPtrKind, Rvalue, StatementKind, UnwindAction, VarDebugInfoContents,
 };
-use crate::ty::{AdtKind, IndexedVal, MirConst, Ty, TyConst};
-use crate::{Body, CrateDef, Mutability, with};
+use stable_mir::ty::{AdtKind, AssocKind, IndexedVal, MirConst, Ty, TyConst};
+use stable_mir::{Body, CrateDef, Mutability, with};
+
+use super::{AggregateKind, AssertMessage, BinOp, BorrowKind, FakeBorrowKind, TerminatorKind};
+use crate::stable_mir;
 
 impl Display for Ty {
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
@@ -18,6 +19,16 @@ impl Display for Ty {
     }
 }
 
+impl Display for AssocKind {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        match self {
+            AssocKind::Fn => write!(f, "method"),
+            AssocKind::Const => write!(f, "associated const"),
+            AssocKind::Type => write!(f, "associated type"),
+        }
+    }
+}
+
 impl Debug for Place {
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
         with(|ctx| write!(f, "{}", ctx.place_pretty(self)))
diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/rustc_smir/src/stable_mir/mir/visit.rs
index 09447e70dfb..786693ea98d 100644
--- a/compiler/stable_mir/src/mir/visit.rs
+++ b/compiler/rustc_smir/src/stable_mir/mir/visit.rs
@@ -35,9 +35,11 @@
 //! The only place that `_` is acceptable is to match a field (or
 //! variant argument) that does not require visiting.
 
-use crate::mir::*;
-use crate::ty::{GenericArgs, MirConst, Region, Ty, TyConst};
-use crate::{Error, Opaque, Span};
+use stable_mir::mir::*;
+use stable_mir::ty::{GenericArgs, MirConst, Region, Ty, TyConst};
+use stable_mir::{Error, Opaque, Span};
+
+use crate::stable_mir;
 
 macro_rules! make_mir_visitor {
     ($visitor_trait_name:ident, $($mutability:ident)?) => {
@@ -563,7 +565,7 @@ pub struct PlaceRef<'a> {
 impl PlaceRef<'_> {
     /// Get the type of this place.
     pub fn ty(&self, locals: &[LocalDecl]) -> Result<Ty, Error> {
-        self.projection.iter().fold(Ok(locals[self.local].ty), |place_ty, elem| elem.ty(place_ty?))
+        self.projection.iter().try_fold(locals[self.local].ty, |place_ty, elem| elem.ty(place_ty))
     }
 }
 
diff --git a/compiler/rustc_smir/src/stable_mir/mod.rs b/compiler/rustc_smir/src/stable_mir/mod.rs
new file mode 100644
index 00000000000..c59758d4ad3
--- /dev/null
+++ b/compiler/rustc_smir/src/stable_mir/mod.rs
@@ -0,0 +1,239 @@
+//! Module that is temporarily parasitic on the `rustc_smir` crate,
+//!
+//! This module is designed to resolve circular dependency that would happen
+//! if we gradually invert the dependency order between `rustc_smir` and `stable_mir`.
+//!
+//! Once refactoring is complete, we will migrate it back to the `stable_mir` crate.
+
+//! The WIP stable interface to rustc internals.
+//!
+//! For more information see <https://github.com/rust-lang/project-stable-mir>
+//!
+//! # Note
+//!
+//! This API is still completely unstable and subject to change.
+
+// #![doc(
+//     html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
+//     test(attr(allow(unused_variables), deny(warnings)))
+// )]
+//!
+//! This crate shall contain all type definitions and APIs that we expect third-party tools to invoke to
+//! interact with the compiler.
+//!
+//! The goal is to eventually be published on
+//! [crates.io](https://crates.io).
+
+use std::fmt::Debug;
+use std::{fmt, io};
+
+use serde::Serialize;
+use stable_mir::compiler_interface::with;
+pub use stable_mir::crate_def::{CrateDef, CrateDefItems, CrateDefType, DefId};
+pub use stable_mir::error::*;
+use stable_mir::mir::mono::StaticDef;
+use stable_mir::mir::{Body, Mutability};
+use stable_mir::ty::{AssocItem, FnDef, ForeignModuleDef, ImplDef, IndexedVal, Span, TraitDef, Ty};
+
+use crate::stable_mir;
+
+pub mod abi;
+#[macro_use]
+pub mod crate_def;
+pub mod compiler_interface;
+#[macro_use]
+pub mod error;
+pub mod mir;
+pub mod target;
+pub mod ty;
+pub mod visitor;
+
+/// Use String for now but we should replace it.
+pub type Symbol = String;
+
+/// The number that identifies a crate.
+pub type CrateNum = usize;
+
+impl Debug for DefId {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("DefId").field("id", &self.0).field("name", &self.name()).finish()
+    }
+}
+
+impl IndexedVal for DefId {
+    fn to_val(index: usize) -> Self {
+        DefId(index)
+    }
+
+    fn to_index(&self) -> usize {
+        self.0
+    }
+}
+
+/// A list of crate items.
+pub type CrateItems = Vec<CrateItem>;
+
+/// A list of trait decls.
+pub type TraitDecls = Vec<TraitDef>;
+
+/// A list of impl trait decls.
+pub type ImplTraitDecls = Vec<ImplDef>;
+
+/// A list of associated items.
+pub type AssocItems = Vec<AssocItem>;
+
+/// Holds information about a crate.
+#[derive(Clone, PartialEq, Eq, Debug, Serialize)]
+pub struct Crate {
+    pub id: CrateNum,
+    pub name: Symbol,
+    pub is_local: bool,
+}
+
+impl Crate {
+    /// The list of foreign modules in this crate.
+    pub fn foreign_modules(&self) -> Vec<ForeignModuleDef> {
+        with(|cx| cx.foreign_modules(self.id))
+    }
+
+    /// The list of traits declared in this crate.
+    pub fn trait_decls(&self) -> TraitDecls {
+        with(|cx| cx.trait_decls(self.id))
+    }
+
+    /// The list of trait implementations in this crate.
+    pub fn trait_impls(&self) -> ImplTraitDecls {
+        with(|cx| cx.trait_impls(self.id))
+    }
+
+    /// Return a list of function definitions from this crate independent on their visibility.
+    pub fn fn_defs(&self) -> Vec<FnDef> {
+        with(|cx| cx.crate_functions(self.id))
+    }
+
+    /// Return a list of static items defined in this crate independent on their visibility.
+    pub fn statics(&self) -> Vec<StaticDef> {
+        with(|cx| cx.crate_statics(self.id))
+    }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize)]
+pub enum ItemKind {
+    Fn,
+    Static,
+    Const,
+    Ctor(CtorKind),
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize)]
+pub enum CtorKind {
+    Const,
+    Fn,
+}
+
+pub type Filename = String;
+
+crate_def_with_ty! {
+    /// Holds information about an item in a crate.
+    #[derive(Serialize)]
+    pub CrateItem;
+}
+
+impl CrateItem {
+    /// This will return the body of an item or panic if it's not available.
+    pub fn expect_body(&self) -> mir::Body {
+        with(|cx| cx.mir_body(self.0))
+    }
+
+    /// Return the body of an item if available.
+    pub fn body(&self) -> Option<mir::Body> {
+        with(|cx| cx.has_body(self.0).then(|| cx.mir_body(self.0)))
+    }
+
+    /// Check if a body is available for this item.
+    pub fn has_body(&self) -> bool {
+        with(|cx| cx.has_body(self.0))
+    }
+
+    pub fn span(&self) -> Span {
+        with(|cx| cx.span_of_an_item(self.0))
+    }
+
+    pub fn kind(&self) -> ItemKind {
+        with(|cx| cx.item_kind(*self))
+    }
+
+    pub fn requires_monomorphization(&self) -> bool {
+        with(|cx| cx.requires_monomorphization(self.0))
+    }
+
+    pub fn ty(&self) -> Ty {
+        with(|cx| cx.def_ty(self.0))
+    }
+
+    pub fn is_foreign_item(&self) -> bool {
+        with(|cx| cx.is_foreign_item(self.0))
+    }
+
+    /// Emit MIR for this item body.
+    pub fn emit_mir<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
+        self.body()
+            .ok_or_else(|| io::Error::other(format!("No body found for `{}`", self.name())))?
+            .dump(w, &self.name())
+    }
+}
+
+/// Return the function where execution starts if the current
+/// crate defines that. This is usually `main`, but could be
+/// `start` if the crate is a no-std crate.
+pub fn entry_fn() -> Option<CrateItem> {
+    with(|cx| cx.entry_fn())
+}
+
+/// Access to the local crate.
+pub fn local_crate() -> Crate {
+    with(|cx| cx.local_crate())
+}
+
+/// Try to find a crate or crates if multiple crates exist from given name.
+pub fn find_crates(name: &str) -> Vec<Crate> {
+    with(|cx| cx.find_crates(name))
+}
+
+/// Try to find a crate with the given name.
+pub fn external_crates() -> Vec<Crate> {
+    with(|cx| cx.external_crates())
+}
+
+/// Retrieve all items in the local crate that have a MIR associated with them.
+pub fn all_local_items() -> CrateItems {
+    with(|cx| cx.all_local_items())
+}
+
+pub fn all_trait_decls() -> TraitDecls {
+    with(|cx| cx.all_trait_decls())
+}
+
+pub fn all_trait_impls() -> ImplTraitDecls {
+    with(|cx| cx.all_trait_impls())
+}
+
+/// A type that provides internal information but that can still be used for debug purpose.
+#[derive(Clone, PartialEq, Eq, Hash, Serialize)]
+pub struct Opaque(String);
+
+impl std::fmt::Display for Opaque {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.0)
+    }
+}
+
+impl std::fmt::Debug for Opaque {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.0)
+    }
+}
+
+pub fn opaque<T: Debug>(value: &T) -> Opaque {
+    Opaque(format!("{value:?}"))
+}
diff --git a/compiler/stable_mir/src/target.rs b/compiler/rustc_smir/src/stable_mir/target.rs
index 32c3a2a9122..6cf1e9feb01 100644
--- a/compiler/stable_mir/src/target.rs
+++ b/compiler/rustc_smir/src/stable_mir/target.rs
@@ -1,8 +1,9 @@
 //! Provide information about the machine that this is being compiled into.
 
 use serde::Serialize;
+use stable_mir::compiler_interface::with;
 
-use crate::compiler_interface::with;
+use crate::stable_mir;
 
 /// The properties of the target machine being compiled into.
 #[derive(Clone, PartialEq, Eq, Serialize)]
diff --git a/compiler/stable_mir/src/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs
index b857a735b72..1efa2fe13c5 100644
--- a/compiler/stable_mir/src/ty.rs
+++ b/compiler/rustc_smir/src/stable_mir/ty.rs
@@ -2,15 +2,16 @@ use std::fmt::{self, Debug, Display, Formatter};
 use std::ops::Range;
 
 use serde::Serialize;
+use stable_mir::abi::{FnAbi, Layout};
+use stable_mir::crate_def::{CrateDef, CrateDefItems, CrateDefType};
+use stable_mir::mir::alloc::{AllocId, read_target_int, read_target_uint};
+use stable_mir::mir::mono::StaticDef;
+use stable_mir::target::MachineInfo;
+use stable_mir::{Filename, Opaque};
 
 use super::mir::{Body, Mutability, Safety};
 use super::{DefId, Error, Symbol, with};
-use crate::abi::{FnAbi, Layout};
-use crate::crate_def::{CrateDef, CrateDefType};
-use crate::mir::alloc::{AllocId, read_target_int, read_target_uint};
-use crate::mir::mono::StaticDef;
-use crate::target::MachineInfo;
-use crate::{Filename, Opaque};
+use crate::stable_mir;
 
 #[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)]
 pub struct Ty(usize);
@@ -588,7 +589,7 @@ pub enum IntTy {
 impl IntTy {
     pub fn num_bytes(self) -> usize {
         match self {
-            IntTy::Isize => crate::target::MachineInfo::target_pointer_width().bytes(),
+            IntTy::Isize => MachineInfo::target_pointer_width().bytes(),
             IntTy::I8 => 1,
             IntTy::I16 => 2,
             IntTy::I32 => 4,
@@ -611,7 +612,7 @@ pub enum UintTy {
 impl UintTy {
     pub fn num_bytes(self) -> usize {
         match self {
-            UintTy::Usize => crate::target::MachineInfo::target_pointer_width().bytes(),
+            UintTy::Usize => MachineInfo::target_pointer_width().bytes(),
             UintTy::U8 => 1,
             UintTy::U16 => 2,
             UintTy::U32 => 4,
@@ -910,6 +911,10 @@ crate_def! {
     pub TraitDef;
 }
 
+impl_crate_def_items! {
+    TraitDef;
+}
+
 impl TraitDef {
     pub fn declaration(trait_def: &TraitDef) -> TraitDecl {
         with(|cx| cx.trait_decl(trait_def))
@@ -932,6 +937,10 @@ crate_def! {
     pub ImplDef;
 }
 
+impl_crate_def_items! {
+    ImplDef;
+}
+
 impl ImplDef {
     /// Retrieve information about this implementation.
     pub fn trait_impl(&self) -> ImplTrait {
@@ -1555,3 +1564,60 @@ index_impl!(Span);
 pub struct VariantIdx(usize);
 
 index_impl!(VariantIdx);
+
+crate_def! {
+    /// Hold infomation about an Opaque definition, particularly useful in `RPITIT`.
+    #[derive(Serialize)]
+    pub OpaqueDef;
+}
+
+crate_def! {
+    #[derive(Serialize)]
+    pub AssocDef;
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct AssocItem {
+    pub def_id: AssocDef,
+    pub name: Symbol,
+    pub kind: AssocKind,
+    pub container: AssocItemContainer,
+
+    /// If this is an item in an impl of a trait then this is the `DefId` of
+    /// the associated item on the trait that this implements.
+    pub trait_item_def_id: Option<AssocDef>,
+
+    /// Whether this is a method with an explicit self
+    /// as its first parameter, allowing method calls.
+    pub fn_has_self_parameter: bool,
+
+    /// `Some` if the associated item (an associated type) comes from the
+    /// return-position `impl Trait` in trait desugaring. The `ImplTraitInTraitData`
+    /// provides additional information about its source.
+    pub opt_rpitit_info: Option<ImplTraitInTraitData>,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum AssocKind {
+    Const,
+    Fn,
+    Type,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub enum AssocItemContainer {
+    Trait,
+    Impl,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)]
+pub enum ImplTraitInTraitData {
+    Trait { fn_def_id: FnDef, opaque_def_id: OpaqueDef },
+    Impl { fn_def_id: FnDef },
+}
+
+impl AssocItem {
+    pub fn is_impl_trait_in_trait(&self) -> bool {
+        self.opt_rpitit_info.is_some()
+    }
+}
diff --git a/compiler/stable_mir/src/visitor.rs b/compiler/rustc_smir/src/stable_mir/visitor.rs
index 8463174f9a4..31a53d1b19d 100644
--- a/compiler/stable_mir/src/visitor.rs
+++ b/compiler/rustc_smir/src/stable_mir/visitor.rs
@@ -1,11 +1,13 @@
 use std::ops::ControlFlow;
 
+use stable_mir::Opaque;
+use stable_mir::ty::TyConst;
+
 use super::ty::{
     Allocation, Binder, ConstDef, ExistentialPredicate, FnSig, GenericArgKind, GenericArgs,
     MirConst, Promoted, Region, RigidTy, TermKind, Ty, UnevaluatedConst,
 };
-use crate::Opaque;
-use crate::ty::TyConst;
+use crate::stable_mir;
 
 pub trait Visitor: Sized {
     type Break;
@@ -47,13 +49,13 @@ impl Visitable for TyConst {
     }
     fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
         match &self.kind {
-            crate::ty::TyConstKind::Param(_) | crate::ty::TyConstKind::Bound(_, _) => {}
-            crate::ty::TyConstKind::Unevaluated(_, args) => args.visit(visitor)?,
-            crate::ty::TyConstKind::Value(ty, alloc) => {
+            super::ty::TyConstKind::Param(_) | super::ty::TyConstKind::Bound(_, _) => {}
+            super::ty::TyConstKind::Unevaluated(_, args) => args.visit(visitor)?,
+            super::ty::TyConstKind::Value(ty, alloc) => {
                 alloc.visit(visitor)?;
                 ty.visit(visitor)?;
             }
-            crate::ty::TyConstKind::ZSTValue(ty) => ty.visit(visitor)?,
+            super::ty::TyConstKind::ZSTValue(ty) => ty.visit(visitor)?,
         }
         ControlFlow::Continue(())
     }
diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs
index 4390085cd04..9be16f8ce0c 100644
--- a/compiler/rustc_span/src/hygiene.rs
+++ b/compiler/rustc_span/src/hygiene.rs
@@ -24,9 +24,6 @@
 // because getting it wrong can lead to nested `HygieneData::with` calls that
 // trigger runtime aborts. (Fortunately these are obvious and easy to fix.)
 
-use std::cell::RefCell;
-use std::collections::hash_map::Entry;
-use std::collections::hash_set::Entry as SetEntry;
 use std::hash::Hash;
 use std::sync::Arc;
 use std::{fmt, iter, mem};
@@ -34,7 +31,7 @@ use std::{fmt, iter, mem};
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stable_hasher::{HashStable, HashingControls, StableHasher};
-use rustc_data_structures::sync::{Lock, WorkerLocal};
+use rustc_data_structures::sync::Lock;
 use rustc_data_structures::unhash::UnhashMap;
 use rustc_hashes::Hash64;
 use rustc_index::IndexVec;
@@ -61,8 +58,8 @@ impl !PartialOrd for SyntaxContext {}
 /// The other fields are only for caching.
 type SyntaxContextKey = (SyntaxContext, ExpnId, Transparency);
 
-#[derive(Clone, Copy, PartialEq, Debug, Encodable, Decodable)]
-pub struct SyntaxContextData {
+#[derive(Clone, Copy, Debug)]
+struct SyntaxContextData {
     outer_expn: ExpnId,
     outer_transparency: Transparency,
     parent: SyntaxContext,
@@ -74,7 +71,33 @@ pub struct SyntaxContextData {
     dollar_crate_name: Symbol,
 }
 
+/// Same as `SyntaxContextData`, but `opaque(_and_semitransparent)` cannot be recursive
+/// and use `None` if they need to refer to self. Used for encoding and decoding metadata.
+#[derive(Encodable, Decodable)]
+pub struct SyntaxContextDataNonRecursive {
+    outer_expn: ExpnId,
+    outer_transparency: Transparency,
+    parent: SyntaxContext,
+    opaque: Option<SyntaxContext>,
+    opaque_and_semitransparent: Option<SyntaxContext>,
+}
+
 impl SyntaxContextData {
+    fn new(
+        (parent, outer_expn, outer_transparency): SyntaxContextKey,
+        opaque: SyntaxContext,
+        opaque_and_semitransparent: SyntaxContext,
+    ) -> SyntaxContextData {
+        SyntaxContextData {
+            outer_expn,
+            outer_transparency,
+            parent,
+            opaque,
+            opaque_and_semitransparent,
+            dollar_crate_name: kw::DollarCrate,
+        }
+    }
+
     fn root() -> SyntaxContextData {
         SyntaxContextData {
             outer_expn: ExpnId::root(),
@@ -99,6 +122,19 @@ impl SyntaxContextData {
     }
 }
 
+impl SyntaxContextDataNonRecursive {
+    fn recursive(&self, ctxt: SyntaxContext) -> SyntaxContextData {
+        SyntaxContextData {
+            outer_expn: self.outer_expn,
+            outer_transparency: self.outer_transparency,
+            parent: self.parent,
+            opaque: self.opaque.unwrap_or(ctxt),
+            opaque_and_semitransparent: self.opaque_and_semitransparent.unwrap_or(ctxt),
+            dollar_crate_name: kw::DollarCrate,
+        }
+    }
+}
+
 rustc_index::newtype_index! {
     /// A unique ID associated with a macro invocation and expansion.
     #[orderable]
@@ -543,7 +579,7 @@ impl HygieneData {
     ) -> SyntaxContext {
         assert_ne!(expn_id, ExpnId::root());
         if transparency == Transparency::Opaque {
-            return self.apply_mark_internal(ctxt, expn_id, transparency);
+            return self.alloc_ctxt(ctxt, expn_id, transparency);
         }
 
         let call_site_ctxt = self.expn_data(expn_id).call_site.ctxt();
@@ -554,7 +590,7 @@ impl HygieneData {
         };
 
         if call_site_ctxt.is_root() {
-            return self.apply_mark_internal(ctxt, expn_id, transparency);
+            return self.alloc_ctxt(ctxt, expn_id, transparency);
         }
 
         // Otherwise, `expn_id` is a macros 1.0 definition and the call site is in a
@@ -567,74 +603,73 @@ impl HygieneData {
         //
         // See the example at `test/ui/hygiene/legacy_interaction.rs`.
         for (expn_id, transparency) in self.marks(ctxt) {
-            call_site_ctxt = self.apply_mark_internal(call_site_ctxt, expn_id, transparency);
+            call_site_ctxt = self.alloc_ctxt(call_site_ctxt, expn_id, transparency);
         }
-        self.apply_mark_internal(call_site_ctxt, expn_id, transparency)
+        self.alloc_ctxt(call_site_ctxt, expn_id, transparency)
     }
 
-    fn apply_mark_internal(
+    /// Allocate a new context with the given key, or retrieve it from cache if the given key
+    /// already exists. The auxiliary fields are calculated from the key.
+    fn alloc_ctxt(
         &mut self,
-        ctxt: SyntaxContext,
+        parent: SyntaxContext,
         expn_id: ExpnId,
         transparency: Transparency,
     ) -> SyntaxContext {
-        let syntax_context_data = &mut self.syntax_context_data;
-        debug_assert!(!syntax_context_data[ctxt.0 as usize].is_decode_placeholder());
-        let mut opaque = syntax_context_data[ctxt.0 as usize].opaque;
-        let mut opaque_and_semitransparent =
-            syntax_context_data[ctxt.0 as usize].opaque_and_semitransparent;
-
-        if transparency >= Transparency::Opaque {
-            let parent = opaque;
-            opaque = *self
-                .syntax_context_map
-                .entry((parent, expn_id, transparency))
-                .or_insert_with(|| {
-                    let new_opaque = SyntaxContext::from_usize(syntax_context_data.len());
-                    syntax_context_data.push(SyntaxContextData {
-                        outer_expn: expn_id,
-                        outer_transparency: transparency,
-                        parent,
-                        opaque: new_opaque,
-                        opaque_and_semitransparent: new_opaque,
-                        dollar_crate_name: kw::DollarCrate,
-                    });
-                    new_opaque
-                });
-        }
+        debug_assert!(!self.syntax_context_data[parent.0 as usize].is_decode_placeholder());
 
-        if transparency >= Transparency::SemiTransparent {
-            let parent = opaque_and_semitransparent;
-            opaque_and_semitransparent = *self
-                .syntax_context_map
-                .entry((parent, expn_id, transparency))
-                .or_insert_with(|| {
-                    let new_opaque_and_semitransparent =
-                        SyntaxContext::from_usize(syntax_context_data.len());
-                    syntax_context_data.push(SyntaxContextData {
-                        outer_expn: expn_id,
-                        outer_transparency: transparency,
-                        parent,
-                        opaque,
-                        opaque_and_semitransparent: new_opaque_and_semitransparent,
-                        dollar_crate_name: kw::DollarCrate,
-                    });
-                    new_opaque_and_semitransparent
-                });
+        // Look into the cache first.
+        let key = (parent, expn_id, transparency);
+        if let Some(ctxt) = self.syntax_context_map.get(&key) {
+            return *ctxt;
         }
 
-        let parent = ctxt;
-        *self.syntax_context_map.entry((parent, expn_id, transparency)).or_insert_with(|| {
-            syntax_context_data.push(SyntaxContextData {
-                outer_expn: expn_id,
-                outer_transparency: transparency,
-                parent,
-                opaque,
-                opaque_and_semitransparent,
-                dollar_crate_name: kw::DollarCrate,
-            });
-            SyntaxContext::from_usize(syntax_context_data.len() - 1)
-        })
+        // Reserve a new syntax context.
+        let ctxt = SyntaxContext::from_usize(self.syntax_context_data.len());
+        self.syntax_context_data.push(SyntaxContextData::decode_placeholder());
+        self.syntax_context_map.insert(key, ctxt);
+
+        // Opaque and semi-transparent versions of the parent. Note that they may be equal to the
+        // parent itself. E.g. `parent_opaque` == `parent` if the expn chain contains only opaques,
+        // and `parent_opaque_and_semitransparent` == `parent` if the expn contains only opaques
+        // and semi-transparents.
+        let parent_opaque = self.syntax_context_data[parent.0 as usize].opaque;
+        let parent_opaque_and_semitransparent =
+            self.syntax_context_data[parent.0 as usize].opaque_and_semitransparent;
+
+        // Evaluate opaque and semi-transparent versions of the new syntax context.
+        let (opaque, opaque_and_semitransparent) = match transparency {
+            Transparency::Transparent => (parent_opaque, parent_opaque_and_semitransparent),
+            Transparency::SemiTransparent => (
+                parent_opaque,
+                // Will be the same as `ctxt` if the expn chain contains only opaques and semi-transparents.
+                self.alloc_ctxt(parent_opaque_and_semitransparent, expn_id, transparency),
+            ),
+            Transparency::Opaque => (
+                // Will be the same as `ctxt` if the expn chain contains only opaques.
+                self.alloc_ctxt(parent_opaque, expn_id, transparency),
+                // Will be the same as `ctxt` if the expn chain contains only opaques and semi-transparents.
+                self.alloc_ctxt(parent_opaque_and_semitransparent, expn_id, transparency),
+            ),
+        };
+
+        // Fill the full data, now that we have it.
+        self.syntax_context_data[ctxt.as_u32() as usize] =
+            SyntaxContextData::new(key, opaque, opaque_and_semitransparent);
+        ctxt
+    }
+
+    fn non_recursive_ctxt(&self, ctxt: SyntaxContext) -> SyntaxContextDataNonRecursive {
+        debug_assert!(!self.syntax_context_data[ctxt.0 as usize].is_decode_placeholder());
+        let data = &self.syntax_context_data[ctxt.0 as usize];
+        SyntaxContextDataNonRecursive {
+            outer_expn: data.outer_expn,
+            outer_transparency: data.outer_transparency,
+            parent: data.parent,
+            opaque: (data.opaque != ctxt).then_some(data.opaque),
+            opaque_and_semitransparent: (data.opaque_and_semitransparent != ctxt)
+                .then_some(data.opaque_and_semitransparent),
+        }
     }
 }
 
@@ -1265,7 +1300,7 @@ impl HygieneEncodeContext {
     pub fn encode<T>(
         &self,
         encoder: &mut T,
-        mut encode_ctxt: impl FnMut(&mut T, u32, &SyntaxContextData),
+        mut encode_ctxt: impl FnMut(&mut T, u32, &SyntaxContextDataNonRecursive),
         mut encode_expn: impl FnMut(&mut T, ExpnId, &ExpnData, ExpnHash),
     ) {
         // When we serialize a `SyntaxContextData`, we may end up serializing
@@ -1314,18 +1349,12 @@ struct HygieneDecodeContextInner {
     // so that multiple occurrences of the same serialized id are decoded to the same
     // `SyntaxContext`. This only stores `SyntaxContext`s which are completely decoded.
     remapped_ctxts: Vec<Option<SyntaxContext>>,
-
-    /// Maps serialized `SyntaxContext` ids that are currently being decoded to a `SyntaxContext`.
-    decoding: FxHashMap<u32, SyntaxContext>,
 }
 
 #[derive(Default)]
 /// Additional information used to assist in decoding hygiene data
 pub struct HygieneDecodeContext {
     inner: Lock<HygieneDecodeContextInner>,
-
-    /// A set of serialized `SyntaxContext` ids that are currently being decoded on each thread.
-    local_in_progress: WorkerLocal<RefCell<FxHashSet<u32>>>,
 }
 
 /// Register an expansion which has been decoded from the on-disk-cache for the local crate.
@@ -1396,7 +1425,10 @@ pub fn decode_expn_id(
 // to track which `SyntaxContext`s we have already decoded.
 // The provided closure will be invoked to deserialize a `SyntaxContextData`
 // if we haven't already seen the id of the `SyntaxContext` we are deserializing.
-pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContextData>(
+pub fn decode_syntax_context<
+    D: Decoder,
+    F: FnOnce(&mut D, u32) -> SyntaxContextDataNonRecursive,
+>(
     d: &mut D,
     context: &HygieneDecodeContext,
     decode_data: F,
@@ -1408,112 +1440,43 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext
         return SyntaxContext::root();
     }
 
-    let pending_ctxt = {
-        let mut inner = context.inner.lock();
-
-        // Reminder: `HygieneDecodeContext` is per-crate, so there are no collisions between
-        // raw ids from different crate metadatas.
-        if let Some(ctxt) = inner.remapped_ctxts.get(raw_id as usize).copied().flatten() {
-            // This has already been decoded.
-            return ctxt;
-        }
-
-        match inner.decoding.entry(raw_id) {
-            Entry::Occupied(ctxt_entry) => {
-                let pending_ctxt = *ctxt_entry.get();
-                match context.local_in_progress.borrow_mut().entry(raw_id) {
-                    // We're decoding this already on the current thread. Return here and let the
-                    // function higher up the stack finish decoding to handle recursive cases.
-                    // Hopefully having a `SyntaxContext` that refers to an incorrect data is ok
-                    // during reminder of the decoding process, it's certainly not ok after the
-                    // top level decoding function returns.
-                    SetEntry::Occupied(..) => return pending_ctxt,
-                    // Some other thread is currently decoding this.
-                    // Race with it (alternatively we could wait here).
-                    // We cannot return this value, unlike in the recursive case above, because it
-                    // may expose a `SyntaxContext` pointing to incorrect data to arbitrary code.
-                    SetEntry::Vacant(entry) => {
-                        entry.insert();
-                        pending_ctxt
-                    }
-                }
-            }
-            Entry::Vacant(entry) => {
-                // We are the first thread to start decoding. Mark the current thread as being progress.
-                context.local_in_progress.borrow_mut().insert(raw_id);
-
-                // Allocate and store SyntaxContext id *before* calling the decoder function,
-                // as the SyntaxContextData may reference itself.
-                let new_ctxt = HygieneData::with(|hygiene_data| {
-                    // Push a dummy SyntaxContextData to ensure that nobody else can get the
-                    // same ID as us. This will be overwritten after call `decode_data`.
-                    hygiene_data.syntax_context_data.push(SyntaxContextData::decode_placeholder());
-                    SyntaxContext::from_usize(hygiene_data.syntax_context_data.len() - 1)
-                });
-                entry.insert(new_ctxt);
-                new_ctxt
-            }
-        }
-    };
+    // Reminder: `HygieneDecodeContext` is per-crate, so there are no collisions between
+    // raw ids from different crate metadatas.
+    if let Some(ctxt) = context.inner.lock().remapped_ctxts.get(raw_id as usize).copied().flatten()
+    {
+        // This has already been decoded.
+        return ctxt;
+    }
 
     // Don't try to decode data while holding the lock, since we need to
     // be able to recursively decode a SyntaxContext
     let ctxt_data = decode_data(d, raw_id);
-    let ctxt_key = ctxt_data.key();
 
     let ctxt = HygieneData::with(|hygiene_data| {
-        match hygiene_data.syntax_context_map.get(&ctxt_key) {
-            // Ensure that syntax contexts are unique.
-            // If syntax contexts with the given key already exists, reuse it instead of
-            // using `pending_ctxt`.
-            // `pending_ctxt` will leave an unused hole in the vector of syntax contexts.
-            // Hopefully its value isn't stored anywhere during decoding and its dummy data
-            // is never accessed later. The `is_decode_placeholder` asserts on all
-            // accesses to syntax context data attempt to ensure it.
-            Some(&ctxt) => ctxt,
-            // This is a completely new context.
-            // Overwrite its placeholder data with our decoded data.
-            None => {
-                let ctxt_data_ref =
-                    &mut hygiene_data.syntax_context_data[pending_ctxt.as_u32() as usize];
-                let prev_ctxt_data = mem::replace(ctxt_data_ref, ctxt_data);
-                // Reset `dollar_crate_name` so that it will be updated by `update_dollar_crate_names`.
-                // We don't care what the encoding crate set this to - we want to resolve it
-                // from the perspective of the current compilation session.
-                ctxt_data_ref.dollar_crate_name = kw::DollarCrate;
-                // Make sure nothing weird happened while `decode_data` was running.
-                if !prev_ctxt_data.is_decode_placeholder() {
-                    // Another thread may have already inserted the decoded data,
-                    // but the decoded data should match.
-                    assert_eq!(prev_ctxt_data, *ctxt_data_ref);
-                }
-                hygiene_data.syntax_context_map.insert(ctxt_key, pending_ctxt);
-                pending_ctxt
-            }
-        }
+        let ctxt_key = (ctxt_data.parent, ctxt_data.outer_expn, ctxt_data.outer_transparency);
+        *hygiene_data.syntax_context_map.entry(ctxt_key).or_insert_with(|| {
+            let ctxt = SyntaxContext::from_usize(hygiene_data.syntax_context_data.len());
+            hygiene_data.syntax_context_data.push(ctxt_data.recursive(ctxt));
+            ctxt
+        })
     });
 
-    // Mark the context as completed
-    context.local_in_progress.borrow_mut().remove(&raw_id);
-
     let mut inner = context.inner.lock();
     let new_len = raw_id as usize + 1;
     if inner.remapped_ctxts.len() < new_len {
         inner.remapped_ctxts.resize(new_len, None);
     }
     inner.remapped_ctxts[raw_id as usize] = Some(ctxt);
-    inner.decoding.remove(&raw_id);
 
     ctxt
 }
 
-fn for_all_ctxts_in<F: FnMut(u32, SyntaxContext, &SyntaxContextData)>(
+fn for_all_ctxts_in<F: FnMut(u32, SyntaxContext, &SyntaxContextDataNonRecursive)>(
     ctxts: impl Iterator<Item = SyntaxContext>,
     mut f: F,
 ) {
-    let all_data: Vec<_> = HygieneData::with(|data| {
-        ctxts.map(|ctxt| (ctxt, data.syntax_context_data[ctxt.0 as usize].clone())).collect()
-    });
+    let all_data: Vec<_> =
+        HygieneData::with(|data| ctxts.map(|ctxt| (ctxt, data.non_recursive_ctxt(ctxt))).collect());
     for (ctxt, data) in all_data.into_iter() {
         f(ctxt.0, ctxt, &data);
     }
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index f19d4d9f362..9e6ba2e1b9c 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -876,7 +876,7 @@ impl Span {
         self.ctxt()
             .outer_expn_data()
             .allow_internal_unstable
-            .is_some_and(|features| features.iter().any(|&f| f == feature))
+            .is_some_and(|features| features.contains(&feature))
     }
 
     /// Checks if this span arises from a compiler desugaring of kind `kind`.
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 6807f96e712..31847ae3b46 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -131,7 +131,7 @@ symbols! {
         // tidy-alphabetical-end
 
         // Weak keywords, have special meaning only in specific contexts.
-        // Matching predicates: none
+        // Matching predicates: `is_weak`
         // tidy-alphabetical-start
         Auto:               "auto",
         Builtin:            "builtin",
@@ -800,6 +800,15 @@ symbols! {
         default_fn,
         default_lib_allocator,
         default_method_body_is_const,
+        // --------------------------
+        // Lang items which are used only for experiments with auto traits with default bounds.
+        // These lang items are not actually defined in core/std. Experiment is a part of
+        // `MCP: Low level components for async drop`(https://github.com/rust-lang/compiler-team/issues/727)
+        default_trait1,
+        default_trait2,
+        default_trait3,
+        default_trait4,
+        // --------------------------
         default_type_parameter_fallback,
         default_type_params,
         define_opaque,
@@ -986,6 +995,7 @@ symbols! {
         field_init_shorthand,
         file,
         file_options,
+        flags,
         float,
         float_to_int_unchecked,
         floorf128,
@@ -1377,6 +1387,7 @@ symbols! {
         movbe_target_feature,
         move_ref_pattern,
         move_size_limit,
+        movrs_target_feature,
         mul,
         mul_assign,
         mul_with_overflow,
@@ -1570,6 +1581,7 @@ symbols! {
         pointer_like,
         poll,
         poll_next,
+        position,
         post_dash_lto: "post-lto",
         postfix_match,
         powerpc_target_feature,
@@ -1585,6 +1597,7 @@ symbols! {
         precise_capturing,
         precise_capturing_in_traits,
         precise_pointer_size_matching,
+        precision,
         pref_align_of,
         prefetch_read_data,
         prefetch_read_instruction,
@@ -1771,6 +1784,7 @@ symbols! {
         rustc_deallocator,
         rustc_def_path,
         rustc_default_body_unstable,
+        rustc_delayed_bug_from_inside_query,
         rustc_deny_explicit_impl,
         rustc_deprecated_safe_2024,
         rustc_diagnostic_item,
@@ -1787,7 +1801,6 @@ symbols! {
         rustc_dump_user_args,
         rustc_dump_vtable,
         rustc_effective_visibility,
-        rustc_error,
         rustc_evaluate_where_clauses,
         rustc_expected_cgu_reuse,
         rustc_force_inline,
@@ -2036,6 +2049,7 @@ symbols! {
         sub_assign,
         sub_with_overflow,
         suggestion,
+        super_let,
         supertrait_item_shadowing,
         surface_async_drop_in_place,
         sym,
@@ -2274,6 +2288,7 @@ symbols! {
         wasm_target_feature,
         where_clause_attrs,
         while_let,
+        width,
         windows,
         windows_subsystem,
         with_negative_coherence,
@@ -2721,6 +2736,10 @@ impl Symbol {
             || self.is_unused_keyword_conditional(edition)
     }
 
+    pub fn is_weak(self) -> bool {
+        self >= kw::Auto && self <= kw::Yeet
+    }
+
     /// A keyword or reserved identifier that can be used as a path segment.
     pub fn is_path_segment_keyword(self) -> bool {
         self == kw::Super
diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs
index 88754f1f15b..2802e891807 100644
--- a/compiler/rustc_symbol_mangling/src/legacy.rs
+++ b/compiler/rustc_symbol_mangling/src/legacy.rs
@@ -28,7 +28,10 @@ pub(super) fn mangle<'tcx>(
     loop {
         let key = tcx.def_key(ty_def_id);
         match key.disambiguated_data.data {
-            DefPathData::TypeNs(_) | DefPathData::ValueNs(_) | DefPathData::Closure => {
+            DefPathData::TypeNs(_)
+            | DefPathData::ValueNs(_)
+            | DefPathData::Closure
+            | DefPathData::SyntheticCoroutineBody => {
                 instance_ty = tcx.type_of(ty_def_id).instantiate_identity();
                 debug!(?instance_ty);
                 break;
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index d824a23279b..99d44bcd7eb 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -850,6 +850,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
             DefPathData::Ctor => 'c',
             DefPathData::AnonConst => 'k',
             DefPathData::OpaqueTy => 'i',
+            DefPathData::SyntheticCoroutineBody => 's',
 
             // These should never show up as `path_append` arguments.
             DefPathData::CrateRoot
diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs
index cdccb3e5d72..43a8d9ca119 100644
--- a/compiler/rustc_target/src/asm/aarch64.rs
+++ b/compiler/rustc_target/src/asm/aarch64.rs
@@ -78,7 +78,7 @@ pub(crate) fn target_reserves_x18(target: &Target, target_features: &FxIndexSet<
     target.os == "android"
         || target.os == "fuchsia"
         || target.env == "ohos"
-        || target.is_like_osx
+        || target.is_like_darwin
         || target.is_like_windows
         || target_features.contains(&sym::reserve_x18)
 }
diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs
index ff0cbddecf7..7fea10ff067 100644
--- a/compiler/rustc_target/src/asm/arm.rs
+++ b/compiler/rustc_target/src/asm/arm.rs
@@ -68,7 +68,7 @@ impl ArmInlineAsmRegClass {
 
 // This uses the same logic as useR7AsFramePointer in LLVM
 fn frame_pointer_is_r7(target_features: &FxIndexSet<Symbol>, target: &Target) -> bool {
-    target.is_like_osx || (!target.is_like_windows && target_features.contains(&sym::thumb_mode))
+    target.is_like_darwin || (!target.is_like_windows && target_features.contains(&sym::thumb_mode))
 }
 
 fn frame_pointer_r11(
diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs
index a52b2b76bc1..55e39d093e2 100644
--- a/compiler/rustc_target/src/callconv/mod.rs
+++ b/compiler/rustc_target/src/callconv/mod.rs
@@ -670,7 +670,7 @@ impl<'a, Ty> FnAbi<'a, Ty> {
                 }
             },
             "aarch64" | "arm64ec" => {
-                let kind = if cx.target_spec().is_like_osx {
+                let kind = if cx.target_spec().is_like_darwin {
                     aarch64::AbiKind::DarwinPCS
                 } else if cx.target_spec().is_like_windows {
                     aarch64::AbiKind::Win64
diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs
index 6f112b49400..ba3c1406211 100644
--- a/compiler/rustc_target/src/callconv/x86.rs
+++ b/compiler/rustc_target/src/callconv/x86.rs
@@ -104,7 +104,7 @@ where
             let byval_align = if arg.layout.align.abi < align_4 {
                 // (1.)
                 align_4
-            } else if t.is_like_osx && contains_vector(cx, arg.layout) {
+            } else if t.is_like_darwin && contains_vector(cx, arg.layout) {
                 // (3.)
                 align_16
             } else {
diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs
index a8d7da5692d..df99280f571 100644
--- a/compiler/rustc_target/src/lib.rs
+++ b/compiler/rustc_target/src/lib.rs
@@ -12,6 +12,7 @@
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![doc(rust_logo)]
 #![feature(assert_matches)]
+#![feature(debug_closure_helpers)]
 #![feature(iter_intersperse)]
 #![feature(let_chains)]
 #![feature(rustc_attrs)]
diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs
index 66c85146c29..46fcd7d5c51 100644
--- a/compiler/rustc_target/src/spec/base/apple/mod.rs
+++ b/compiler/rustc_target/src/spec/base/apple/mod.rs
@@ -1,9 +1,12 @@
 use std::borrow::Cow;
 use std::env;
+use std::fmt::{Display, from_fn};
+use std::num::ParseIntError;
+use std::str::FromStr;
 
 use crate::spec::{
     BinaryFormat, Cc, DebuginfoKind, FloatAbi, FramePointer, LinkerFlavor, Lld, RustcAbi,
-    SplitDebuginfo, StackProbeType, StaticCow, TargetOptions, cvs,
+    SplitDebuginfo, StackProbeType, StaticCow, Target, TargetOptions, cvs,
 };
 
 #[cfg(test)]
@@ -115,7 +118,7 @@ pub(crate) fn base(
         function_sections: false,
         dynamic_linking: true,
         families: cvs!["unix"],
-        is_like_osx: true,
+        is_like_darwin: true,
         binary_format: BinaryFormat::MachO,
         // LLVM notes that macOS 10.11+ and iOS 9+ default
         // to v4, so we do the same.
@@ -222,3 +225,107 @@ fn link_env_remove(os: &'static str) -> StaticCow<[StaticCow<str>]> {
         cvs!["MACOSX_DEPLOYMENT_TARGET"]
     }
 }
+
+/// Deployment target or SDK version.
+///
+/// The size of the numbers in here are limited by Mach-O's `LC_BUILD_VERSION`.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct OSVersion {
+    pub major: u16,
+    pub minor: u8,
+    pub patch: u8,
+}
+
+impl FromStr for OSVersion {
+    type Err = ParseIntError;
+
+    /// Parse an OS version triple (SDK version or deployment target).
+    fn from_str(version: &str) -> Result<Self, ParseIntError> {
+        if let Some((major, minor)) = version.split_once('.') {
+            let major = major.parse()?;
+            if let Some((minor, patch)) = minor.split_once('.') {
+                Ok(Self { major, minor: minor.parse()?, patch: patch.parse()? })
+            } else {
+                Ok(Self { major, minor: minor.parse()?, patch: 0 })
+            }
+        } else {
+            Ok(Self { major: version.parse()?, minor: 0, patch: 0 })
+        }
+    }
+}
+
+impl OSVersion {
+    pub fn new(major: u16, minor: u8, patch: u8) -> Self {
+        Self { major, minor, patch }
+    }
+
+    pub fn fmt_pretty(self) -> impl Display {
+        let Self { major, minor, patch } = self;
+        from_fn(move |f| {
+            write!(f, "{major}.{minor}")?;
+            if patch != 0 {
+                write!(f, ".{patch}")?;
+            }
+            Ok(())
+        })
+    }
+
+    pub fn fmt_full(self) -> impl Display {
+        let Self { major, minor, patch } = self;
+        from_fn(move |f| write!(f, "{major}.{minor}.{patch}"))
+    }
+
+    /// Minimum operating system versions currently supported by `rustc`.
+    pub fn os_minimum_deployment_target(os: &str) -> Self {
+        // When bumping a version in here, remember to update the platform-support docs too.
+        //
+        // NOTE: The defaults may change in future `rustc` versions, so if you are looking for the
+        // default deployment target, prefer:
+        // ```
+        // $ rustc --print deployment-target
+        // ```
+        let (major, minor, patch) = match os {
+            "macos" => (10, 12, 0),
+            "ios" => (10, 0, 0),
+            "tvos" => (10, 0, 0),
+            "watchos" => (5, 0, 0),
+            "visionos" => (1, 0, 0),
+            _ => unreachable!("tried to get deployment target for non-Apple platform"),
+        };
+        Self { major, minor, patch }
+    }
+
+    /// The deployment target for the given target.
+    ///
+    /// This is similar to `os_minimum_deployment_target`, except that on certain targets it makes sense
+    /// to raise the minimum OS version.
+    ///
+    /// This matches what LLVM does, see in part:
+    /// <https://github.com/llvm/llvm-project/blob/llvmorg-18.1.8/llvm/lib/TargetParser/Triple.cpp#L1900-L1932>
+    pub fn minimum_deployment_target(target: &Target) -> Self {
+        let (major, minor, patch) = match (&*target.os, &*target.arch, &*target.abi) {
+            ("macos", "aarch64", _) => (11, 0, 0),
+            ("ios", "aarch64", "macabi") => (14, 0, 0),
+            ("ios", "aarch64", "sim") => (14, 0, 0),
+            ("ios", _, _) if target.llvm_target.starts_with("arm64e") => (14, 0, 0),
+            // Mac Catalyst defaults to 13.1 in Clang.
+            ("ios", _, "macabi") => (13, 1, 0),
+            ("tvos", "aarch64", "sim") => (14, 0, 0),
+            ("watchos", "aarch64", "sim") => (7, 0, 0),
+            (os, _, _) => return Self::os_minimum_deployment_target(os),
+        };
+        Self { major, minor, patch }
+    }
+}
+
+/// Name of the environment variable used to fetch the deployment target on the given OS.
+pub fn deployment_target_env_var(os: &str) -> &'static str {
+    match os {
+        "macos" => "MACOSX_DEPLOYMENT_TARGET",
+        "ios" => "IPHONEOS_DEPLOYMENT_TARGET",
+        "watchos" => "WATCHOS_DEPLOYMENT_TARGET",
+        "tvos" => "TVOS_DEPLOYMENT_TARGET",
+        "visionos" => "XROS_DEPLOYMENT_TARGET",
+        _ => unreachable!("tried to get deployment target env var for non-Apple platform"),
+    }
+}
diff --git a/compiler/rustc_target/src/spec/base/apple/tests.rs b/compiler/rustc_target/src/spec/base/apple/tests.rs
index 7a985ad4dc0..391f3470104 100644
--- a/compiler/rustc_target/src/spec/base/apple/tests.rs
+++ b/compiler/rustc_target/src/spec/base/apple/tests.rs
@@ -1,3 +1,4 @@
+use super::OSVersion;
 use crate::spec::targets::{
     aarch64_apple_darwin, aarch64_apple_ios_sim, aarch64_apple_visionos_sim,
     aarch64_apple_watchos_sim, i686_apple_darwin, x86_64_apple_darwin, x86_64_apple_ios,
@@ -42,3 +43,11 @@ fn macos_link_environment_unmodified() {
         );
     }
 }
+
+#[test]
+fn test_parse_version() {
+    assert_eq!("10".parse(), Ok(OSVersion::new(10, 0, 0)));
+    assert_eq!("10.12".parse(), Ok(OSVersion::new(10, 12, 0)));
+    assert_eq!("10.12.6".parse(), Ok(OSVersion::new(10, 12, 6)));
+    assert_eq!("9999.99.99".parse(), Ok(OSVersion::new(9999, 99, 99)));
+}
diff --git a/compiler/rustc_target/src/spec/base/mod.rs b/compiler/rustc_target/src/spec/base/mod.rs
index e8fdc871785..71b6528c2dd 100644
--- a/compiler/rustc_target/src/spec/base/mod.rs
+++ b/compiler/rustc_target/src/spec/base/mod.rs
@@ -1,6 +1,6 @@
 pub(crate) mod aix;
 pub(crate) mod android;
-pub(crate) mod apple;
+pub mod apple;
 pub(crate) mod avr;
 pub(crate) mod bpf;
 pub(crate) mod cygwin;
diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs
index 4b6de5e18f5..be71da76b4a 100644
--- a/compiler/rustc_target/src/spec/json.rs
+++ b/compiler/rustc_target/src/spec/json.rs
@@ -598,7 +598,7 @@ impl Target {
         key!(families, target_families);
         key!(abi_return_struct_as_int, bool);
         key!(is_like_aix, bool);
-        key!(is_like_osx, bool);
+        key!(is_like_darwin, bool);
         key!(is_like_solaris, bool);
         key!(is_like_windows, bool);
         key!(is_like_msvc, bool);
@@ -777,7 +777,7 @@ impl ToJson for Target {
         target_option_val!(families, "target-family");
         target_option_val!(abi_return_struct_as_int);
         target_option_val!(is_like_aix);
-        target_option_val!(is_like_osx);
+        target_option_val!(is_like_darwin);
         target_option_val!(is_like_solaris);
         target_option_val!(is_like_windows);
         target_option_val!(is_like_msvc);
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 7234d1dc63e..79f73ef28b3 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -60,6 +60,7 @@ pub mod crt_objects;
 mod base;
 mod json;
 
+pub use base::apple;
 pub use base::avr::ef_avr_arch;
 
 /// Linker is called through a C/C++ compiler.
@@ -81,7 +82,7 @@ pub enum Lld {
 /// of classes that we call "linker flavors".
 ///
 /// Technically, it's not even necessary, we can nearly always infer the flavor from linker name
-/// and target properties like `is_like_windows`/`is_like_osx`/etc. However, the PRs originally
+/// and target properties like `is_like_windows`/`is_like_darwin`/etc. However, the PRs originally
 /// introducing `-Clinker-flavor` (#40018 and friends) were aiming to reduce this kind of inference
 /// and provide something certain and explicitly specified instead, and that design goal is still
 /// relevant now.
@@ -2406,7 +2407,7 @@ pub struct TargetOptions {
     /// in particular running dsymutil and some other stuff like `-dead_strip`. Defaults to false.
     /// Also indicates whether to use Apple-specific ABI changes, such as extending function
     /// parameters to 32-bits.
-    pub is_like_osx: bool,
+    pub is_like_darwin: bool,
     /// Whether the target toolchain is like Solaris's.
     /// Only useful for compiling against Illumos/Solaris,
     /// as they have a different set of linker flags. Defaults to false.
@@ -2700,7 +2701,7 @@ fn add_link_args(link_args: &mut LinkArgs, flavor: LinkerFlavor, args: &[&'stati
 impl TargetOptions {
     pub fn supports_comdat(&self) -> bool {
         // XCOFF and MachO don't support COMDAT.
-        !self.is_like_aix && !self.is_like_osx
+        !self.is_like_aix && !self.is_like_darwin
     }
 }
 
@@ -2804,7 +2805,7 @@ impl Default for TargetOptions {
             families: cvs![],
             abi_return_struct_as_int: false,
             is_like_aix: false,
-            is_like_osx: false,
+            is_like_darwin: false,
             is_like_solaris: false,
             is_like_windows: false,
             is_like_msvc: false,
@@ -3070,9 +3071,9 @@ impl Target {
         }
 
         check_eq!(
-            self.is_like_osx,
+            self.is_like_darwin,
             self.vendor == "apple",
-            "`is_like_osx` must be set if and only if `vendor` is `apple`"
+            "`is_like_darwin` must be set if and only if `vendor` is `apple`"
         );
         check_eq!(
             self.is_like_solaris,
@@ -3098,9 +3099,9 @@ impl Target {
 
         // Check that default linker flavor is compatible with some other key properties.
         check_eq!(
-            self.is_like_osx,
+            self.is_like_darwin,
             matches!(self.linker_flavor, LinkerFlavor::Darwin(..)),
-            "`linker_flavor` must be `darwin` if and only if `is_like_osx` is set"
+            "`linker_flavor` must be `darwin` if and only if `is_like_darwin` is set"
         );
         check_eq!(
             self.is_like_msvc,
@@ -3516,7 +3517,7 @@ impl Target {
                     Err("the `i586-pc-windows-msvc` target has been removed. Use the `i686-pc-windows-msvc` target instead.\n\
                         Windows 10 (the minimum required OS version) requires a CPU baseline of at least i686 so you can safely switch".into())
                 } else {
-                    Err(format!("Could not find specification for target {target_tuple:?}"))
+                    Err(format!("could not find specification for target {target_tuple:?}"))
                 }
             }
             TargetTuple::TargetJson { ref contents, .. } => {
diff --git a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs
index dbe1540364a..860629b08e1 100644
--- a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs
+++ b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs
@@ -2,7 +2,7 @@ use crate::spec::{FloatAbi, Target, TargetMetadata, TargetOptions, base};
 
 pub(crate) fn target() -> Target {
     Target {
-        llvm_target: "armv5te-unknown-linux-uclibcgnueabi".into(),
+        llvm_target: "armv5te-unknown-linux-gnueabi".into(),
         metadata: TargetMetadata {
             description: Some("Armv5TE Linux with uClibc".into()),
             tier: Some(3),
diff --git a/compiler/rustc_target/src/spec/targets/armv7_sony_vita_newlibeabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_sony_vita_newlibeabihf.rs
index 5d292bbf8ad..6a83835059e 100644
--- a/compiler/rustc_target/src/spec/targets/armv7_sony_vita_newlibeabihf.rs
+++ b/compiler/rustc_target/src/spec/targets/armv7_sony_vita_newlibeabihf.rs
@@ -15,7 +15,7 @@ pub(crate) fn target() -> Target {
     );
 
     Target {
-        llvm_target: "thumbv7a-vita-eabihf".into(),
+        llvm_target: "thumbv7a-sony-vita-eabihf".into(),
         metadata: TargetMetadata {
             description: Some(
                 "Armv7-A Cortex-A9 Sony PlayStation Vita (requires VITASDK toolchain)".into(),
diff --git a/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs
index 2a26323e514..e775c8fc524 100644
--- a/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs
+++ b/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs
@@ -20,7 +20,7 @@ pub(crate) fn target() -> Target {
         llvm_target: "i686-pc-windows-gnu".into(),
         metadata: TargetMetadata {
             description: Some("32-bit MinGW (Windows 10+)".into()),
-            tier: Some(1),
+            tier: Some(2),
             host_tools: Some(true),
             std: Some(true),
         },
diff --git a/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs b/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs
index 1300280e35b..71b3fbe00b2 100644
--- a/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs
@@ -23,6 +23,7 @@ pub(crate) fn target() -> Target {
         data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
         arch: "mips64".into(),
         options: TargetOptions {
+            vendor: "openwrt".into(),
             abi: "abi64".into(),
             endian: Endian::Big,
             mcount: "_mcount".into(),
diff --git a/compiler/rustc_target/src/spec/targets/mips_unknown_linux_uclibc.rs b/compiler/rustc_target/src/spec/targets/mips_unknown_linux_uclibc.rs
index 0955b3debea..c14bfbf46d2 100644
--- a/compiler/rustc_target/src/spec/targets/mips_unknown_linux_uclibc.rs
+++ b/compiler/rustc_target/src/spec/targets/mips_unknown_linux_uclibc.rs
@@ -4,7 +4,7 @@ use crate::spec::{Target, TargetMetadata, TargetOptions, base};
 
 pub(crate) fn target() -> Target {
     Target {
-        llvm_target: "mips-unknown-linux-uclibc".into(),
+        llvm_target: "mips-unknown-linux-gnu".into(),
         metadata: TargetMetadata {
             description: Some("MIPS Linux with uClibc".into()),
             tier: Some(3),
diff --git a/compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_uclibc.rs b/compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_uclibc.rs
index 08c4347fe01..f0056799d66 100644
--- a/compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_uclibc.rs
+++ b/compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_uclibc.rs
@@ -2,7 +2,7 @@ use crate::spec::{Target, TargetMetadata, TargetOptions, base};
 
 pub(crate) fn target() -> Target {
     Target {
-        llvm_target: "mipsel-unknown-linux-uclibc".into(),
+        llvm_target: "mipsel-unknown-linux-gnu".into(),
         metadata: TargetMetadata {
             description: Some("MIPS (LE) Linux with uClibc".into()),
             tier: Some(3),
diff --git a/compiler/rustc_target/src/spec/targets/riscv32_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/riscv32_wrs_vxworks.rs
index 8a4bc58e546..efc17d8d083 100644
--- a/compiler/rustc_target/src/spec/targets/riscv32_wrs_vxworks.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv32_wrs_vxworks.rs
@@ -16,7 +16,7 @@ pub(crate) fn target() -> Target {
             cpu: "generic-rv32".into(),
             llvm_abiname: "ilp32d".into(),
             max_atomic_width: Some(32),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             stack_probes: StackProbeType::Inline,
             ..base::vxworks::opts()
         },
diff --git a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs
index 6dda346aaaf..5b7feef70d0 100644
--- a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs
@@ -17,7 +17,7 @@ pub(crate) fn target() -> Target {
         options: TargetOptions {
             code_model: Some(CodeModel::Medium),
             cpu: "generic-rv32".into(),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             llvm_abiname: "ilp32d".into(),
             max_atomic_width: Some(32),
             supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]),
diff --git a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs
index ba10e3c6881..938b39b10c6 100644
--- a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs
@@ -19,7 +19,7 @@ pub(crate) fn target() -> Target {
         options: TargetOptions {
             code_model: Some(CodeModel::Medium),
             cpu: "generic-rv32".into(),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             llvm_abiname: "ilp32d".into(),
             max_atomic_width: Some(32),
             supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]),
diff --git a/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs b/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs
index c8ef737b9e7..9f02ed4bcbe 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs
@@ -19,7 +19,7 @@ pub(crate) fn target() -> Target {
         options: TargetOptions {
             code_model: Some(CodeModel::Medium),
             cpu: "generic-rv64".into(),
-            features: "+m,+a,+f,+d,+c,+zba,+zbb,+zbs,+v".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei,+zba,+zbb,+zbs,+v".into(),
             llvm_abiname: "lp64d".into(),
             supported_sanitizers: SanitizerSet::ADDRESS,
             max_atomic_width: Some(64),
diff --git a/compiler/rustc_target/src/spec/targets/riscv64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/riscv64_wrs_vxworks.rs
index 39aa70035e4..8d8c21952de 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64_wrs_vxworks.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64_wrs_vxworks.rs
@@ -16,7 +16,7 @@ pub(crate) fn target() -> Target {
             cpu: "generic-rv64".into(),
             llvm_abiname: "lp64d".into(),
             max_atomic_width: Some(64),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             stack_probes: StackProbeType::Inline,
             ..base::vxworks::opts()
         },
diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs
index ecf65677531..e628095b88a 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs
@@ -15,7 +15,7 @@ pub(crate) fn target() -> Target {
         options: TargetOptions {
             code_model: Some(CodeModel::Medium),
             cpu: "generic-rv64".into(),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             llvm_abiname: "lp64d".into(),
             max_atomic_width: Some(64),
             ..base::freebsd::opts()
diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs
index e260237ca77..c4466e13d14 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs
@@ -4,7 +4,7 @@ pub(crate) fn target() -> Target {
     let mut base = base::fuchsia::opts();
     base.code_model = Some(CodeModel::Medium);
     base.cpu = "generic-rv64".into();
-    base.features = "+m,+a,+f,+d,+c".into();
+    base.features = "+m,+a,+f,+d,+c,+zicsr,+zifencei".into();
     base.llvm_abiname = "lp64d".into();
     base.max_atomic_width = Some(64);
     base.stack_probes = StackProbeType::Inline;
diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_hermit.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_hermit.rs
index 88b5dca284a..5c15bdd9f64 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_hermit.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_hermit.rs
@@ -14,7 +14,7 @@ pub(crate) fn target() -> Target {
         data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(),
         options: TargetOptions {
             cpu: "generic-rv64".into(),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             relocation_model: RelocModel::Pic,
             code_model: Some(CodeModel::Medium),
             tls_model: TlsModel::LocalExec,
diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs
index 8ffb622511d..af2f42fa00a 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs
@@ -17,7 +17,7 @@ pub(crate) fn target() -> Target {
         options: TargetOptions {
             code_model: Some(CodeModel::Medium),
             cpu: "generic-rv64".into(),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             llvm_abiname: "lp64d".into(),
             max_atomic_width: Some(64),
             supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]),
diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs
index 33b08fdcb05..70c19952af0 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs
@@ -17,7 +17,7 @@ pub(crate) fn target() -> Target {
         options: TargetOptions {
             code_model: Some(CodeModel::Medium),
             cpu: "generic-rv64".into(),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             llvm_abiname: "lp64d".into(),
             max_atomic_width: Some(64),
             supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]),
diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_netbsd.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_netbsd.rs
index 2b647e36f18..1f359d1e7fe 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_netbsd.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_netbsd.rs
@@ -15,7 +15,7 @@ pub(crate) fn target() -> Target {
         options: TargetOptions {
             code_model: Some(CodeModel::Medium),
             cpu: "generic-rv64".into(),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             llvm_abiname: "lp64d".into(),
             max_atomic_width: Some(64),
             mcount: "__mcount".into(),
diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_none_elf.rs
index d6f0a5499b9..5a5aad93efb 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_none_elf.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_none_elf.rs
@@ -22,7 +22,7 @@ pub(crate) fn target() -> Target {
             llvm_abiname: "lp64d".into(),
             cpu: "generic-rv64".into(),
             max_atomic_width: Some(64),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
             code_model: Some(CodeModel::Medium),
diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs
index bc6829897a4..e8abc926dd0 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs
@@ -24,7 +24,7 @@ pub(crate) fn target() -> Target {
             llvm_abiname: "lp64d".into(),
             cpu: "generic-rv64".into(),
             max_atomic_width: Some(64),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
             code_model: Some(CodeModel::Medium),
diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_openbsd.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_openbsd.rs
index 75f508d8e93..85d7dfe7865 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_openbsd.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_openbsd.rs
@@ -15,7 +15,7 @@ pub(crate) fn target() -> Target {
         options: TargetOptions {
             code_model: Some(CodeModel::Medium),
             cpu: "generic-rv64".into(),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             llvm_abiname: "lp64d".into(),
             max_atomic_width: Some(64),
             ..base::openbsd::opts()
diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_l4re_uclibc.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_l4re_uclibc.rs
index a034a9fb244..4107249dcf1 100644
--- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_l4re_uclibc.rs
+++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_l4re_uclibc.rs
@@ -8,7 +8,7 @@ pub(crate) fn target() -> Target {
     base.panic_strategy = PanicStrategy::Abort;
 
     Target {
-        llvm_target: "x86_64-unknown-l4re-uclibc".into(),
+        llvm_target: "x86_64-unknown-l4re-gnu".into(),
         metadata: TargetMetadata {
             description: None,
             tier: Some(3),
diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs
index a96caf227f7..b4ec1879fed 100644
--- a/compiler/rustc_target/src/target_features.rs
+++ b/compiler/rustc_target/src/target_features.rs
@@ -380,11 +380,16 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     // tidy-alphabetical-start
     ("adx", Stable, &[]),
     ("aes", Stable, &["sse2"]),
+    ("amx-avx512", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
     ("amx-bf16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
     ("amx-complex", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
     ("amx-fp16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
+    ("amx-fp8", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
     ("amx-int8", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
+    ("amx-movrs", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
+    ("amx-tf32", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
     ("amx-tile", Unstable(sym::x86_amx_intrinsics), &[]),
+    ("amx-transpose", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
     ("avx", Stable, &["sse4.2"]),
     ("avx2", Stable, &["avx"]),
     ("avx512bf16", Unstable(sym::avx512_target_feature), &["avx512bw"]),
@@ -418,6 +423,7 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]),
     ("lzcnt", Stable, &[]),
     ("movbe", Stable, &[]),
+    ("movrs", Unstable(sym::movrs_target_feature), &[]),
     ("pclmulqdq", Stable, &["sse2"]),
     ("popcnt", Stable, &[]),
     ("prfchw", Unstable(sym::prfchw_target_feature), &[]),
@@ -488,7 +494,7 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     ("c", Stable, &[]),
     ("d", Unstable(sym::riscv_target_feature), &["f"]),
     ("e", Unstable(sym::riscv_target_feature), &[]),
-    ("f", Unstable(sym::riscv_target_feature), &[]),
+    ("f", Unstable(sym::riscv_target_feature), &["zicsr"]),
     (
         "forced-atomics",
         Stability::Forbidden { reason: "unsound because it changes the ABI of atomic operations" },
@@ -497,7 +503,8 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     ("m", Stable, &[]),
     ("relax", Unstable(sym::riscv_target_feature), &[]),
     ("unaligned-scalar-mem", Unstable(sym::riscv_target_feature), &[]),
-    ("v", Unstable(sym::riscv_target_feature), &[]),
+    ("unaligned-vector-mem", Unstable(sym::riscv_target_feature), &[]),
+    ("v", Unstable(sym::riscv_target_feature), &["zvl128b", "zve64d"]),
     ("za128rs", Unstable(sym::riscv_target_feature), &[]),
     ("za64rs", Unstable(sym::riscv_target_feature), &[]),
     ("zaamo", Unstable(sym::riscv_target_feature), &[]),
@@ -516,19 +523,59 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     ("zdinx", Unstable(sym::riscv_target_feature), &["zfinx"]),
     ("zfh", Unstable(sym::riscv_target_feature), &["zfhmin"]),
     ("zfhmin", Unstable(sym::riscv_target_feature), &["f"]),
-    ("zfinx", Unstable(sym::riscv_target_feature), &[]),
+    ("zfinx", Unstable(sym::riscv_target_feature), &["zicsr"]),
     ("zhinx", Unstable(sym::riscv_target_feature), &["zhinxmin"]),
     ("zhinxmin", Unstable(sym::riscv_target_feature), &["zfinx"]),
+    ("zicntr", Unstable(sym::riscv_target_feature), &["zicsr"]),
+    ("zicsr", Unstable(sym::riscv_target_feature), &[]),
+    ("zifencei", Unstable(sym::riscv_target_feature), &[]),
+    ("zihintpause", Unstable(sym::riscv_target_feature), &[]),
+    ("zihpm", Unstable(sym::riscv_target_feature), &["zicsr"]),
     ("zk", Stable, &["zkn", "zkr", "zkt"]),
     ("zkn", Stable, &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]),
     ("zknd", Stable, &[]),
     ("zkne", Stable, &[]),
     ("zknh", Stable, &[]),
-    ("zkr", Stable, &[]),
+    ("zkr", Stable, &["zicsr"]),
     ("zks", Stable, &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]),
     ("zksed", Stable, &[]),
     ("zksh", Stable, &[]),
     ("zkt", Stable, &[]),
+    ("zvbb", Unstable(sym::riscv_target_feature), &["zvkb"]),
+    ("zvbc", Unstable(sym::riscv_target_feature), &["zve64x"]),
+    ("zve32f", Unstable(sym::riscv_target_feature), &["zve32x", "f"]),
+    ("zve32x", Unstable(sym::riscv_target_feature), &["zvl32b", "zicsr"]),
+    ("zve64d", Unstable(sym::riscv_target_feature), &["zve64f", "d"]),
+    ("zve64f", Unstable(sym::riscv_target_feature), &["zve32f", "zve64x"]),
+    ("zve64x", Unstable(sym::riscv_target_feature), &["zve32x", "zvl64b"]),
+    ("zvfh", Unstable(sym::riscv_target_feature), &["zvfhmin", "zfhmin"]),
+    ("zvfhmin", Unstable(sym::riscv_target_feature), &["zve32f"]),
+    ("zvkb", Unstable(sym::riscv_target_feature), &["zve32x"]),
+    ("zvkg", Unstable(sym::riscv_target_feature), &["zve32x"]),
+    ("zvkn", Unstable(sym::riscv_target_feature), &["zvkned", "zvknhb", "zvkb", "zvkt"]),
+    ("zvknc", Unstable(sym::riscv_target_feature), &["zvkn", "zvbc"]),
+    ("zvkned", Unstable(sym::riscv_target_feature), &["zve32x"]),
+    ("zvkng", Unstable(sym::riscv_target_feature), &["zvkn", "zvkg"]),
+    ("zvknha", Unstable(sym::riscv_target_feature), &["zve32x"]),
+    ("zvknhb", Unstable(sym::riscv_target_feature), &["zve64x"]),
+    ("zvks", Unstable(sym::riscv_target_feature), &["zvksed", "zvksh", "zvkb", "zvkt"]),
+    ("zvksc", Unstable(sym::riscv_target_feature), &["zvks", "zvbc"]),
+    ("zvksed", Unstable(sym::riscv_target_feature), &["zve32x"]),
+    ("zvksg", Unstable(sym::riscv_target_feature), &["zvks", "zvkg"]),
+    ("zvksh", Unstable(sym::riscv_target_feature), &["zve32x"]),
+    ("zvkt", Unstable(sym::riscv_target_feature), &[]),
+    ("zvl1024b", Unstable(sym::riscv_target_feature), &["zvl512b"]),
+    ("zvl128b", Unstable(sym::riscv_target_feature), &["zvl64b"]),
+    ("zvl16384b", Unstable(sym::riscv_target_feature), &["zvl8192b"]),
+    ("zvl2048b", Unstable(sym::riscv_target_feature), &["zvl1024b"]),
+    ("zvl256b", Unstable(sym::riscv_target_feature), &["zvl128b"]),
+    ("zvl32768b", Unstable(sym::riscv_target_feature), &["zvl16384b"]),
+    ("zvl32b", Unstable(sym::riscv_target_feature), &[]),
+    ("zvl4096b", Unstable(sym::riscv_target_feature), &["zvl2048b"]),
+    ("zvl512b", Unstable(sym::riscv_target_feature), &["zvl256b"]),
+    ("zvl64b", Unstable(sym::riscv_target_feature), &["zvl32b"]),
+    ("zvl65536b", Unstable(sym::riscv_target_feature), &["zvl32768b"]),
+    ("zvl8192b", Unstable(sym::riscv_target_feature), &["zvl4096b"]),
     // tidy-alphabetical-end
 ];
 
@@ -704,8 +751,20 @@ const ARM_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(1
 const POWERPC_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "altivec")];
 const WASM_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "simd128")];
 const S390X_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "vector")];
-const RISCV_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] =
-    &[/*(64, "zvl64b"), */ (128, "v")];
+const RISCV_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[
+    (32, "zvl32b"),
+    (64, "zvl64b"),
+    (128, "zvl128b"),
+    (256, "zvl256b"),
+    (512, "zvl512b"),
+    (1024, "zvl1024b"),
+    (2048, "zvl2048b"),
+    (4096, "zvl4096b"),
+    (8192, "zvl8192b"),
+    (16384, "zvl16384b"),
+    (32768, "zvl32768b"),
+    (65536, "zvl65536b"),
+];
 // Always warn on SPARC, as the necessary target features cannot be enabled in Rust at the moment.
 const SPARC_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[/*(64, "vis")*/];
 
diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl
index 4db9d9915b1..05bbb42fb7c 100644
--- a/compiler/rustc_trait_selection/messages.ftl
+++ b/compiler/rustc_trait_selection/messages.ftl
@@ -264,8 +264,15 @@ trait_selection_oc_no_diverge = `else` clause of `let...else` does not diverge
 trait_selection_oc_no_else = `if` may be missing an `else` clause
 trait_selection_oc_try_compat = `?` operator has incompatible types
 trait_selection_oc_type_compat = type not compatible with trait
+
 trait_selection_opaque_captures_lifetime = hidden type for `{$opaque_ty}` captures lifetime that does not appear in bounds
     .label = opaque type defined here
+trait_selection_opaque_type_non_generic_param =
+    expected generic {$kind} parameter, found `{$ty}`
+    .label = {STREQ($ty, "'static") ->
+        [true] cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
+        *[other] this generic parameter must be used with a generic {$kind} parameter
+    }
 
 trait_selection_outlives_bound = lifetime of the source pointer does not outlive lifetime bound of the object type
 trait_selection_outlives_content = lifetime of reference outlives lifetime of borrowed content...
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs
index 0bcb5f6f3b2..f45e3904212 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs
@@ -1051,7 +1051,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
                             None?
                         }
                         let args = self.node_args_opt(expr.hir_id)?;
-                        let span = tcx.hir().span(segment.hir_id);
+                        let span = tcx.hir_span(segment.hir_id);
                         let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
                         InsertableGenericArgs {
                             insert_span,
@@ -1110,7 +1110,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
                 if generics.has_impl_trait() {
                     return None;
                 }
-                let span = tcx.hir().span(segment.hir_id);
+                let span = tcx.hir_span(segment.hir_id);
                 let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
                 Some(InsertableGenericArgs {
                     insert_span,
@@ -1144,7 +1144,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
                     if !segment.infer_args || generics.has_impl_trait() {
                         do yeet ();
                     }
-                    let span = tcx.hir().span(segment.hir_id);
+                    let span = tcx.hir_span(segment.hir_id);
                     let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
                     InsertableGenericArgs {
                         insert_span,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
index 083ce022238..3559c660ee2 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
@@ -365,7 +365,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
                 // obligation comes from the `impl`. Find that `impl` so that we can point
                 // at it in the suggestion.
                 let trait_did = trait_id.to_def_id();
-                tcx.hir_trait_impls(trait_did).iter().find_map(|&impl_did| {
+                tcx.local_trait_impls(trait_did).iter().find_map(|&impl_did| {
                     if let Node::Item(Item {
                         kind: ItemKind::Impl(hir::Impl { self_ty, .. }), ..
                     }) = tcx.hir_node_by_def_id(impl_did)
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs
index 00f053fa599..683b5b528c6 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs
@@ -51,7 +51,6 @@ pub fn find_param_with_region<'tcx>(
         _ => return None, // not a free region
     };
 
-    let hir = &tcx.hir();
     let def_id = id.as_local()?;
 
     // FIXME: use def_kind
@@ -93,7 +92,7 @@ pub fn find_param_with_region<'tcx>(
             });
             found_anon_region.then(|| {
                 let ty_hir_id = fn_decl.inputs[index].hir_id;
-                let param_ty_span = hir.span(ty_hir_id);
+                let param_ty_span = tcx.hir_span(ty_hir_id);
                 let is_first = index == 0;
                 AnonymousParamInfo { param, param_ty: new_param_ty, param_ty_span, kind, is_first }
             })
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 07a67cde3be..bc45fc11e9b 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
@@ -14,6 +14,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::{self as hir, LangItem, Node};
 use rustc_infer::infer::{InferOk, TypeTrace};
+use rustc_infer::traits::solve::Goal;
 use rustc_middle::traits::SignatureMismatchData;
 use rustc_middle::traits::select::OverflowError;
 use rustc_middle::ty::abstract_const::NotConstEvaluatable;
@@ -930,7 +931,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             )) = arg.kind
             && let Node::Pat(pat) = self.tcx.hir_node(*hir_id)
             && let Some((preds, guar)) = self.reported_trait_errors.borrow().get(&pat.span)
-            && preds.contains(&obligation.predicate)
+            && preds.contains(&obligation.as_goal())
         {
             return Err(*guar);
         }
@@ -1236,7 +1237,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 );
                 // Only suggest derive if this isn't a derived obligation,
                 // and the struct is local.
-                if let Some(span) = self.tcx.hir().span_if_local(def.did())
+                if let Some(span) = self.tcx.hir_span_if_local(def.did())
                     && obligation.cause.code().parent().is_none()
                 {
                     if ty.is_structural_eq_shallow(self.tcx) {
@@ -1292,6 +1293,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     fn can_match_trait(
         &self,
+        param_env: ty::ParamEnv<'tcx>,
         goal: ty::TraitPredicate<'tcx>,
         assumption: ty::PolyTraitPredicate<'tcx>,
     ) -> bool {
@@ -1306,11 +1308,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             assumption,
         );
 
-        self.can_eq(ty::ParamEnv::empty(), goal.trait_ref, trait_assumption.trait_ref)
+        self.can_eq(param_env, goal.trait_ref, trait_assumption.trait_ref)
     }
 
     fn can_match_projection(
         &self,
+        param_env: ty::ParamEnv<'tcx>,
         goal: ty::ProjectionPredicate<'tcx>,
         assumption: ty::PolyProjectionPredicate<'tcx>,
     ) -> bool {
@@ -1320,7 +1323,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             assumption,
         );
 
-        let param_env = ty::ParamEnv::empty();
         self.can_eq(param_env, goal.projection_term, assumption.projection_term)
             && self.can_eq(param_env, goal.term, assumption.term)
     }
@@ -1330,24 +1332,32 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     #[instrument(level = "debug", skip(self), ret)]
     pub(super) fn error_implies(
         &self,
-        cond: ty::Predicate<'tcx>,
-        error: ty::Predicate<'tcx>,
+        cond: Goal<'tcx, ty::Predicate<'tcx>>,
+        error: Goal<'tcx, ty::Predicate<'tcx>>,
     ) -> bool {
         if cond == error {
             return true;
         }
 
-        if let Some(error) = error.as_trait_clause() {
+        // FIXME: We could be smarter about this, i.e. if cond's param-env is a
+        // subset of error's param-env. This only matters when binders will carry
+        // predicates though, and obviously only matters for error reporting.
+        if cond.param_env != error.param_env {
+            return false;
+        }
+        let param_env = error.param_env;
+
+        if let Some(error) = error.predicate.as_trait_clause() {
             self.enter_forall(error, |error| {
-                elaborate(self.tcx, std::iter::once(cond))
+                elaborate(self.tcx, std::iter::once(cond.predicate))
                     .filter_map(|implied| implied.as_trait_clause())
-                    .any(|implied| self.can_match_trait(error, implied))
+                    .any(|implied| self.can_match_trait(param_env, error, implied))
             })
-        } else if let Some(error) = error.as_projection_clause() {
+        } else if let Some(error) = error.predicate.as_projection_clause() {
             self.enter_forall(error, |error| {
-                elaborate(self.tcx, std::iter::once(cond))
+                elaborate(self.tcx, std::iter::once(cond.predicate))
                     .filter_map(|implied| implied.as_projection_clause())
-                    .any(|implied| self.can_match_projection(error, implied))
+                    .any(|implied| self.can_match_projection(param_env, error, implied))
             })
         } else {
             false
@@ -2943,7 +2953,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         };
 
         let found_node = found_did.and_then(|did| self.tcx.hir_get_if_local(did));
-        let found_span = found_did.and_then(|did| self.tcx.hir().span_if_local(did));
+        let found_span = found_did.and_then(|did| self.tcx.hir_span_if_local(did));
 
         if !self.reported_signature_mismatch.borrow_mut().insert((span, found_span)) {
             // We check closures twice, with obligations flowing in different directions,
@@ -3030,7 +3040,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         node: Node<'_>,
     ) -> Option<(Span, Option<Span>, Vec<ArgKind>)> {
         let sm = self.tcx.sess.source_map();
-        let hir = self.tcx.hir();
         Some(match node {
             Node::Expr(&hir::Expr {
                 kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }),
@@ -3086,7 +3095,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     .collect::<Vec<ArgKind>>(),
             ),
             Node::Ctor(variant_data) => {
-                let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id));
+                let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| self.tcx.hir_span(id));
                 (span, None, vec![ArgKind::empty(); variant_data.fields().len()])
             }
             _ => panic!("non-FnLike node found: {node:?}"),
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
index 98df09b6f7b..8ff7030717a 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
@@ -12,6 +12,7 @@ use rustc_errors::{Applicability, Diag, E0038, E0276, MultiSpan, struct_span_cod
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::{self as hir, AmbigArg, LangItem};
+use rustc_infer::traits::solve::Goal;
 use rustc_infer::traits::{
     DynCompatibilityViolation, Obligation, ObligationCause, ObligationCauseCode,
     PredicateObligation, SelectionError,
@@ -144,7 +145,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
         #[derive(Debug)]
         struct ErrorDescriptor<'tcx> {
-            predicate: ty::Predicate<'tcx>,
+            goal: Goal<'tcx, ty::Predicate<'tcx>>,
             index: Option<usize>, // None if this is an old error
         }
 
@@ -152,15 +153,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             .reported_trait_errors
             .borrow()
             .iter()
-            .map(|(&span, predicates)| {
-                (
-                    span,
-                    predicates
-                        .0
-                        .iter()
-                        .map(|&predicate| ErrorDescriptor { predicate, index: None })
-                        .collect(),
-                )
+            .map(|(&span, goals)| {
+                (span, goals.0.iter().map(|&goal| ErrorDescriptor { goal, index: None }).collect())
             })
             .collect();
 
@@ -186,10 +180,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 span = expn_data.call_site;
             }
 
-            error_map.entry(span).or_default().push(ErrorDescriptor {
-                predicate: error.obligation.predicate,
-                index: Some(index),
-            });
+            error_map
+                .entry(span)
+                .or_default()
+                .push(ErrorDescriptor { goal: error.obligation.as_goal(), index: Some(index) });
         }
 
         // We do this in 2 passes because we want to display errors in order, though
@@ -210,9 +204,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             continue;
                         }
 
-                        if self.error_implies(error2.predicate, error.predicate)
+                        if self.error_implies(error2.goal, error.goal)
                             && !(error2.index >= error.index
-                                && self.error_implies(error.predicate, error2.predicate))
+                                && self.error_implies(error.goal, error2.goal))
                         {
                             info!("skipping {:?} (implied by {:?})", error, error2);
                             is_suppressed[index] = true;
@@ -243,7 +237,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         .entry(span)
                         .or_insert_with(|| (vec![], guar))
                         .0
-                        .push(error.obligation.predicate);
+                        .push(error.obligation.as_goal());
                 }
             }
         }
@@ -398,7 +392,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         );
 
         if !self.tcx.is_impl_trait_in_trait(trait_item_def_id) {
-            if let Some(span) = self.tcx.hir().span_if_local(trait_item_def_id) {
+            if let Some(span) = self.tcx.hir_span_if_local(trait_item_def_id) {
                 let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
                 err.span_label(span, format!("definition of `{item_name}` from trait"));
             }
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 dc8022b95c3..38fcba4ea62 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -1193,7 +1193,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             // FIXME(compiler-errors): This is kind of a mess, but required for obligations
             // that come from a path expr to affect the *call* expr.
             c @ ObligationCauseCode::WhereClauseInExpr(_, _, hir_id, _)
-                if self.tcx.hir().span(*hir_id).lo() == span.lo() =>
+                if self.tcx.hir_span(*hir_id).lo() == span.lo() =>
             {
                 c
             }
@@ -4481,7 +4481,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             Obligation::new(self.tcx, obligation.cause.clone(), obligation.param_env, trait_ref);
 
         if self.predicate_must_hold_modulo_regions(&obligation) {
-            let arg_span = self.tcx.hir().span(*arg_hir_id);
+            let arg_span = self.tcx.hir_span(*arg_hir_id);
             err.multipart_suggestion_verbose(
                 format!("use a unary tuple instead"),
                 vec![(arg_span.shrink_to_lo(), "(".into()), (arg_span.shrink_to_hi(), ",)".into())],
@@ -4521,7 +4521,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     parent_code: _,
                 } = cause.code()
                 {
-                    let arg_span = self.tcx.hir().span(*arg_hir_id);
+                    let arg_span = self.tcx.hir_span(*arg_hir_id);
                     let mut sp: MultiSpan = arg_span.into();
 
                     sp.push_span_label(
@@ -4530,7 +4530,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         generic types that should be inferred from this argument",
                     );
                     sp.push_span_label(
-                        self.tcx.hir().span(*call_hir_id),
+                        self.tcx.hir_span(*call_hir_id),
                         "add turbofish arguments to this call to \
                         specify the types manually, even if it's redundant",
                     );
@@ -4939,7 +4939,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         .type_implements_trait(pred.def_id(), [rhs_ty, lhs_ty], param_env)
                         .must_apply_modulo_regions()
                 {
-                    let lhs_span = tcx.hir().span(lhs_hir_id);
+                    let lhs_span = tcx.hir_span(lhs_hir_id);
                     let sm = tcx.sess.source_map();
                     if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_span)
                         && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_span)
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index b30390a9330..9f7bfe5101a 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -12,7 +12,7 @@ use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty};
 use rustc_hir::{self as hir, AmbigArg, FnRetTy, GenericParamKind, IsAnonInPath, Node};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath};
-use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, Region, Ty, TyCtxt};
+use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, GenericArg, Region, Ty, TyCtxt};
 use rustc_span::{BytePos, Ident, Span, Symbol, kw};
 
 use crate::error_reporting::infer::ObligationCauseAsDiagArg;
@@ -1922,3 +1922,14 @@ impl Subdiagnostic for AddPreciseCapturingForOvercapture {
         }
     }
 }
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_opaque_type_non_generic_param, code = E0792)]
+pub(crate) struct NonGenericOpaqueTypeParam<'a, 'tcx> {
+    pub ty: GenericArg<'tcx>,
+    pub kind: &'a str,
+    #[primary_span]
+    pub span: Span,
+    #[label]
+    pub param_span: Span,
+}
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index b18fb0fb8fd..93c11805304 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -36,6 +36,7 @@
 pub mod error_reporting;
 pub mod errors;
 pub mod infer;
+pub mod opaque_types;
 pub mod regions;
 pub mod solve;
 pub mod traits;
diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs
new file mode 100644
index 00000000000..c7b8f063196
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/opaque_types.rs
@@ -0,0 +1,182 @@
+use rustc_data_structures::fx::FxIndexMap;
+use rustc_hir::OpaqueTyOrigin;
+use rustc_hir::def_id::LocalDefId;
+use rustc_infer::infer::outlives::env::OutlivesEnvironment;
+use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
+use rustc_middle::ty::{
+    self, DefiningScopeKind, GenericArgKind, GenericArgs, OpaqueTypeKey, TyCtxt, TypeVisitableExt,
+    TypingMode, fold_regions,
+};
+use rustc_span::{ErrorGuaranteed, Span};
+
+use crate::errors::NonGenericOpaqueTypeParam;
+use crate::regions::OutlivesEnvironmentBuildExt;
+use crate::traits::ObligationCtxt;
+
+/// Opaque type parameter validity check as documented in the [rustc-dev-guide chapter].
+///
+/// [rustc-dev-guide chapter]:
+/// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
+pub fn check_opaque_type_parameter_valid<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    opaque_type_key: OpaqueTypeKey<'tcx>,
+    span: Span,
+    defining_scope_kind: DefiningScopeKind,
+) -> Result<(), ErrorGuaranteed> {
+    let tcx = infcx.tcx;
+    let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
+    let opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id);
+    let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default();
+
+    // Avoid duplicate errors in case the opaque has already been malformed in
+    // HIR typeck.
+    if let DefiningScopeKind::MirBorrowck = defining_scope_kind {
+        if let Err(guar) = infcx
+            .tcx
+            .type_of_opaque_hir_typeck(opaque_type_key.def_id)
+            .instantiate_identity()
+            .error_reported()
+        {
+            return Err(guar);
+        }
+    }
+
+    for (i, arg) in opaque_type_key.iter_captured_args(tcx) {
+        let arg_is_param = match arg.unpack() {
+            GenericArgKind::Lifetime(lt) => match defining_scope_kind {
+                DefiningScopeKind::HirTypeck => continue,
+                DefiningScopeKind::MirBorrowck => {
+                    matches!(*lt, ty::ReEarlyParam(_) | ty::ReLateParam(_))
+                        || (lt.is_static() && opaque_env.param_equal_static(i))
+                }
+            },
+            GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
+            GenericArgKind::Const(ct) => matches!(ct.kind(), ty::ConstKind::Param(_)),
+        };
+
+        if arg_is_param {
+            // Register if the same lifetime appears multiple times in the generic args.
+            // There is an exception when the opaque type *requires* the lifetimes to be equal.
+            // See [rustc-dev-guide chapter] § "An exception to uniqueness rule".
+            let seen_where = seen_params.entry(arg).or_default();
+            if !seen_where.first().is_some_and(|&prev_i| opaque_env.params_equal(i, prev_i)) {
+                seen_where.push(i);
+            }
+        } else {
+            // Prevent `fn foo() -> Foo<u32>` from being defining.
+            let opaque_param = opaque_generics.param_at(i, tcx);
+            let kind = opaque_param.kind.descr();
+
+            opaque_env.param_is_error(i)?;
+
+            return Err(infcx.dcx().emit_err(NonGenericOpaqueTypeParam {
+                ty: arg,
+                kind,
+                span,
+                param_span: tcx.def_span(opaque_param.def_id),
+            }));
+        }
+    }
+
+    for (_, indices) in seen_params {
+        if indices.len() > 1 {
+            let descr = opaque_generics.param_at(indices[0], tcx).kind.descr();
+            let spans: Vec<_> = indices
+                .into_iter()
+                .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id))
+                .collect();
+            return Err(infcx
+                .dcx()
+                .struct_span_err(span, "non-defining opaque type use in defining scope")
+                .with_span_note(spans, format!("{descr} used multiple times"))
+                .emit());
+        }
+    }
+
+    Ok(())
+}
+
+/// Computes if an opaque type requires a lifetime parameter to be equal to
+/// another one or to the `'static` lifetime.
+/// These requirements are derived from the explicit and implied bounds.
+struct LazyOpaqueTyEnv<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    def_id: LocalDefId,
+
+    /// Equal parameters will have the same name. Computed Lazily.
+    /// Example:
+    ///     `type Opaque<'a: 'static, 'b: 'c, 'c: 'b> = impl Sized;`
+    ///     Identity args: `['a, 'b, 'c]`
+    ///     Canonical args: `['static, 'b, 'b]`
+    canonical_args: std::cell::OnceCell<ty::GenericArgsRef<'tcx>>,
+}
+
+impl<'tcx> LazyOpaqueTyEnv<'tcx> {
+    fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
+        Self { tcx, def_id, canonical_args: std::cell::OnceCell::new() }
+    }
+
+    fn param_equal_static(&self, param_index: usize) -> bool {
+        self.get_canonical_args()[param_index].expect_region().is_static()
+    }
+
+    fn params_equal(&self, param1: usize, param2: usize) -> bool {
+        let canonical_args = self.get_canonical_args();
+        canonical_args[param1] == canonical_args[param2]
+    }
+
+    fn param_is_error(&self, param_index: usize) -> Result<(), ErrorGuaranteed> {
+        self.get_canonical_args()[param_index].error_reported()
+    }
+
+    fn get_canonical_args(&self) -> ty::GenericArgsRef<'tcx> {
+        if let Some(&canonical_args) = self.canonical_args.get() {
+            return canonical_args;
+        }
+
+        let &Self { tcx, def_id, .. } = self;
+        let origin = tcx.local_opaque_ty_origin(def_id);
+        let parent = match origin {
+            OpaqueTyOrigin::FnReturn { parent, .. }
+            | OpaqueTyOrigin::AsyncFn { parent, .. }
+            | OpaqueTyOrigin::TyAlias { parent, .. } => parent,
+        };
+        let param_env = tcx.param_env(parent);
+        let args = GenericArgs::identity_for_item(tcx, parent).extend_to(
+            tcx,
+            def_id.to_def_id(),
+            |param, _| {
+                tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()).into()
+            },
+        );
+
+        // FIXME(#132279): It feels wrong to use `non_body_analysis` here given that we're
+        // in a body here.
+        let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
+        let ocx = ObligationCtxt::new(&infcx);
+
+        let wf_tys = ocx.assumed_wf_types(param_env, parent).unwrap_or_else(|_| {
+            tcx.dcx().span_delayed_bug(tcx.def_span(def_id), "error getting implied bounds");
+            Default::default()
+        });
+        let outlives_env = OutlivesEnvironment::new(&infcx, parent, param_env, wf_tys);
+
+        let mut seen = vec![tcx.lifetimes.re_static];
+        let canonical_args = fold_regions(tcx, args, |r1, _| {
+            if r1.is_error() {
+                r1
+            } else if let Some(&r2) = seen.iter().find(|&&r2| {
+                let free_regions = outlives_env.free_region_map();
+                free_regions.sub_free_regions(tcx, r1, r2)
+                    && free_regions.sub_free_regions(tcx, r2, r1)
+            }) {
+                r2
+            } else {
+                seen.push(r1);
+                r1
+            }
+        });
+        self.canonical_args.set(canonical_args).unwrap();
+        canonical_args
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs
index 3d9a90eb74e..e0b425fa739 100644
--- a/compiler/rustc_trait_selection/src/solve/delegate.rs
+++ b/compiler/rustc_trait_selection/src/solve/delegate.rs
@@ -149,16 +149,16 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
         self.0.instantiate_canonical_var(span, cv_info, universe_map)
     }
 
-    fn insert_hidden_type(
+    fn register_hidden_type_in_storage(
         &self,
-        opaque_type_key: ty::OpaqueTypeKey<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        hidden_ty: Ty<'tcx>,
-        goals: &mut Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
-    ) -> Result<(), NoSolution> {
-        self.0
-            .insert_hidden_type(opaque_type_key, DUMMY_SP, param_env, hidden_ty, goals)
-            .map_err(|_| NoSolution)
+        opaque_type_key: rustc_type_ir::OpaqueTypeKey<Self::Interner>,
+        hidden_ty: <Self::Interner as ty::Interner>::Ty,
+        span: <Self::Interner as ty::Interner>::Span,
+    ) -> Option<<Self::Interner as ty::Interner>::Ty> {
+        self.0.register_hidden_type_in_storage(
+            opaque_type_key,
+            ty::OpaqueHiddenType { span, ty: hidden_ty },
+        )
     }
 
     fn add_item_bounds_for_hidden_type(
@@ -172,15 +172,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
         self.0.add_item_bounds_for_hidden_type(def_id, args, param_env, hidden_ty, goals);
     }
 
-    fn inject_new_hidden_type_unchecked(
-        &self,
-        key: ty::OpaqueTypeKey<'tcx>,
-        hidden_ty: Ty<'tcx>,
-        span: Span,
-    ) {
-        self.0.inject_new_hidden_type_unchecked(key, ty::OpaqueHiddenType { ty: hidden_ty, span })
-    }
-
     fn reset_opaque_types(&self) {
         let _ = self.take_opaque_types();
     }
@@ -204,6 +195,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
             match self.typing_mode() {
                 TypingMode::Coherence
                 | TypingMode::Analysis { .. }
+                | TypingMode::Borrowck { .. }
                 | TypingMode::PostBorrowckAnalysis { .. } => false,
                 TypingMode::PostAnalysis => {
                     let poly_trait_ref = self.resolve_vars_if_possible(goal_trait_ref);
diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
index 78a45243983..fa6bbf1d6e5 100644
--- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
+++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
@@ -583,27 +583,36 @@ fn receiver_is_dispatchable<'tcx>(
     // create a modified param env, with `Self: Unsize<U>` and `U: Trait` (and all of
     // its supertraits) added to caller bounds. `U: ?Sized` is already implied here.
     let param_env = {
-        let param_env = tcx.param_env(method.def_id);
+        // N.B. We generally want to emulate the construction of the `unnormalized_param_env`
+        // in the param-env query here. The fact that we don't just start with the clauses
+        // in the param-env of the method is because those are already normalized, and mixing
+        // normalized and unnormalized copies of predicates in `normalize_param_env_or_error`
+        // will cause ambiguity that the user can't really avoid.
+        //
+        // We leave out certain complexities of the param-env query here. Specifically, we:
+        // 1. Do not add `~const` bounds since there are no `dyn const Trait`s.
+        // 2. Do not add RPITIT self projection bounds for defaulted methods, since we
+        //    are not constructing a param-env for "inside" of the body of the defaulted
+        //    method, so we don't really care about projecting to a specific RPIT type,
+        //    and because RPITITs are not dyn compatible (yet).
+        let mut predicates = tcx.predicates_of(method.def_id).instantiate_identity(tcx).predicates;
 
         // Self: Unsize<U>
         let unsize_predicate =
-            ty::TraitRef::new(tcx, unsize_did, [tcx.types.self_param, unsized_self_ty]).upcast(tcx);
+            ty::TraitRef::new(tcx, unsize_did, [tcx.types.self_param, unsized_self_ty]);
+        predicates.push(unsize_predicate.upcast(tcx));
 
         // U: Trait<Arg1, ..., ArgN>
-        let trait_predicate = {
-            let trait_def_id = method.trait_container(tcx).unwrap();
-            let args = GenericArgs::for_item(tcx, trait_def_id, |param, _| {
-                if param.index == 0 { unsized_self_ty.into() } else { tcx.mk_param_from_def(param) }
-            });
-
-            ty::TraitRef::new_from_args(tcx, trait_def_id, args).upcast(tcx)
-        };
+        let trait_def_id = method.trait_container(tcx).unwrap();
+        let args = GenericArgs::for_item(tcx, trait_def_id, |param, _| {
+            if param.index == 0 { unsized_self_ty.into() } else { tcx.mk_param_from_def(param) }
+        });
+        let trait_predicate = ty::TraitRef::new_from_args(tcx, trait_def_id, args);
+        predicates.push(trait_predicate.upcast(tcx));
 
         normalize_param_env_or_error(
             tcx,
-            ty::ParamEnv::new(tcx.mk_clauses_from_iter(
-                param_env.caller_bounds().iter().chain([unsize_predicate, trait_predicate]),
-            )),
+            ty::ParamEnv::new(tcx.mk_clauses(&predicates)),
             ObligationCause::dummy_with_span(tcx.def_span(method.def_id)),
         )
     };
diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs
index 3c127416cbf..defbafac20b 100644
--- a/compiler/rustc_trait_selection/src/traits/effects.rs
+++ b/compiler/rustc_trait_selection/src/traits/effects.rs
@@ -106,10 +106,6 @@ fn match_candidate<'tcx>(
 
     more_nested(selcx, &mut nested);
 
-    for nested in &mut nested {
-        nested.set_depth_from_parent(obligation.recursion_depth);
-    }
-
     Ok(nested)
 }
 
@@ -263,7 +259,7 @@ fn evaluate_host_effect_for_destruct_goal<'tcx>(
                 .all_fields()
                 .map(|field| ty::TraitRef::new(tcx, destruct_def_id, [field.ty(tcx, args)]))
                 .collect();
-            match adt_def.destructor(tcx).map(|dtor| dtor.constness) {
+            match adt_def.destructor(tcx).map(|dtor| tcx.constness(dtor.did)) {
                 // `Drop` impl exists, but it's not const. Type cannot be `~const Destruct`.
                 Some(hir::Constness::NotConst) => return Err(EvaluationFailure::NoSolution),
                 // `Drop` impl exists, and it's const. Require `Ty: ~const Drop` to hold.
@@ -378,10 +374,6 @@ fn evaluate_host_effect_from_selection_candiate<'tcx>(
                             }),
                     );
 
-                    for nested in &mut nested {
-                        nested.set_depth_from_parent(obligation.recursion_depth);
-                    }
-
                     Ok(nested)
                 }
                 _ => Err(EvaluationFailure::NoSolution),
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index e39f8e673db..e98a240a53f 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -225,9 +225,15 @@ struct FulfillProcessor<'a, 'tcx> {
     selcx: SelectionContext<'a, 'tcx>,
 }
 
-fn mk_pending<'tcx>(os: PredicateObligations<'tcx>) -> PendingPredicateObligations<'tcx> {
+fn mk_pending<'tcx>(
+    parent: &PredicateObligation<'tcx>,
+    os: PredicateObligations<'tcx>,
+) -> PendingPredicateObligations<'tcx> {
     os.into_iter()
-        .map(|o| PendingPredicateObligation { obligation: o, stalled_on: vec![] })
+        .map(|mut o| {
+            o.set_depth_from_parent(parent.recursion_depth);
+            PendingPredicateObligation { obligation: o, stalled_on: vec![] }
+        })
         .collect()
 }
 
@@ -341,7 +347,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
             );
             if predicate != obligation.predicate {
                 obligations.push(obligation.with(infcx.tcx, predicate));
-                return ProcessResult::Changed(mk_pending(obligations));
+                return ProcessResult::Changed(mk_pending(obligation, obligations));
             }
         }
         let binder = obligation.predicate.kind();
@@ -385,7 +391,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                     let mut obligations = PredicateObligations::with_capacity(1);
                     obligations.push(obligation.with(infcx.tcx, pred));
 
-                    ProcessResult::Changed(mk_pending(obligations))
+                    ProcessResult::Changed(mk_pending(obligation, obligations))
                 }
                 ty::PredicateKind::Ambiguous => ProcessResult::Unchanged,
                 ty::PredicateKind::NormalizesTo(..) => {
@@ -410,6 +416,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                     let host_obligation = obligation.with(infcx.tcx, data);
 
                     self.process_host_obligation(
+                        obligation,
                         host_obligation,
                         &mut pending_obligation.stalled_on,
                     )
@@ -486,7 +493,10 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                         // `<lhs_ty as Add<rhs_ty>>::Output` when this is an `Expr` representing
                         // `lhs + rhs`.
                         ty::ConstKind::Expr(_) => {
-                            return ProcessResult::Changed(mk_pending(PredicateObligations::new()));
+                            return ProcessResult::Changed(mk_pending(
+                                obligation,
+                                PredicateObligations::new(),
+                            ));
                         }
                         ty::ConstKind::Placeholder(_) => {
                             bug!("placeholder const {:?} in old solver", ct)
@@ -503,7 +513,10 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                         ct_ty,
                         ty,
                     ) {
-                        Ok(inf_ok) => ProcessResult::Changed(mk_pending(inf_ok.into_obligations())),
+                        Ok(inf_ok) => ProcessResult::Changed(mk_pending(
+                            obligation,
+                            inf_ok.into_obligations(),
+                        )),
                         Err(_) => ProcessResult::Error(FulfillmentErrorCode::Select(
                             SelectionError::ConstArgHasWrongType { ct, ct_ty, expected_ty: ty },
                         )),
@@ -537,7 +550,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                                 vec![TyOrConstInferVar::maybe_from_generic_arg(arg).unwrap()];
                             ProcessResult::Unchanged
                         }
-                        Some(os) => ProcessResult::Changed(mk_pending(os)),
+                        Some(os) => ProcessResult::Changed(mk_pending(obligation, os)),
                     }
                 }
 
@@ -553,11 +566,8 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                                 vec![TyOrConstInferVar::Ty(a), TyOrConstInferVar::Ty(b)];
                             ProcessResult::Unchanged
                         }
-                        Ok(Ok(mut ok)) => {
-                            for subobligation in &mut ok.obligations {
-                                subobligation.set_depth_from_parent(obligation.recursion_depth);
-                            }
-                            ProcessResult::Changed(mk_pending(ok.obligations))
+                        Ok(Ok(ok)) => {
+                            ProcessResult::Changed(mk_pending(obligation, ok.obligations))
                         }
                         Ok(Err(err)) => {
                             let expected_found = if subtype.a_is_expected {
@@ -582,7 +592,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                                 vec![TyOrConstInferVar::Ty(a), TyOrConstInferVar::Ty(b)];
                             ProcessResult::Unchanged
                         }
-                        Ok(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)),
+                        Ok(Ok(ok)) => {
+                            ProcessResult::Changed(mk_pending(obligation, ok.obligations))
+                        }
                         Ok(Err(err)) => {
                             let expected_found = ExpectedFound::new(coerce.b, coerce.a);
                             ProcessResult::Error(FulfillmentErrorCode::Subtype(expected_found, err))
@@ -645,6 +657,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                                     )
                                 {
                                     return ProcessResult::Changed(mk_pending(
+                                        obligation,
                                         new_obligations.into_obligations(),
                                     ));
                                 }
@@ -659,6 +672,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                                     .eq(DefineOpaqueTypes::Yes, c1, c2)
                                 {
                                     return ProcessResult::Changed(mk_pending(
+                                        obligation,
                                         new_obligations.into_obligations(),
                                     ));
                                 }
@@ -704,9 +718,10 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                                 c1,
                                 c2,
                             ) {
-                                Ok(inf_ok) => {
-                                    ProcessResult::Changed(mk_pending(inf_ok.into_obligations()))
-                                }
+                                Ok(inf_ok) => ProcessResult::Changed(mk_pending(
+                                    obligation,
+                                    inf_ok.into_obligations(),
+                                )),
                                 Err(err) => {
                                     ProcessResult::Error(FulfillmentErrorCode::ConstEquate(
                                         ExpectedFound::new(c1, c2),
@@ -790,7 +805,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> {
         match self.selcx.poly_select(&trait_obligation) {
             Ok(Some(impl_source)) => {
                 debug!("selecting trait at depth {} yielded Ok(Some)", obligation.recursion_depth);
-                ProcessResult::Changed(mk_pending(impl_source.nested_obligations()))
+                ProcessResult::Changed(mk_pending(obligation, impl_source.nested_obligations()))
             }
             Ok(None) => {
                 debug!("selecting trait at depth {} yielded Ok(None)", obligation.recursion_depth);
@@ -854,7 +869,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> {
         }
 
         match project::poly_project_and_unify_term(&mut self.selcx, &project_obligation) {
-            ProjectAndUnifyResult::Holds(os) => ProcessResult::Changed(mk_pending(os)),
+            ProjectAndUnifyResult::Holds(os) => ProcessResult::Changed(mk_pending(obligation, os)),
             ProjectAndUnifyResult::FailedNormalization => {
                 stalled_on.clear();
                 stalled_on.extend(args_infer_vars(
@@ -868,7 +883,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> {
                 let mut obligations = PredicateObligations::with_capacity(1);
                 obligations.push(project_obligation.with(tcx, project_obligation.predicate));
 
-                ProcessResult::Changed(mk_pending(obligations))
+                ProcessResult::Changed(mk_pending(obligation, obligations))
             }
             ProjectAndUnifyResult::MismatchedProjectionTypes(e) => {
                 ProcessResult::Error(FulfillmentErrorCode::Project(e))
@@ -878,11 +893,12 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> {
 
     fn process_host_obligation(
         &mut self,
+        obligation: &PredicateObligation<'tcx>,
         host_obligation: HostEffectObligation<'tcx>,
         stalled_on: &mut Vec<TyOrConstInferVar>,
     ) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> {
         match effects::evaluate_host_effect_obligation(&mut self.selcx, &host_obligation) {
-            Ok(nested) => ProcessResult::Changed(mk_pending(nested)),
+            Ok(nested) => ProcessResult::Changed(mk_pending(obligation, nested)),
             Err(effects::EvaluationFailure::Ambiguous) => {
                 stalled_on.clear();
                 stalled_on.extend(args_infer_vars(
diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs
index ad62b456ad4..4ac45172a0e 100644
--- a/compiler/rustc_trait_selection/src/traits/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/normalize.rs
@@ -130,6 +130,7 @@ pub(super) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
         // FIXME(#132279): We likely want to reveal opaques during post borrowck analysis
         TypingMode::Coherence
         | TypingMode::Analysis { .. }
+        | TypingMode::Borrowck { .. }
         | TypingMode::PostBorrowckAnalysis { .. } => flags.remove(ty::TypeFlags::HAS_TY_OPAQUE),
         TypingMode::PostAnalysis => {}
     }
@@ -226,6 +227,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
                     // FIXME(#132279): We likely want to reveal opaques during post borrowck analysis
                     TypingMode::Coherence
                     | TypingMode::Analysis { .. }
+                    | TypingMode::Borrowck { .. }
                     | TypingMode::PostBorrowckAnalysis { .. } => ty.super_fold_with(self),
                     TypingMode::PostAnalysis => {
                         let recursion_limit = self.cx().recursion_limit();
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 6057b66c483..349569d750e 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -952,6 +952,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                             match selcx.infcx.typing_mode() {
                                 TypingMode::Coherence
                                 | TypingMode::Analysis { .. }
+                                | TypingMode::Borrowck { .. }
                                 | TypingMode::PostBorrowckAnalysis { .. } => {
                                     debug!(
                                         assoc_ty = ?selcx.tcx().def_path_str(node_item.item.def_id),
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index 165c63f3745..5dbb4382fd1 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -216,6 +216,7 @@ impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'a, 'tcx> {
                 match self.infcx.typing_mode() {
                     TypingMode::Coherence
                     | TypingMode::Analysis { .. }
+                    | TypingMode::Borrowck { .. }
                     | TypingMode::PostBorrowckAnalysis { .. } => ty.try_super_fold_with(self)?,
 
                     TypingMode::PostAnalysis => {
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index 4cfd8149b1e..cf6d2bc151f 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -86,10 +86,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 // `Pointee` is automatically implemented for every type.
                 candidates.vec.push(BuiltinCandidate { has_nested: false });
             } else if tcx.is_lang_item(def_id, LangItem::Sized) {
-                // Sized is never implementable by end-users, it is
-                // always automatically computed.
-                let sized_conditions = self.sized_conditions(obligation);
-                self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates);
+                self.assemble_builtin_sized_candidate(obligation, &mut candidates);
             } else if tcx.is_lang_item(def_id, LangItem::Unsize) {
                 self.assemble_candidates_for_unsizing(obligation, &mut candidates);
             } else if tcx.is_lang_item(def_id, LangItem::Destruct) {
@@ -695,6 +692,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         let def_id = obligation.predicate.def_id();
 
+        let mut check_impls = || {
+            // Only consider auto impls if there are no manual impls for the root of `self_ty`.
+            //
+            // For example, we only consider auto candidates for `&i32: Auto` if no explicit impl
+            // for `&SomeType: Auto` exists. Due to E0321 the only crate where impls
+            // for `&SomeType: Auto` can be defined is the crate where `Auto` has been defined.
+            //
+            // Generally, we have to guarantee that for all `SimplifiedType`s the only crate
+            // which may define impls for that type is either the crate defining the type
+            // or the trait. This should be guaranteed by the orphan check.
+            let mut has_impl = false;
+            self.tcx().for_each_relevant_impl(def_id, self_ty, |_| has_impl = true);
+            if !has_impl {
+                candidates.vec.push(AutoImplCandidate)
+            }
+        };
+
         if self.tcx().trait_is_auto(def_id) {
             match *self_ty.kind() {
                 ty::Dynamic(..) => {
@@ -708,6 +722,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     // we don't add any `..` impl. Default traits could
                     // still be provided by a manual implementation for
                     // this trait and type.
+
+                    // Backward compatibility for default auto traits.
+                    // Test: ui/traits/default_auto_traits/extern-types.rs
+                    if self.tcx().is_default_trait(def_id) {
+                        check_impls()
+                    }
                 }
                 ty::Param(..)
                 | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
@@ -808,20 +828,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                         return;
                     }
 
-                    // Only consider auto impls if there are no manual impls for the root of `self_ty`.
-                    //
-                    // For example, we only consider auto candidates for `&i32: Auto` if no explicit impl
-                    // for `&SomeType: Auto` exists. Due to E0321 the only crate where impls
-                    // for `&SomeType: Auto` can be defined is the crate where `Auto` has been defined.
-                    //
-                    // Generally, we have to guarantee that for all `SimplifiedType`s the only crate
-                    // which may define impls for that type is either the crate defining the type
-                    // or the trait. This should be guaranteed by the orphan check.
-                    let mut has_impl = false;
-                    self.tcx().for_each_relevant_impl(def_id, self_ty, |_| has_impl = true);
-                    if !has_impl {
-                        candidates.vec.push(AutoImplCandidate)
-                    }
+                    check_impls();
                 }
                 ty::Error(_) => {
                     candidates.vec.push(AutoImplCandidate);
@@ -1061,6 +1068,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     /// Assembles the trait which are built-in to the language itself:
     /// `Copy`, `Clone` and `Sized`.
     #[instrument(level = "debug", skip(self, candidates))]
+    fn assemble_builtin_sized_candidate(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        match self.sized_conditions(obligation) {
+            BuiltinImplConditions::Where(nested) => {
+                candidates
+                    .vec
+                    .push(SizedCandidate { has_nested: !nested.skip_binder().is_empty() });
+            }
+            BuiltinImplConditions::None => {}
+            BuiltinImplConditions::Ambiguous => {
+                candidates.ambiguous = true;
+            }
+        }
+    }
+
+    /// Assembles the trait which are built-in to the language itself:
+    /// e.g. `Copy` and `Clone`.
+    #[instrument(level = "debug", skip(self, candidates))]
     fn assemble_builtin_bound_candidates(
         &mut self,
         conditions: BuiltinImplConditions<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index a66c958c109..2cb7d2d8931 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -39,7 +39,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         obligation: &PolyTraitObligation<'tcx>,
         candidate: SelectionCandidate<'tcx>,
     ) -> Result<Selection<'tcx>, SelectionError<'tcx>> {
-        let mut impl_src = match candidate {
+        Ok(match candidate {
+            SizedCandidate { has_nested } => {
+                let data = self.confirm_builtin_candidate(obligation, has_nested);
+                ImplSource::Builtin(BuiltinImplSource::Misc, data)
+            }
+
             BuiltinCandidate { has_nested } => {
                 let data = self.confirm_builtin_candidate(obligation, has_nested);
                 ImplSource::Builtin(BuiltinImplSource::Misc, data)
@@ -134,15 +139,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             BikeshedGuaranteedNoDropCandidate => {
                 self.confirm_bikeshed_guaranteed_no_drop_candidate(obligation)
             }
-        };
-
-        // The obligations returned by confirmation are recursively evaluated
-        // so we need to make sure they have the correct depth.
-        for subobligation in impl_src.borrow_nested_obligations_mut() {
-            subobligation.set_depth_from_parent(obligation.recursion_depth);
-        }
-
-        Ok(impl_src)
+        })
     }
 
     fn confirm_projection_candidate(
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index e439df76cd4..56ff46e89e7 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -1446,6 +1446,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         match self.infcx.typing_mode() {
             TypingMode::Coherence => {}
             TypingMode::Analysis { .. }
+            | TypingMode::Borrowck { .. }
             | TypingMode::PostBorrowckAnalysis { .. }
             | TypingMode::PostAnalysis => return Ok(()),
         }
@@ -1491,7 +1492,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             // However, if we disqualify *all* goals from being cached, perf suffers.
             // This is likely fixed by better caching in general in the new solver.
             // See: <https://github.com/rust-lang/rust/issues/132064>.
-            TypingMode::Analysis { defining_opaque_types } => {
+            TypingMode::Analysis { defining_opaque_types }
+            | TypingMode::Borrowck { defining_opaque_types } => {
                 defining_opaque_types.is_empty() || !pred.has_opaque_types()
             }
             // The hidden types of `defined_opaque_types` is not local to the current
@@ -1801,17 +1803,21 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             return Some(candidates.pop().unwrap().candidate);
         }
 
-        // We prefer trivial builtin candidates, i.e. builtin impls without any nested
-        // requirements, over all others. This is a fix for #53123 and prevents winnowing
-        // from accidentally extending the lifetime of a variable.
-        let mut trivial_builtin = candidates
-            .iter()
-            .filter(|c| matches!(c.candidate, BuiltinCandidate { has_nested: false }));
-        if let Some(_trivial) = trivial_builtin.next() {
-            // There should only ever be a single trivial builtin candidate
+        // We prefer `Sized` candidates over everything.
+        let mut sized_candidates =
+            candidates.iter().filter(|c| matches!(c.candidate, SizedCandidate { has_nested: _ }));
+        if let Some(sized_candidate) = sized_candidates.next() {
+            // There should only ever be a single sized candidate
             // as they would otherwise overlap.
-            debug_assert_eq!(trivial_builtin.next(), None);
-            return Some(BuiltinCandidate { has_nested: false });
+            debug_assert_eq!(sized_candidates.next(), None);
+            // Only prefer the built-in `Sized` candidate if its nested goals are certain.
+            // Otherwise, we may encounter failure later on if inference causes this candidate
+            // to not hold, but a where clause would've applied instead.
+            if sized_candidate.evaluation.must_apply_modulo_regions() {
+                return Some(sized_candidate.candidate.clone());
+            } else {
+                return None;
+            }
         }
 
         // Before we consider where-bounds, we have to deduplicate them here and also
@@ -1940,7 +1946,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             // Don't use impl candidates which overlap with other candidates.
             // This should pretty much only ever happen with malformed impls.
             if candidates.iter().all(|c| match c.candidate {
-                BuiltinCandidate { has_nested: _ }
+                SizedCandidate { has_nested: _ }
+                | BuiltinCandidate { has_nested: _ }
                 | TransmutabilityCandidate
                 | AutoImplCandidate
                 | ClosureCandidate { .. }
@@ -2294,6 +2301,11 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             | ty::Never
             | ty::Char => ty::Binder::dummy(Vec::new()),
 
+            // This branch is only for `experimental_default_bounds`.
+            // Other foreign types were rejected earlier in
+            // `assemble_candidates_from_auto_impls`.
+            ty::Foreign(..) => ty::Binder::dummy(Vec::new()),
+
             // FIXME(unsafe_binders): Squash the double binder for now, I guess.
             ty::UnsafeBinder(_) => return Err(SelectionError::Unimplemented),
 
@@ -2303,7 +2315,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             ty::Placeholder(..)
             | ty::Dynamic(..)
             | ty::Param(..)
-            | ty::Foreign(..)
             | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
             | ty::Bound(..)
             | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index 962e1353ebc..66c18bed5e7 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -149,6 +149,7 @@ fn resolve_associated_item<'tcx>(
                 match typing_env.typing_mode {
                     ty::TypingMode::Coherence
                     | ty::TypingMode::Analysis { .. }
+                    | ty::TypingMode::Borrowck { .. }
                     | ty::TypingMode::PostBorrowckAnalysis { .. } => false,
                     ty::TypingMode::PostAnalysis => !trait_ref.still_further_specializable(),
                 }
diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs
index 0bc8b94bbf4..e58f25f4ce7 100644
--- a/compiler/rustc_type_ir/src/fold.rs
+++ b/compiler/rustc_type_ir/src/fold.rs
@@ -54,7 +54,7 @@ use tracing::{debug, instrument};
 
 use crate::inherent::*;
 use crate::visit::{TypeVisitable, TypeVisitableExt as _};
-use crate::{self as ty, Interner};
+use crate::{self as ty, Interner, TypeFlags};
 
 #[cfg(feature = "nightly")]
 type Never = !;
@@ -438,12 +438,12 @@ where
 pub fn fold_regions<I: Interner, T>(
     cx: I,
     value: T,
-    mut f: impl FnMut(I::Region, ty::DebruijnIndex) -> I::Region,
+    f: impl FnMut(I::Region, ty::DebruijnIndex) -> I::Region,
 ) -> T
 where
     T: TypeFoldable<I>,
 {
-    value.fold_with(&mut RegionFolder::new(cx, &mut f))
+    value.fold_with(&mut RegionFolder::new(cx, f))
 }
 
 /// Folds over the substructure of a type, visiting its component
@@ -453,7 +453,7 @@ where
 /// new bound regions which are not visited by this visitors as
 /// they are not free; only regions that occur free will be
 /// visited by `fld_r`.
-pub struct RegionFolder<'a, I: Interner> {
+pub struct RegionFolder<I, F> {
     cx: I,
 
     /// Stores the index of a binder *just outside* the stuff we have
@@ -464,20 +464,21 @@ pub struct RegionFolder<'a, I: Interner> {
     /// Callback invokes for each free region. The `DebruijnIndex`
     /// points to the binder *just outside* the ones we have passed
     /// through.
-    fold_region_fn: &'a mut (dyn FnMut(I::Region, ty::DebruijnIndex) -> I::Region + 'a),
+    fold_region_fn: F,
 }
 
-impl<'a, I: Interner> RegionFolder<'a, I> {
+impl<I, F> RegionFolder<I, F> {
     #[inline]
-    pub fn new(
-        cx: I,
-        fold_region_fn: &'a mut dyn FnMut(I::Region, ty::DebruijnIndex) -> I::Region,
-    ) -> RegionFolder<'a, I> {
+    pub fn new(cx: I, fold_region_fn: F) -> RegionFolder<I, F> {
         RegionFolder { cx, current_index: ty::INNERMOST, fold_region_fn }
     }
 }
 
-impl<'a, I: Interner> TypeFolder<I> for RegionFolder<'a, I> {
+impl<I, F> TypeFolder<I> for RegionFolder<I, F>
+where
+    I: Interner,
+    F: FnMut(I::Region, ty::DebruijnIndex) -> I::Region,
+{
     fn cx(&self) -> I {
         self.cx
     }
@@ -502,4 +503,34 @@ impl<'a, I: Interner> TypeFolder<I> for RegionFolder<'a, I> {
             }
         }
     }
+
+    fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
+        if t.has_type_flags(
+            TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_BOUND | TypeFlags::HAS_RE_ERASED,
+        ) {
+            t.super_fold_with(self)
+        } else {
+            t
+        }
+    }
+
+    fn fold_const(&mut self, ct: I::Const) -> I::Const {
+        if ct.has_type_flags(
+            TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_BOUND | TypeFlags::HAS_RE_ERASED,
+        ) {
+            ct.super_fold_with(self)
+        } else {
+            ct
+        }
+    }
+
+    fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate {
+        if p.has_type_flags(
+            TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_BOUND | TypeFlags::HAS_RE_ERASED,
+        ) {
+            p.super_fold_with(self)
+        } else {
+            p
+        }
+    }
 }
diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs
index e512e8fc838..fec6e24e2cb 100644
--- a/compiler/rustc_type_ir/src/infer_ctxt.rs
+++ b/compiler/rustc_type_ir/src/infer_ctxt.rs
@@ -66,6 +66,14 @@ pub enum TypingMode<I: Interner> {
     /// }
     /// ```
     Analysis { defining_opaque_types: I::DefiningOpaqueTypes },
+    /// The behavior during MIR borrowck is identical to `TypingMode::Analysis`
+    /// except that the initial value for opaque types is the type computed during
+    /// HIR typeck with unique unconstrained region inference variables.
+    ///
+    /// This is currently only used with by the new solver as it results in new
+    /// non-universal defining uses of opaque types, which is a breaking change.
+    /// See tests/ui/impl-trait/non-defining-use/as-projection-term.rs.
+    Borrowck { defining_opaque_types: I::DefiningOpaqueTypes },
     /// Any analysis after borrowck for a given body should be able to use all the
     /// hidden types defined by borrowck, without being able to define any new ones.
     ///
@@ -95,6 +103,10 @@ impl<I: Interner> TypingMode<I> {
         TypingMode::Analysis { defining_opaque_types: cx.opaque_types_defined_by(body_def_id) }
     }
 
+    pub fn borrowck(cx: I, body_def_id: I::LocalDefId) -> TypingMode<I> {
+        TypingMode::Borrowck { defining_opaque_types: cx.opaque_types_defined_by(body_def_id) }
+    }
+
     pub fn post_borrowck_analysis(cx: I, body_def_id: I::LocalDefId) -> TypingMode<I> {
         TypingMode::PostBorrowckAnalysis {
             defined_opaque_types: cx.opaque_types_defined_by(body_def_id),
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index d4134bdf3a7..59c2d3c2fc8 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -146,67 +146,14 @@ pub trait Ty<I: Interner<Ty = Self>>:
     fn has_unsafe_fields(self) -> bool;
 
     fn fn_sig(self, interner: I) -> ty::Binder<I, ty::FnSig<I>> {
-        match self.kind() {
-            ty::FnPtr(sig_tys, hdr) => sig_tys.with(hdr),
-            ty::FnDef(def_id, args) => interner.fn_sig(def_id).instantiate(interner, args),
-            ty::Error(_) => {
-                // ignore errors (#54954)
-                ty::Binder::dummy(ty::FnSig {
-                    inputs_and_output: Default::default(),
-                    c_variadic: false,
-                    safety: I::Safety::safe(),
-                    abi: I::Abi::rust(),
-                })
-            }
-            ty::Closure(..) => panic!(
-                "to get the signature of a closure, use `args.as_closure().sig()` not `fn_sig()`",
-            ),
-            _ => panic!("Ty::fn_sig() called on non-fn type: {:?}", self),
-        }
+        self.kind().fn_sig(interner)
     }
 
     fn discriminant_ty(self, interner: I) -> I::Ty;
 
     fn async_destructor_ty(self, interner: I) -> I::Ty;
-
-    /// Returns `true` when the outermost type cannot be further normalized,
-    /// resolved, or instantiated. This includes all primitive types, but also
-    /// things like ADTs and trait objects, since even if their arguments or
-    /// nested types may be further simplified, the outermost [`ty::TyKind`] or
-    /// type constructor remains the same.
     fn is_known_rigid(self) -> bool {
-        match self.kind() {
-            ty::Bool
-            | ty::Char
-            | ty::Int(_)
-            | ty::Uint(_)
-            | ty::Float(_)
-            | ty::Adt(_, _)
-            | ty::Foreign(_)
-            | ty::Str
-            | ty::Array(_, _)
-            | ty::Pat(_, _)
-            | ty::Slice(_)
-            | ty::RawPtr(_, _)
-            | ty::Ref(_, _, _)
-            | ty::FnDef(_, _)
-            | ty::FnPtr(..)
-            | ty::UnsafeBinder(_)
-            | ty::Dynamic(_, _, _)
-            | ty::Closure(_, _)
-            | ty::CoroutineClosure(_, _)
-            | ty::Coroutine(_, _)
-            | ty::CoroutineWitness(..)
-            | ty::Never
-            | ty::Tuple(_) => true,
-
-            ty::Error(_)
-            | ty::Infer(_)
-            | ty::Alias(_, _)
-            | ty::Param(_)
-            | ty::Bound(_, _)
-            | ty::Placeholder(_) => false,
-        }
+        self.kind().is_known_rigid()
     }
 }
 
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index 8f86270d7dc..fce93b735d7 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -149,6 +149,8 @@ pub trait Interner:
     ) -> Option<Self::VariancesOf>;
 
     fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder<Self, Self::Ty>;
+    fn type_of_opaque_hir_typeck(self, def_id: Self::LocalDefId)
+    -> ty::EarlyBinder<Self, Self::Ty>;
 
     type AdtDef: AdtDef<Self>;
     fn adt_def(self, adt_def_id: Self::DefId) -> Self::AdtDef;
@@ -259,6 +261,8 @@ pub trait Interner:
 
     fn is_lang_item(self, def_id: Self::DefId, lang_item: TraitSolverLangItem) -> bool;
 
+    fn is_default_trait(self, def_id: Self::DefId) -> bool;
+
     fn as_lang_item(self, def_id: Self::DefId) -> Option<TraitSolverLangItem>;
 
     fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator<Item = Self::DefId>;
diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs
index d49f8d3093d..8dd7c4df244 100644
--- a/compiler/rustc_type_ir/src/relate/combine.rs
+++ b/compiler/rustc_type_ir/src/relate/combine.rs
@@ -137,6 +137,7 @@ where
                     Ok(a)
                 }
                 TypingMode::Analysis { .. }
+                | TypingMode::Borrowck { .. }
                 | TypingMode::PostBorrowckAnalysis { .. }
                 | TypingMode::PostAnalysis => structurally_relate_tys(relation, a, b),
             }
diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs
index 9bea4482b55..d35b22d517c 100644
--- a/compiler/rustc_type_ir/src/ty_kind.rs
+++ b/compiler/rustc_type_ir/src/ty_kind.rs
@@ -273,6 +273,68 @@ pub enum TyKind<I: Interner> {
     Error(I::ErrorGuaranteed),
 }
 
+impl<I: Interner> TyKind<I> {
+    pub fn fn_sig(self, interner: I) -> ty::Binder<I, ty::FnSig<I>> {
+        match self {
+            ty::FnPtr(sig_tys, hdr) => sig_tys.with(hdr),
+            ty::FnDef(def_id, args) => interner.fn_sig(def_id).instantiate(interner, args),
+            ty::Error(_) => {
+                // ignore errors (#54954)
+                ty::Binder::dummy(ty::FnSig {
+                    inputs_and_output: Default::default(),
+                    c_variadic: false,
+                    safety: I::Safety::safe(),
+                    abi: I::Abi::rust(),
+                })
+            }
+            ty::Closure(..) => panic!(
+                "to get the signature of a closure, use `args.as_closure().sig()` not `fn_sig()`",
+            ),
+            _ => panic!("Ty::fn_sig() called on non-fn type: {:?}", self),
+        }
+    }
+
+    /// Returns `true` when the outermost type cannot be further normalized,
+    /// resolved, or instantiated. This includes all primitive types, but also
+    /// things like ADTs and trait objects, since even if their arguments or
+    /// nested types may be further simplified, the outermost [`ty::TyKind`] or
+    /// type constructor remains the same.
+    pub fn is_known_rigid(self) -> bool {
+        match self {
+            ty::Bool
+            | ty::Char
+            | ty::Int(_)
+            | ty::Uint(_)
+            | ty::Float(_)
+            | ty::Adt(_, _)
+            | ty::Foreign(_)
+            | ty::Str
+            | ty::Array(_, _)
+            | ty::Pat(_, _)
+            | ty::Slice(_)
+            | ty::RawPtr(_, _)
+            | ty::Ref(_, _, _)
+            | ty::FnDef(_, _)
+            | ty::FnPtr(..)
+            | ty::UnsafeBinder(_)
+            | ty::Dynamic(_, _, _)
+            | ty::Closure(_, _)
+            | ty::CoroutineClosure(_, _)
+            | ty::Coroutine(_, _)
+            | ty::CoroutineWitness(..)
+            | ty::Never
+            | ty::Tuple(_) => true,
+
+            ty::Error(_)
+            | ty::Infer(_)
+            | ty::Alias(_, _)
+            | ty::Param(_)
+            | ty::Bound(_, _)
+            | ty::Placeholder(_) => false,
+        }
+    }
+}
+
 // This is manually implemented because a derive would require `I: Debug`
 impl<I: Interner> fmt::Debug for TyKind<I> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
diff --git a/compiler/stable_mir/Cargo.toml b/compiler/stable_mir/Cargo.toml
index d691a0e4f22..3a01ee5783e 100644
--- a/compiler/stable_mir/Cargo.toml
+++ b/compiler/stable_mir/Cargo.toml
@@ -4,5 +4,4 @@ version = "0.1.0-preview"
 edition = "2024"
 
 [dependencies]
-scoped-tls = "1.0"
-serde = { version = "1.0.125", features = [ "derive" ] }
+rustc_smir = { path = "../rustc_smir" }
diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs
index 70d42dfbfcb..cc0fb52433d 100644
--- a/compiler/stable_mir/src/lib.rs
+++ b/compiler/stable_mir/src/lib.rs
@@ -1,228 +1,7 @@
-//! The WIP stable interface to rustc internals.
+//! We've temporarily moved the `stable_mir` implementation to [`rustc_smir::stable_mir`],
+//! during refactoring to break the circular dependency between `rustc_smir` and `stable_mir`,
 //!
-//! For more information see <https://github.com/rust-lang/project-stable-mir>
-//!
-//! # Note
-//!
-//! This API is still completely unstable and subject to change.
-
-#![doc(
-    html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
-    test(attr(allow(unused_variables), deny(warnings)))
-)]
-//!
-//! This crate shall contain all type definitions and APIs that we expect third-party tools to invoke to
-//! interact with the compiler.
-//!
-//! The goal is to eventually be published on
-//! [crates.io](https://crates.io).
-
-use std::fmt::Debug;
-use std::{fmt, io};
-
-use serde::Serialize;
-
-use crate::compiler_interface::with;
-pub use crate::crate_def::{CrateDef, CrateDefType, DefId};
-pub use crate::error::*;
-use crate::mir::mono::StaticDef;
-use crate::mir::{Body, Mutability};
-use crate::ty::{FnDef, ForeignModuleDef, ImplDef, IndexedVal, Span, TraitDef, Ty};
-
-pub mod abi;
-#[macro_use]
-pub mod crate_def;
-pub mod compiler_interface;
-#[macro_use]
-pub mod error;
-pub mod mir;
-pub mod target;
-pub mod ty;
-pub mod visitor;
-
-/// Use String for now but we should replace it.
-pub type Symbol = String;
-
-/// The number that identifies a crate.
-pub type CrateNum = usize;
-
-impl Debug for DefId {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("DefId").field("id", &self.0).field("name", &self.name()).finish()
-    }
-}
-
-impl IndexedVal for DefId {
-    fn to_val(index: usize) -> Self {
-        DefId(index)
-    }
-
-    fn to_index(&self) -> usize {
-        self.0
-    }
-}
-
-/// A list of crate items.
-pub type CrateItems = Vec<CrateItem>;
-
-/// A list of trait decls.
-pub type TraitDecls = Vec<TraitDef>;
-
-/// A list of impl trait decls.
-pub type ImplTraitDecls = Vec<ImplDef>;
-
-/// Holds information about a crate.
-#[derive(Clone, PartialEq, Eq, Debug, Serialize)]
-pub struct Crate {
-    pub id: CrateNum,
-    pub name: Symbol,
-    pub is_local: bool,
-}
-
-impl Crate {
-    /// The list of foreign modules in this crate.
-    pub fn foreign_modules(&self) -> Vec<ForeignModuleDef> {
-        with(|cx| cx.foreign_modules(self.id))
-    }
-
-    /// The list of traits declared in this crate.
-    pub fn trait_decls(&self) -> TraitDecls {
-        with(|cx| cx.trait_decls(self.id))
-    }
-
-    /// The list of trait implementations in this crate.
-    pub fn trait_impls(&self) -> ImplTraitDecls {
-        with(|cx| cx.trait_impls(self.id))
-    }
-
-    /// Return a list of function definitions from this crate independent on their visibility.
-    pub fn fn_defs(&self) -> Vec<FnDef> {
-        with(|cx| cx.crate_functions(self.id))
-    }
-
-    /// Return a list of static items defined in this crate independent on their visibility.
-    pub fn statics(&self) -> Vec<StaticDef> {
-        with(|cx| cx.crate_statics(self.id))
-    }
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize)]
-pub enum ItemKind {
-    Fn,
-    Static,
-    Const,
-    Ctor(CtorKind),
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize)]
-pub enum CtorKind {
-    Const,
-    Fn,
-}
-
-pub type Filename = String;
-
-crate_def_with_ty! {
-    /// Holds information about an item in a crate.
-    #[derive(Serialize)]
-    pub CrateItem;
-}
-
-impl CrateItem {
-    /// This will return the body of an item or panic if it's not available.
-    pub fn expect_body(&self) -> mir::Body {
-        with(|cx| cx.mir_body(self.0))
-    }
-
-    /// Return the body of an item if available.
-    pub fn body(&self) -> Option<mir::Body> {
-        with(|cx| cx.has_body(self.0).then(|| cx.mir_body(self.0)))
-    }
-
-    /// Check if a body is available for this item.
-    pub fn has_body(&self) -> bool {
-        with(|cx| cx.has_body(self.0))
-    }
-
-    pub fn span(&self) -> Span {
-        with(|cx| cx.span_of_an_item(self.0))
-    }
-
-    pub fn kind(&self) -> ItemKind {
-        with(|cx| cx.item_kind(*self))
-    }
-
-    pub fn requires_monomorphization(&self) -> bool {
-        with(|cx| cx.requires_monomorphization(self.0))
-    }
-
-    pub fn ty(&self) -> Ty {
-        with(|cx| cx.def_ty(self.0))
-    }
-
-    pub fn is_foreign_item(&self) -> bool {
-        with(|cx| cx.is_foreign_item(self.0))
-    }
-
-    /// Emit MIR for this item body.
-    pub fn emit_mir<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
-        self.body()
-            .ok_or_else(|| io::Error::other(format!("No body found for `{}`", self.name())))?
-            .dump(w, &self.name())
-    }
-}
-
-/// Return the function where execution starts if the current
-/// crate defines that. This is usually `main`, but could be
-/// `start` if the crate is a no-std crate.
-pub fn entry_fn() -> Option<CrateItem> {
-    with(|cx| cx.entry_fn())
-}
-
-/// Access to the local crate.
-pub fn local_crate() -> Crate {
-    with(|cx| cx.local_crate())
-}
-
-/// Try to find a crate or crates if multiple crates exist from given name.
-pub fn find_crates(name: &str) -> Vec<Crate> {
-    with(|cx| cx.find_crates(name))
-}
-
-/// Try to find a crate with the given name.
-pub fn external_crates() -> Vec<Crate> {
-    with(|cx| cx.external_crates())
-}
-
-/// Retrieve all items in the local crate that have a MIR associated with them.
-pub fn all_local_items() -> CrateItems {
-    with(|cx| cx.all_local_items())
-}
-
-pub fn all_trait_decls() -> TraitDecls {
-    with(|cx| cx.all_trait_decls())
-}
-
-pub fn all_trait_impls() -> ImplTraitDecls {
-    with(|cx| cx.all_trait_impls())
-}
-
-/// A type that provides internal information but that can still be used for debug purpose.
-#[derive(Clone, PartialEq, Eq, Hash, Serialize)]
-pub struct Opaque(String);
-
-impl std::fmt::Display for Opaque {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{}", self.0)
-    }
-}
-
-impl std::fmt::Debug for Opaque {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{}", self.0)
-    }
-}
+//! This is a transitional measure as described in [PR #139319](https://github.com/rust-lang/rust/pull/139319).
+//! Once the refactoring is complete, the `stable_mir` implementation will be moved back here.
 
-pub fn opaque<T: Debug>(value: &T) -> Opaque {
-    Opaque(format!("{value:?}"))
-}
+pub use rustc_smir::stable_mir::*;