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/callconv.rs254
-rw-r--r--compiler/rustc_abi/src/layout.rs4
-rw-r--r--compiler/rustc_abi/src/layout/ty.rs (renamed from compiler/rustc_target/src/abi/mod.rs)17
-rw-r--r--compiler/rustc_abi/src/lib.rs8
-rw-r--r--compiler/rustc_ast/src/ast.rs40
-rw-r--r--compiler/rustc_ast/src/expand/autodiff_attrs.rs283
-rw-r--r--compiler/rustc_ast/src/expand/mod.rs2
-rw-r--r--compiler/rustc_ast/src/expand/typetree.rs90
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs10
-rw-r--r--compiler/rustc_ast/src/util/classify.rs12
-rw-r--r--compiler/rustc_ast/src/visit.rs10
-rw-r--r--compiler/rustc_ast_ir/src/lib.rs7
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs5
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs4
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs166
-rw-r--r--compiler/rustc_ast_lowering/src/lifetime_collector.rs2
-rw-r--r--compiler/rustc_ast_passes/messages.ftl20
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs74
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs28
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs4
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs62
-rw-r--r--compiler/rustc_attr/src/builtin.rs8
-rw-r--r--compiler/rustc_borrowck/src/dataflow.rs64
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs4
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs2
-rw-r--r--compiler/rustc_borrowck/src/nll.rs2
-rw-r--r--compiler/rustc_borrowck/src/places_conflict.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs4
-rw-r--r--compiler/rustc_borrowck/src/type_check/relate_tys.rs7
-rw-r--r--compiler/rustc_builtin_macros/Cargo.toml4
-rw-r--r--compiler/rustc_builtin_macros/messages.ftl9
-rw-r--r--compiler/rustc_builtin_macros/src/autodiff.rs820
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/clone.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/mod.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs12
-rw-r--r--compiler/rustc_builtin_macros/src/env.rs34
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs72
-rw-r--r--compiler/rustc_builtin_macros/src/format.rs22
-rw-r--r--compiler/rustc_builtin_macros/src/lib.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/util.rs32
-rw-r--r--compiler/rustc_codegen_cranelift/src/discriminant.rs3
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs5
-rw-r--r--compiler/rustc_codegen_cranelift/src/lib.rs1
-rw-r--r--compiler/rustc_codegen_gcc/messages.ftl3
-rw-r--r--compiler/rustc_codegen_gcc/src/attributes.rs22
-rw-r--r--compiler/rustc_codegen_gcc/src/builder.rs7
-rw-r--r--compiler/rustc_codegen_gcc/src/common.rs4
-rw-r--r--compiler/rustc_codegen_gcc/src/errors.rs36
-rw-r--r--compiler/rustc_codegen_gcc/src/gcc_util.rs24
-rw-r--r--compiler/rustc_codegen_gcc/src/intrinsic/mod.rs3
-rw-r--r--compiler/rustc_codegen_gcc/src/lib.rs1
-rw-r--r--compiler/rustc_codegen_gcc/src/type_of.rs8
-rw-r--r--compiler/rustc_codegen_llvm/Cargo.toml1
-rw-r--r--compiler/rustc_codegen_llvm/messages.ftl6
-rw-r--r--compiler/rustc_codegen_llvm/src/abi.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/asm.rs53
-rw-r--r--compiler/rustc_codegen_llvm/src/attributes.rs23
-rw-r--r--compiler/rustc_codegen_llvm/src/builder.rs9
-rw-r--r--compiler/rustc_codegen_llvm/src/common.rs17
-rw-r--r--compiler/rustc_codegen_llvm/src/context.rs26
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs21
-rw-r--r--compiler/rustc_codegen_llvm/src/declare.rs7
-rw-r--r--compiler/rustc_codegen_llvm/src/errors.rs24
-rw-r--r--compiler/rustc_codegen_llvm/src/intrinsic.rs5
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs30
-rw-r--r--compiler/rustc_codegen_llvm/src/type_of.rs3
-rw-r--r--compiler/rustc_codegen_ssa/Cargo.toml1
-rw-r--r--compiler/rustc_codegen_ssa/messages.ftl5
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs8
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs8
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs45
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs26
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/operand.rs5
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/place.rs6
-rw-r--r--compiler/rustc_const_eval/Cargo.toml1
-rw-r--r--compiler/rustc_const_eval/messages.ftl51
-rw-r--r--compiler/rustc_const_eval/src/check_consts/check.rs1
-rw-r--r--compiler/rustc_const_eval/src/check_consts/ops.rs6
-rw-r--r--compiler/rustc_const_eval/src/check_consts/resolver.rs9
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs2
-rw-r--r--compiler/rustc_const_eval/src/errors.rs12
-rw-r--r--compiler/rustc_const_eval/src/interpret/cast.rs27
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs8
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs80
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs31
-rw-r--r--compiler/rustc_const_eval/src/interpret/operand.rs15
-rw-r--r--compiler/rustc_const_eval/src/interpret/operator.rs3
-rw-r--r--compiler/rustc_const_eval/src/lib.rs4
-rw-r--r--compiler/rustc_data_structures/src/lib.rs1
-rw-r--r--compiler/rustc_data_structures/src/obligation_forest/mod.rs6
-rw-r--r--compiler/rustc_data_structures/src/obligation_forest/tests.rs68
-rw-r--r--compiler/rustc_data_structures/src/steal.rs2
-rw-r--r--compiler/rustc_data_structures/src/thinvec.rs92
-rw-r--r--compiler/rustc_data_structures/src/unhash.rs1
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0793.md2
-rw-r--r--compiler/rustc_expand/src/build.rs50
-rw-r--r--compiler/rustc_feature/src/accepted.rs10
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs5
-rw-r--r--compiler/rustc_feature/src/removed.rs6
-rw-r--r--compiler/rustc_feature/src/unstable.rs29
-rw-r--r--compiler/rustc_hir/src/hir.rs20
-rw-r--r--compiler/rustc_hir/src/intravisit.rs4
-rw-r--r--compiler/rustc_hir/src/lib.rs1
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl33
-rw-r--r--compiler/rustc_hir_analysis/src/autoderef.rs11
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs105
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsic.rs13
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs67
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/mod.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs104
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs59
-rw-r--r--compiler/rustc_hir_analysis/src/errors/precise_captures.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs156
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs57
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs26
-rw-r--r--compiler/rustc_hir_analysis/src/variance/mod.rs49
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs14
-rw-r--r--compiler/rustc_hir_typeck/Cargo.toml1
-rw-r--r--compiler/rustc_hir_typeck/src/_match.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/autoderef.rs5
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs8
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs75
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs77
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs11
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs13
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs38
-rw-r--r--compiler/rustc_hir_typeck/src/intrinsicck.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/method/mod.rs5
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs14
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs35
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs11
-rw-r--r--compiler/rustc_index/src/lib.rs1
-rw-r--r--compiler/rustc_index_macros/Cargo.toml2
-rw-r--r--compiler/rustc_infer/Cargo.toml1
-rw-r--r--compiler/rustc_infer/src/infer/at.rs146
-rw-r--r--compiler/rustc_infer/src/infer/canonical/query_response.rs7
-rw-r--r--compiler/rustc_infer/src/infer/context.rs114
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs68
-rw-r--r--compiler/rustc_infer/src/infer/opaque_types/mod.rs18
-rw-r--r--compiler/rustc_infer/src/infer/outlives/env.rs87
-rw-r--r--compiler/rustc_infer/src/infer/outlives/mod.rs15
-rw-r--r--compiler/rustc_infer/src/infer/outlives/obligations.rs11
-rw-r--r--compiler/rustc_infer/src/infer/projection.rs4
-rw-r--r--compiler/rustc_infer/src/infer/region_constraints/leak_check.rs65
-rw-r--r--compiler/rustc_infer/src/infer/region_constraints/mod.rs112
-rw-r--r--compiler/rustc_infer/src/infer/relate/combine.rs240
-rw-r--r--compiler/rustc_infer/src/infer/relate/generalize.rs8
-rw-r--r--compiler/rustc_infer/src/infer/relate/lattice.rs15
-rw-r--r--compiler/rustc_infer/src/infer/relate/mod.rs7
-rw-r--r--compiler/rustc_infer/src/infer/relate/type_relating.rs30
-rw-r--r--compiler/rustc_infer/src/infer/resolve.rs2
-rw-r--r--compiler/rustc_infer/src/infer/type_variable.rs9
-rw-r--r--compiler/rustc_infer/src/traits/engine.rs10
-rw-r--r--compiler/rustc_infer/src/traits/mod.rs3
-rw-r--r--compiler/rustc_infer/src/traits/project.rs59
-rw-r--r--compiler/rustc_interface/src/tests.rs2
-rw-r--r--compiler/rustc_lexer/src/lib.rs93
-rw-r--r--compiler/rustc_lint/messages.ftl5
-rw-r--r--compiler/rustc_lint/src/context/diagnostics.rs3
-rw-r--r--compiler/rustc_lint/src/if_let_rescope.rs2
-rw-r--r--compiler/rustc_lint/src/lints.rs10
-rw-r--r--compiler/rustc_lint/src/non_local_def.rs94
-rw-r--r--compiler/rustc_lint/src/traits.rs5
-rw-r--r--compiler/rustc_lint/src/unused.rs10
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs94
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs2
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp2
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp25
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs3
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs5
-rw-r--r--compiler/rustc_middle/Cargo.toml1
-rw-r--r--compiler/rustc_middle/src/lib.rs1
-rw-r--r--compiler/rustc_middle/src/mir/basic_blocks.rs7
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs1
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs9
-rw-r--r--compiler/rustc_middle/src/ty/adt.rs9
-rw-r--r--compiler/rustc_middle/src/ty/context.rs26
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs5
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs24
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs22
-rw-r--r--compiler/rustc_middle/src/ty/typeck_results.rs34
-rw-r--r--compiler/rustc_mir_build/messages.ftl2
-rw-r--r--compiler/rustc_mir_build/src/errors.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs20
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs27
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/cursor.rs11
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/direction.rs66
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/engine.rs89
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/mod.rs265
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/tests.rs12
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs26
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/initialized.rs91
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/liveness.rs48
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs107
-rw-r--r--compiler/rustc_mir_dataflow/src/lib.rs6
-rw-r--r--compiler/rustc_mir_dataflow/src/value_analysis.rs9
-rw-r--r--compiler/rustc_mir_transform/src/elaborate_drops.rs2
-rw-r--r--compiler/rustc_mir_transform/src/ref_prop.rs2
-rw-r--r--compiler/rustc_mir_transform/src/validate.rs11
-rw-r--r--compiler/rustc_monomorphize/src/partitioning.rs7
-rw-r--r--compiler/rustc_next_trait_solver/src/lib.rs3
-rw-r--r--compiler/rustc_next_trait_solver/src/relate.rs15
-rw-r--r--compiler/rustc_next_trait_solver/src/relate/combine.rs34
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs20
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs2
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs124
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/mod.rs11
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs65
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs34
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/search_graph.rs3
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/trait_goals.rs11
-rw-r--r--compiler/rustc_parse/Cargo.toml2
-rw-r--r--compiler/rustc_parse/messages.ftl7
-rw-r--r--compiler/rustc_parse/src/errors.rs26
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs84
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs2
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs7
-rw-r--r--compiler/rustc_parse/src/parser/item.rs6
-rw-r--r--compiler/rustc_parse/src/parser/path.rs4
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs99
-rw-r--r--compiler/rustc_parse_format/src/lib.rs8
-rw-r--r--compiler/rustc_passes/messages.ftl10
-rw-r--r--compiler/rustc_passes/src/check_attr.rs16
-rw-r--r--compiler/rustc_passes/src/errors.rs19
-rw-r--r--compiler/rustc_passes/src/hir_stats.rs3
-rw-r--r--compiler/rustc_passes/src/stability.rs16
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs23
-rw-r--r--compiler/rustc_resolve/src/def_collector.rs18
-rw-r--r--compiler/rustc_resolve/src/late.rs4
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs39
-rw-r--r--compiler/rustc_serialize/src/lib.rs1
-rw-r--r--compiler/rustc_serialize/src/serialize.rs22
-rw-r--r--compiler/rustc_session/src/config.rs5
-rw-r--r--compiler/rustc_session/src/options.rs31
-rw-r--r--compiler/rustc_session/src/parse.rs2
-rw-r--r--compiler/rustc_session/src/session.rs2
-rw-r--r--compiler/rustc_session/src/utils.rs2
-rw-r--r--compiler/rustc_span/Cargo.toml2
-rw-r--r--compiler/rustc_span/src/symbol.rs11
-rw-r--r--compiler/rustc_target/src/asm/aarch64.rs97
-rw-r--r--compiler/rustc_target/src/asm/mod.rs27
-rw-r--r--compiler/rustc_target/src/callconv/aarch64.rs (renamed from compiler/rustc_target/src/abi/call/aarch64.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/amdgpu.rs (renamed from compiler/rustc_target/src/abi/call/amdgpu.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/arm.rs (renamed from compiler/rustc_target/src/abi/call/arm.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/avr.rs (renamed from compiler/rustc_target/src/abi/call/avr.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/bpf.rs (renamed from compiler/rustc_target/src/abi/call/bpf.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/csky.rs (renamed from compiler/rustc_target/src/abi/call/csky.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/hexagon.rs (renamed from compiler/rustc_target/src/abi/call/hexagon.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/loongarch.rs (renamed from compiler/rustc_target/src/abi/call/loongarch.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/m68k.rs (renamed from compiler/rustc_target/src/abi/call/m68k.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/mips.rs (renamed from compiler/rustc_target/src/abi/call/mips.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/mips64.rs (renamed from compiler/rustc_target/src/abi/call/mips64.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/mod.rs (renamed from compiler/rustc_target/src/abi/call/mod.rs)249
-rw-r--r--compiler/rustc_target/src/callconv/msp430.rs (renamed from compiler/rustc_target/src/abi/call/msp430.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/nvptx64.rs (renamed from compiler/rustc_target/src/abi/call/nvptx64.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/powerpc.rs (renamed from compiler/rustc_target/src/abi/call/powerpc.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/powerpc64.rs (renamed from compiler/rustc_target/src/abi/call/powerpc64.rs)16
-rw-r--r--compiler/rustc_target/src/callconv/riscv.rs (renamed from compiler/rustc_target/src/abi/call/riscv.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/s390x.rs (renamed from compiler/rustc_target/src/abi/call/s390x.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/sparc.rs (renamed from compiler/rustc_target/src/abi/call/sparc.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/sparc64.rs (renamed from compiler/rustc_target/src/abi/call/sparc64.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/wasm.rs (renamed from compiler/rustc_target/src/abi/call/wasm.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/x86.rs (renamed from compiler/rustc_target/src/abi/call/x86.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/x86_64.rs (renamed from compiler/rustc_target/src/abi/call/x86_64.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/x86_win64.rs (renamed from compiler/rustc_target/src/abi/call/x86_win64.rs)0
-rw-r--r--compiler/rustc_target/src/callconv/xtensa.rs (renamed from compiler/rustc_target/src/abi/call/xtensa.rs)0
-rw-r--r--compiler/rustc_target/src/json.rs6
-rw-r--r--compiler/rustc_target/src/lib.rs11
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs3
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs3
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs3
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs3
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs3
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs3
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs3
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs3
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs3
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs3
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_kmc_solid_asp3.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_linux_android.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs4
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs4
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_unknown_hermit.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_unknown_illumos.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_unknown_netbsd.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_unknown_none.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_unknown_openbsd.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_unknown_redox.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_unknown_teeos.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_unknown_trusty.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_unknown_uefi.rs4
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_uwp_windows_msvc.rs4
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs3
-rw-r--r--compiler/rustc_target/src/spec/targets/arm64e_apple_darwin.rs3
-rw-r--r--compiler/rustc_target/src/spec/targets/arm64e_apple_ios.rs3
-rw-r--r--compiler/rustc_target/src/spec/targets/arm64e_apple_tvos.rs3
-rw-r--r--compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs4
-rw-r--r--compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs7
-rw-r--r--compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs7
-rw-r--r--compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs7
-rw-r--r--compiler/rustc_target/src/target_features.rs4
-rw-r--r--compiler/rustc_trait_selection/Cargo.toml1
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs24
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs2
-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/note_and_explain.rs4
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs5
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/mod.rs7
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs5
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs20
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs6
-rw-r--r--compiler/rustc_trait_selection/src/solve.rs1
-rw-r--r--compiler/rustc_trait_selection/src/solve/delegate.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs61
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/analyse.rs6
-rw-r--r--compiler/rustc_trait_selection/src/solve/select.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs22
-rw-r--r--compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs26
-rw-r--r--compiler/rustc_trait_selection/src/traits/engine.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs55
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/normalize.rs14
-rw-r--r--compiler/rustc_trait_selection/src/traits/outlives_bounds.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs63
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs14
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs65
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs20
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs41
-rw-r--r--compiler/rustc_traits/src/normalize_projection_ty.rs5
-rw-r--r--compiler/rustc_ty_utils/Cargo.toml1
-rw-r--r--compiler/rustc_ty_utils/src/abi.rs10
-rw-r--r--compiler/rustc_ty_utils/src/layout.rs20
-rw-r--r--compiler/rustc_type_ir/Cargo.toml3
-rw-r--r--compiler/rustc_type_ir/src/fold.rs7
-rw-r--r--compiler/rustc_type_ir/src/infer_ctxt.rs63
-rw-r--r--compiler/rustc_type_ir/src/inherent.rs4
-rw-r--r--compiler/rustc_type_ir/src/interner.rs4
-rw-r--r--compiler/rustc_type_ir/src/lib.rs6
-rw-r--r--compiler/rustc_type_ir/src/relate.rs25
-rw-r--r--compiler/rustc_type_ir/src/relate/combine.rs246
-rw-r--r--compiler/rustc_type_ir/src/relate/solver_relating.rs394
-rw-r--r--compiler/rustc_type_ir/src/search_graph/mod.rs12
-rw-r--r--compiler/rustc_type_ir/src/solve/inspect.rs2
-rw-r--r--compiler/rustc_type_ir/src/solve/mod.rs4
-rw-r--r--compiler/rustc_type_ir/src/visit.rs8
-rw-r--r--compiler/stable_mir/src/mir/body.rs6
-rw-r--r--compiler/stable_mir/src/mir/pretty.rs34
-rw-r--r--compiler/stable_mir/src/mir/visit.rs38
-rw-r--r--compiler/stable_mir/src/visitor.rs16
375 files changed, 5659 insertions, 3684 deletions
diff --git a/compiler/rustc_abi/src/callconv.rs b/compiler/rustc_abi/src/callconv.rs
new file mode 100644
index 00000000000..2ecac8a9df1
--- /dev/null
+++ b/compiler/rustc_abi/src/callconv.rs
@@ -0,0 +1,254 @@
+mod abi {
+    pub(crate) use crate::Primitive::*;
+    pub(crate) use crate::Variants;
+}
+
+use rustc_macros::HashStable_Generic;
+
+use crate::{Abi, Align, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout};
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+pub enum RegKind {
+    Integer,
+    Float,
+    Vector,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+pub struct Reg {
+    pub kind: RegKind,
+    pub size: Size,
+}
+
+macro_rules! reg_ctor {
+    ($name:ident, $kind:ident, $bits:expr) => {
+        pub fn $name() -> Reg {
+            Reg { kind: RegKind::$kind, size: Size::from_bits($bits) }
+        }
+    };
+}
+
+impl Reg {
+    reg_ctor!(i8, Integer, 8);
+    reg_ctor!(i16, Integer, 16);
+    reg_ctor!(i32, Integer, 32);
+    reg_ctor!(i64, Integer, 64);
+    reg_ctor!(i128, Integer, 128);
+
+    reg_ctor!(f32, Float, 32);
+    reg_ctor!(f64, Float, 64);
+}
+
+impl Reg {
+    pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align {
+        let dl = cx.data_layout();
+        match self.kind {
+            RegKind::Integer => match self.size.bits() {
+                1 => dl.i1_align.abi,
+                2..=8 => dl.i8_align.abi,
+                9..=16 => dl.i16_align.abi,
+                17..=32 => dl.i32_align.abi,
+                33..=64 => dl.i64_align.abi,
+                65..=128 => dl.i128_align.abi,
+                _ => panic!("unsupported integer: {self:?}"),
+            },
+            RegKind::Float => match self.size.bits() {
+                16 => dl.f16_align.abi,
+                32 => dl.f32_align.abi,
+                64 => dl.f64_align.abi,
+                128 => dl.f128_align.abi,
+                _ => panic!("unsupported float: {self:?}"),
+            },
+            RegKind::Vector => dl.vector_align(self.size).abi,
+        }
+    }
+}
+
+/// Return value from the `homogeneous_aggregate` test function.
+#[derive(Copy, Clone, Debug)]
+pub enum HomogeneousAggregate {
+    /// Yes, all the "leaf fields" of this struct are passed in the
+    /// same way (specified in the `Reg` value).
+    Homogeneous(Reg),
+
+    /// There are no leaf fields at all.
+    NoData,
+}
+
+/// Error from the `homogeneous_aggregate` test function, indicating
+/// there are distinct leaf fields passed in different ways,
+/// or this is uninhabited.
+#[derive(Copy, Clone, Debug)]
+pub struct Heterogeneous;
+
+impl HomogeneousAggregate {
+    /// If this is a homogeneous aggregate, returns the homogeneous
+    /// unit, else `None`.
+    pub fn unit(self) -> Option<Reg> {
+        match self {
+            HomogeneousAggregate::Homogeneous(reg) => Some(reg),
+            HomogeneousAggregate::NoData => None,
+        }
+    }
+
+    /// Try to combine two `HomogeneousAggregate`s, e.g. from two fields in
+    /// the same `struct`. Only succeeds if only one of them has any data,
+    /// or both units are identical.
+    fn merge(self, other: HomogeneousAggregate) -> Result<HomogeneousAggregate, Heterogeneous> {
+        match (self, other) {
+            (x, HomogeneousAggregate::NoData) | (HomogeneousAggregate::NoData, x) => Ok(x),
+
+            (HomogeneousAggregate::Homogeneous(a), HomogeneousAggregate::Homogeneous(b)) => {
+                if a != b {
+                    return Err(Heterogeneous);
+                }
+                Ok(self)
+            }
+        }
+    }
+}
+
+impl<'a, Ty> TyAndLayout<'a, Ty> {
+    /// Returns `true` if this is an aggregate type (including a ScalarPair!)
+    pub fn is_aggregate(&self) -> bool {
+        match self.abi {
+            Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } => false,
+            Abi::ScalarPair(..) | Abi::Aggregate { .. } => true,
+        }
+    }
+
+    /// Returns `Homogeneous` if this layout is an aggregate containing fields of
+    /// only a single type (e.g., `(u32, u32)`). Such aggregates are often
+    /// special-cased in ABIs.
+    ///
+    /// Note: We generally ignore 1-ZST fields when computing this value (see #56877).
+    ///
+    /// This is public so that it can be used in unit tests, but
+    /// should generally only be relevant to the ABI details of
+    /// specific targets.
+    pub fn homogeneous_aggregate<C>(&self, cx: &C) -> Result<HomogeneousAggregate, Heterogeneous>
+    where
+        Ty: TyAbiInterface<'a, C> + Copy,
+    {
+        match self.abi {
+            Abi::Uninhabited => Err(Heterogeneous),
+
+            // The primitive for this algorithm.
+            Abi::Scalar(scalar) => {
+                let kind = match scalar.primitive() {
+                    abi::Int(..) | abi::Pointer(_) => RegKind::Integer,
+                    abi::Float(_) => RegKind::Float,
+                };
+                Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size }))
+            }
+
+            Abi::Vector { .. } => {
+                assert!(!self.is_zst());
+                Ok(HomogeneousAggregate::Homogeneous(Reg {
+                    kind: RegKind::Vector,
+                    size: self.size,
+                }))
+            }
+
+            Abi::ScalarPair(..) | Abi::Aggregate { sized: true } => {
+                // Helper for computing `homogeneous_aggregate`, allowing a custom
+                // starting offset (used below for handling variants).
+                let from_fields_at =
+                    |layout: Self,
+                     start: Size|
+                     -> Result<(HomogeneousAggregate, Size), Heterogeneous> {
+                        let is_union = match layout.fields {
+                            FieldsShape::Primitive => {
+                                unreachable!("aggregates can't have `FieldsShape::Primitive`")
+                            }
+                            FieldsShape::Array { count, .. } => {
+                                assert_eq!(start, Size::ZERO);
+
+                                let result = if count > 0 {
+                                    layout.field(cx, 0).homogeneous_aggregate(cx)?
+                                } else {
+                                    HomogeneousAggregate::NoData
+                                };
+                                return Ok((result, layout.size));
+                            }
+                            FieldsShape::Union(_) => true,
+                            FieldsShape::Arbitrary { .. } => false,
+                        };
+
+                        let mut result = HomogeneousAggregate::NoData;
+                        let mut total = start;
+
+                        for i in 0..layout.fields.count() {
+                            let field = layout.field(cx, i);
+                            if field.is_1zst() {
+                                // No data here and no impact on layout, can be ignored.
+                                // (We might be able to also ignore all aligned ZST but that's less clear.)
+                                continue;
+                            }
+
+                            if !is_union && total != layout.fields.offset(i) {
+                                // This field isn't just after the previous one we considered, abort.
+                                return Err(Heterogeneous);
+                            }
+
+                            result = result.merge(field.homogeneous_aggregate(cx)?)?;
+
+                            // Keep track of the offset (without padding).
+                            let size = field.size;
+                            if is_union {
+                                total = total.max(size);
+                            } else {
+                                total += size;
+                            }
+                        }
+
+                        Ok((result, total))
+                    };
+
+                let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?;
+
+                match &self.variants {
+                    abi::Variants::Single { .. } => {}
+                    abi::Variants::Multiple { variants, .. } => {
+                        // Treat enum variants like union members.
+                        // HACK(eddyb) pretend the `enum` field (discriminant)
+                        // is at the start of every variant (otherwise the gap
+                        // at the start of all variants would disqualify them).
+                        //
+                        // NB: for all tagged `enum`s (which include all non-C-like
+                        // `enum`s with defined FFI representation), this will
+                        // match the homogeneous computation on the equivalent
+                        // `struct { tag; union { variant1; ... } }` and/or
+                        // `union { struct { tag; variant1; } ... }`
+                        // (the offsets of variant fields should be identical
+                        // between the two for either to be a homogeneous aggregate).
+                        let variant_start = total;
+                        for variant_idx in variants.indices() {
+                            let (variant_result, variant_total) =
+                                from_fields_at(self.for_variant(cx, variant_idx), variant_start)?;
+
+                            result = result.merge(variant_result)?;
+                            total = total.max(variant_total);
+                        }
+                    }
+                }
+
+                // There needs to be no padding.
+                if total != self.size {
+                    Err(Heterogeneous)
+                } else {
+                    match result {
+                        HomogeneousAggregate::Homogeneous(_) => {
+                            assert_ne!(total, Size::ZERO);
+                        }
+                        HomogeneousAggregate::NoData => {
+                            assert_eq!(total, Size::ZERO);
+                        }
+                    }
+                    Ok(result)
+                }
+            }
+            Abi::Aggregate { sized: false } => Err(Heterogeneous),
+        }
+    }
+}
diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs
index 620a051eeb7..6e1299944a0 100644
--- a/compiler/rustc_abi/src/layout.rs
+++ b/compiler/rustc_abi/src/layout.rs
@@ -11,6 +11,10 @@ use crate::{
     Variants, WrappingRange,
 };
 
+mod ty;
+
+pub use ty::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
+
 // A variant is absent if it's uninhabited and only has ZST fields.
 // Present uninhabited variants only require space for their fields,
 // but *not* an encoding of the discriminant (e.g., a tag value).
diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_abi/src/layout/ty.rs
index fc92e755fea..c6812c4d4c0 100644
--- a/compiler/rustc_target/src/abi/mod.rs
+++ b/compiler/rustc_abi/src/layout/ty.rs
@@ -1,24 +1,13 @@
 use std::fmt;
 use std::ops::Deref;
 
-pub use Float::*;
-pub use Integer::*;
-pub use Primitive::*;
+use Float::*;
+use Primitive::*;
 use rustc_data_structures::intern::Interned;
 use rustc_macros::HashStable_Generic;
 
-use crate::json::{Json, ToJson};
-
-pub mod call;
-
 // Explicitly import `Float` to avoid ambiguity with `Primitive::Float`.
-pub use rustc_abi::{Float, *};
-
-impl ToJson for Endian {
-    fn to_json(&self) -> Json {
-        self.as_str().to_json()
-    }
-}
+use crate::{Float, *};
 
 rustc_index::newtype_index! {
     /// The *source-order* index of a field in a variant.
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index fa7c98a7fa0..84d756b6d51 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -1,6 +1,7 @@
 // tidy-alphabetical-start
 #![cfg_attr(feature = "nightly", allow(internal_features))]
 #![cfg_attr(feature = "nightly", doc(rust_logo))]
+#![cfg_attr(feature = "nightly", feature(rustc_attrs))]
 #![cfg_attr(feature = "nightly", feature(rustdoc_internals))]
 #![cfg_attr(feature = "nightly", feature(step_trait))]
 #![warn(unreachable_pub)]
@@ -22,11 +23,16 @@ use rustc_macros::HashStable_Generic;
 #[cfg(feature = "nightly")]
 use rustc_macros::{Decodable_Generic, Encodable_Generic};
 
+mod callconv;
 mod layout;
 #[cfg(test)]
 mod tests;
 
-pub use layout::{LayoutCalculator, LayoutCalculatorError};
+pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind};
+pub use layout::{
+    FIRST_VARIANT, FieldIdx, Layout, LayoutCalculator, LayoutCalculatorError, TyAbiInterface,
+    TyAndLayout, VariantIdx,
+};
 
 /// Requirements for a `StableHashingContext` to be used in this crate.
 /// This is a hack to allow using the `HashStable_Generic` derive macro
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index f2b52eea12f..02cb6f188a7 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -23,7 +23,7 @@ use std::{cmp, fmt, mem};
 
 pub use GenericArgs::*;
 pub use UnsafeSource::*;
-pub use rustc_ast_ir::{Movability, Mutability};
+pub use rustc_ast_ir::{Movability, Mutability, Pinnedness};
 use rustc_data_structures::packed::Pu128;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::stack::ensure_sufficient_stack;
@@ -308,7 +308,7 @@ impl TraitBoundModifiers {
 
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum GenericBound {
-    Trait(PolyTraitRef, TraitBoundModifiers),
+    Trait(PolyTraitRef),
     Outlives(Lifetime),
     /// Precise capturing syntax: `impl Sized + use<'a>`
     Use(ThinVec<PreciseCapturingArg>, Span),
@@ -1213,10 +1213,12 @@ impl Expr {
 
     pub fn to_bound(&self) -> Option<GenericBound> {
         match &self.kind {
-            ExprKind::Path(None, path) => Some(GenericBound::Trait(
-                PolyTraitRef::new(ThinVec::new(), path.clone(), self.span),
+            ExprKind::Path(None, path) => Some(GenericBound::Trait(PolyTraitRef::new(
+                ThinVec::new(),
+                path.clone(),
                 TraitBoundModifiers::NONE,
-            )),
+                self.span,
+            ))),
             _ => None,
         }
     }
@@ -2161,16 +2163,16 @@ pub enum TyKind {
     Ptr(MutTy),
     /// A reference (`&'a T` or `&'a mut T`).
     Ref(Option<Lifetime>, MutTy),
+    /// A pinned reference (`&'a pin const T` or `&'a pin mut T`).
+    ///
+    /// Desugars into `Pin<&'a T>` or `Pin<&'a mut T>`.
+    PinnedRef(Option<Lifetime>, MutTy),
     /// A bare function (e.g., `fn(usize) -> bool`).
     BareFn(P<BareFnTy>),
     /// The never type (`!`).
     Never,
     /// A tuple (`(A, B, C, D,...)`).
     Tup(ThinVec<P<Ty>>),
-    /// An anonymous struct type i.e. `struct { foo: Type }`.
-    AnonStruct(NodeId, ThinVec<FieldDef>),
-    /// An anonymous union type i.e. `union { bar: Type }`.
-    AnonUnion(NodeId, ThinVec<FieldDef>),
     /// A path (`module::module::...::Type`), optionally
     /// "qualified", e.g., `<Vec<T> as SomeTrait>::SomeType`.
     ///
@@ -2227,10 +2229,6 @@ impl TyKind {
             None
         }
     }
-
-    pub fn is_anon_adt(&self) -> bool {
-        matches!(self, TyKind::AnonStruct(..) | TyKind::AnonUnion(..))
-    }
 }
 
 /// Syntax used to declare a trait object.
@@ -2509,7 +2507,10 @@ impl Param {
             if ident.name == kw::SelfLower {
                 return match self.ty.kind {
                     TyKind::ImplicitSelf => Some(respan(self.pat.span, SelfKind::Value(mutbl))),
-                    TyKind::Ref(lt, MutTy { ref ty, mutbl }) if ty.kind.is_implicit_self() => {
+                    TyKind::Ref(lt, MutTy { ref ty, mutbl })
+                    | TyKind::PinnedRef(lt, MutTy { ref ty, mutbl })
+                        if ty.kind.is_implicit_self() =>
+                    {
                         Some(respan(self.pat.span, SelfKind::Region(lt, mutbl)))
                     }
                     _ => Some(respan(
@@ -2973,6 +2974,9 @@ pub struct PolyTraitRef {
     /// The `'a` in `for<'a> Foo<&'a T>`.
     pub bound_generic_params: ThinVec<GenericParam>,
 
+    // Optional constness, asyncness, or polarity.
+    pub modifiers: TraitBoundModifiers,
+
     /// The `Foo<&'a T>` in `<'a> Foo<&'a T>`.
     pub trait_ref: TraitRef,
 
@@ -2980,9 +2984,15 @@ pub struct PolyTraitRef {
 }
 
 impl PolyTraitRef {
-    pub fn new(generic_params: ThinVec<GenericParam>, path: Path, span: Span) -> Self {
+    pub fn new(
+        generic_params: ThinVec<GenericParam>,
+        path: Path,
+        modifiers: TraitBoundModifiers,
+        span: Span,
+    ) -> Self {
         PolyTraitRef {
             bound_generic_params: generic_params,
+            modifiers,
             trait_ref: TraitRef { path, ref_id: DUMMY_NODE_ID },
             span,
         }
diff --git a/compiler/rustc_ast/src/expand/autodiff_attrs.rs b/compiler/rustc_ast/src/expand/autodiff_attrs.rs
new file mode 100644
index 00000000000..05714731b9d
--- /dev/null
+++ b/compiler/rustc_ast/src/expand/autodiff_attrs.rs
@@ -0,0 +1,283 @@
+//! This crate handles the user facing autodiff macro. For each `#[autodiff(...)]` attribute,
+//! we create an [`AutoDiffItem`] which contains the source and target function names. The source
+//! is the function to which the autodiff attribute is applied, and the target is the function
+//! getting generated by us (with a name given by the user as the first autodiff arg).
+
+use std::fmt::{self, Display, Formatter};
+use std::str::FromStr;
+
+use crate::expand::typetree::TypeTree;
+use crate::expand::{Decodable, Encodable, HashStable_Generic};
+use crate::ptr::P;
+use crate::{Ty, TyKind};
+
+/// Forward and Reverse Mode are well known names for automatic differentiation implementations.
+/// Enzyme does support both, but with different semantics, see DiffActivity. The First variants
+/// are a hack to support higher order derivatives. We need to compute first order derivatives
+/// before we compute second order derivatives, otherwise we would differentiate our placeholder
+/// functions. The proper solution is to recognize and resolve this DAG of autodiff invocations,
+/// as it's already done in the C++ and Julia frontend of Enzyme.
+///
+/// (FIXME) remove *First variants.
+/// Documentation for using [reverse](https://enzyme.mit.edu/rust/rev.html) and
+/// [forward](https://enzyme.mit.edu/rust/fwd.html) mode is available online.
+#[derive(Clone, Copy, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+pub enum DiffMode {
+    /// No autodiff is applied (used during error handling).
+    Error,
+    /// The primal function which we will differentiate.
+    Source,
+    /// The target function, to be created using forward mode AD.
+    Forward,
+    /// The target function, to be created using reverse mode AD.
+    Reverse,
+    /// The target function, to be created using forward mode AD.
+    /// This target function will also be used as a source for higher order derivatives,
+    /// so compute it before all Forward/Reverse targets and optimize it through llvm.
+    ForwardFirst,
+    /// The target function, to be created using reverse mode AD.
+    /// This target function will also be used as a source for higher order derivatives,
+    /// so compute it before all Forward/Reverse targets and optimize it through llvm.
+    ReverseFirst,
+}
+
+/// Dual and Duplicated (and their Only variants) are getting lowered to the same Enzyme Activity.
+/// However, under forward mode we overwrite the previous shadow value, while for reverse mode
+/// we add to the previous shadow value. To not surprise users, we picked different names.
+/// Dual numbers is also a quite well known name for forward mode AD types.
+#[derive(Clone, Copy, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+pub enum DiffActivity {
+    /// Implicit or Explicit () return type, so a special case of Const.
+    None,
+    /// Don't compute derivatives with respect to this input/output.
+    Const,
+    /// Reverse Mode, Compute derivatives for this scalar input/output.
+    Active,
+    /// Reverse Mode, Compute derivatives for this scalar output, but don't compute
+    /// the original return value.
+    ActiveOnly,
+    /// Forward Mode, Compute derivatives for this input/output and *overwrite* the shadow argument
+    /// with it.
+    Dual,
+    /// Forward Mode, Compute derivatives for this input/output and *overwrite* the shadow argument
+    /// with it. Drop the code which updates the original input/output for maximum performance.
+    DualOnly,
+    /// Reverse Mode, Compute derivatives for this &T or *T input and *add* it to the shadow argument.
+    Duplicated,
+    /// Reverse Mode, Compute derivatives for this &T or *T input and *add* it to the shadow argument.
+    /// Drop the code which updates the original input for maximum performance.
+    DuplicatedOnly,
+    /// All Integers must be Const, but these are used to mark the integer which represents the
+    /// length of a slice/vec. This is used for safety checks on slices.
+    FakeActivitySize,
+}
+/// We generate one of these structs for each `#[autodiff(...)]` attribute.
+#[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+pub struct AutoDiffItem {
+    /// The name of the function getting differentiated
+    pub source: String,
+    /// The name of the function being generated
+    pub target: String,
+    pub attrs: AutoDiffAttrs,
+    /// Describe the memory layout of input types
+    pub inputs: Vec<TypeTree>,
+    /// Describe the memory layout of the output type
+    pub output: TypeTree,
+}
+#[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+pub struct AutoDiffAttrs {
+    /// Conceptually either forward or reverse mode AD, as described in various autodiff papers and
+    /// 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,
+    pub ret_activity: DiffActivity,
+    pub input_activity: Vec<DiffActivity>,
+}
+
+impl DiffMode {
+    pub fn is_rev(&self) -> bool {
+        matches!(self, DiffMode::Reverse | DiffMode::ReverseFirst)
+    }
+    pub fn is_fwd(&self) -> bool {
+        matches!(self, DiffMode::Forward | DiffMode::ForwardFirst)
+    }
+}
+
+impl Display for DiffMode {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        match self {
+            DiffMode::Error => write!(f, "Error"),
+            DiffMode::Source => write!(f, "Source"),
+            DiffMode::Forward => write!(f, "Forward"),
+            DiffMode::Reverse => write!(f, "Reverse"),
+            DiffMode::ForwardFirst => write!(f, "ForwardFirst"),
+            DiffMode::ReverseFirst => write!(f, "ReverseFirst"),
+        }
+    }
+}
+
+/// Active(Only) is valid in reverse-mode AD for scalar float returns (f16/f32/...).
+/// Dual(Only) is valid in forward-mode AD for scalar float returns (f16/f32/...).
+/// Const is valid for all cases and means that we don't compute derivatives wrt. this output.
+/// That usually means we have a &mut or *mut T output and compute derivatives wrt. that arg,
+/// but this is too complex to verify here. Also it's just a logic error if users get this wrong.
+pub fn valid_ret_activity(mode: DiffMode, activity: DiffActivity) -> bool {
+    if activity == DiffActivity::None {
+        // Only valid if primal returns (), but we can't check that here.
+        return true;
+    }
+    match mode {
+        DiffMode::Error => false,
+        DiffMode::Source => false,
+        DiffMode::Forward | DiffMode::ForwardFirst => {
+            activity == DiffActivity::Dual
+                || activity == DiffActivity::DualOnly
+                || activity == DiffActivity::Const
+        }
+        DiffMode::Reverse | DiffMode::ReverseFirst => {
+            activity == DiffActivity::Const
+                || activity == DiffActivity::Active
+                || activity == DiffActivity::ActiveOnly
+        }
+    }
+}
+
+/// For indirections (ptr/ref) we can't use Active, since Active allocates a shadow value
+/// for the given argument, but we generally can't know the size of such a type.
+/// For scalar types (f16/f32/f64/f128) we can use Active and we can't use Duplicated,
+/// since Duplicated expects a mutable ref/ptr and we would thus end up with a shadow value
+/// who is an indirect type, which doesn't match the primal scalar type. We can't prevent
+/// users here from marking scalars as Duplicated, due to type aliases.
+pub fn valid_ty_for_activity(ty: &P<Ty>, activity: DiffActivity) -> bool {
+    use DiffActivity::*;
+    // It's always allowed to mark something as Const, since we won't compute derivatives wrt. it.
+    if matches!(activity, Const) {
+        return true;
+    }
+    if matches!(activity, Dual | DualOnly) {
+        return true;
+    }
+    // FIXME(ZuseZ4) We should make this more robust to also
+    // handle type aliases. Once that is done, we can be more restrictive here.
+    if matches!(activity, Active | ActiveOnly) {
+        return true;
+    }
+    matches!(ty.kind, TyKind::Ptr(_) | TyKind::Ref(..))
+        && matches!(activity, Duplicated | DuplicatedOnly)
+}
+pub fn valid_input_activity(mode: DiffMode, activity: DiffActivity) -> bool {
+    use DiffActivity::*;
+    return match mode {
+        DiffMode::Error => false,
+        DiffMode::Source => false,
+        DiffMode::Forward | DiffMode::ForwardFirst => {
+            matches!(activity, Dual | DualOnly | Const)
+        }
+        DiffMode::Reverse | DiffMode::ReverseFirst => {
+            matches!(activity, Active | ActiveOnly | Duplicated | DuplicatedOnly | Const)
+        }
+    };
+}
+
+impl Display for DiffActivity {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        match self {
+            DiffActivity::None => write!(f, "None"),
+            DiffActivity::Const => write!(f, "Const"),
+            DiffActivity::Active => write!(f, "Active"),
+            DiffActivity::ActiveOnly => write!(f, "ActiveOnly"),
+            DiffActivity::Dual => write!(f, "Dual"),
+            DiffActivity::DualOnly => write!(f, "DualOnly"),
+            DiffActivity::Duplicated => write!(f, "Duplicated"),
+            DiffActivity::DuplicatedOnly => write!(f, "DuplicatedOnly"),
+            DiffActivity::FakeActivitySize => write!(f, "FakeActivitySize"),
+        }
+    }
+}
+
+impl FromStr for DiffMode {
+    type Err = ();
+
+    fn from_str(s: &str) -> Result<DiffMode, ()> {
+        match s {
+            "Error" => Ok(DiffMode::Error),
+            "Source" => Ok(DiffMode::Source),
+            "Forward" => Ok(DiffMode::Forward),
+            "Reverse" => Ok(DiffMode::Reverse),
+            "ForwardFirst" => Ok(DiffMode::ForwardFirst),
+            "ReverseFirst" => Ok(DiffMode::ReverseFirst),
+            _ => Err(()),
+        }
+    }
+}
+impl FromStr for DiffActivity {
+    type Err = ();
+
+    fn from_str(s: &str) -> Result<DiffActivity, ()> {
+        match s {
+            "None" => Ok(DiffActivity::None),
+            "Active" => Ok(DiffActivity::Active),
+            "ActiveOnly" => Ok(DiffActivity::ActiveOnly),
+            "Const" => Ok(DiffActivity::Const),
+            "Dual" => Ok(DiffActivity::Dual),
+            "DualOnly" => Ok(DiffActivity::DualOnly),
+            "Duplicated" => Ok(DiffActivity::Duplicated),
+            "DuplicatedOnly" => Ok(DiffActivity::DuplicatedOnly),
+            _ => Err(()),
+        }
+    }
+}
+
+impl AutoDiffAttrs {
+    pub fn has_ret_activity(&self) -> bool {
+        self.ret_activity != DiffActivity::None
+    }
+    pub fn has_active_only_ret(&self) -> bool {
+        self.ret_activity == DiffActivity::ActiveOnly
+    }
+
+    pub fn error() -> Self {
+        AutoDiffAttrs {
+            mode: DiffMode::Error,
+            ret_activity: DiffActivity::None,
+            input_activity: Vec::new(),
+        }
+    }
+    pub fn source() -> Self {
+        AutoDiffAttrs {
+            mode: DiffMode::Source,
+            ret_activity: DiffActivity::None,
+            input_activity: Vec::new(),
+        }
+    }
+
+    pub fn is_active(&self) -> bool {
+        self.mode != DiffMode::Error
+    }
+
+    pub fn is_source(&self) -> bool {
+        self.mode == DiffMode::Source
+    }
+    pub fn apply_autodiff(&self) -> bool {
+        !matches!(self.mode, DiffMode::Error | DiffMode::Source)
+    }
+
+    pub fn into_item(
+        self,
+        source: String,
+        target: String,
+        inputs: Vec<TypeTree>,
+        output: TypeTree,
+    ) -> AutoDiffItem {
+        AutoDiffItem { source, target, inputs, output, attrs: self }
+    }
+}
+
+impl fmt::Display for AutoDiffItem {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "Differentiating {} -> {}", self.source, self.target)?;
+        write!(f, " with attributes: {:?}", self.attrs)?;
+        write!(f, " with inputs: {:?}", self.inputs)?;
+        write!(f, " with output: {:?}", self.output)
+    }
+}
diff --git a/compiler/rustc_ast/src/expand/mod.rs b/compiler/rustc_ast/src/expand/mod.rs
index 13413281bc7..d259677e98e 100644
--- a/compiler/rustc_ast/src/expand/mod.rs
+++ b/compiler/rustc_ast/src/expand/mod.rs
@@ -7,6 +7,8 @@ use rustc_span::symbol::Ident;
 use crate::MetaItem;
 
 pub mod allocator;
+pub mod autodiff_attrs;
+pub mod typetree;
 
 #[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)]
 pub struct StrippedCfgItem<ModId = DefId> {
diff --git a/compiler/rustc_ast/src/expand/typetree.rs b/compiler/rustc_ast/src/expand/typetree.rs
new file mode 100644
index 00000000000..9a2dd2e85e0
--- /dev/null
+++ b/compiler/rustc_ast/src/expand/typetree.rs
@@ -0,0 +1,90 @@
+//! This module contains the definition of the `TypeTree` and `Type` structs.
+//! They are thin Rust wrappers around the TypeTrees used by Enzyme as the LLVM based autodiff
+//! backend. The Enzyme TypeTrees currently have various limitations and should be rewritten, so the
+//! Rust frontend obviously has the same limitations. The main motivation of TypeTrees is to
+//! represent how a type looks like "in memory". Enzyme can deduce this based on usage patterns in
+//! the user code, but this is extremely slow and not even always sufficient. As such we lower some
+//! information from rustc to help Enzyme. For a full explanation of their design it is necessary to
+//! analyze the implementation in Enzyme core itself. As a rough summary, `-1` in Enzyme speech means
+//! everywhere. That is `{0:-1: Float}` means at index 0 you have a ptr, if you dereference it it
+//! will be floats everywhere. Thus `* f32`. If you have `{-1:int}` it means int's everywhere,
+//! e.g. [i32; N]. `{0:-1:-1 float}` then means one pointer at offset 0, if you dereference it there
+//! will be only pointers, if you dereference these new pointers they will point to array of floats.
+//! Generally, it allows byte-specific descriptions.
+//! FIXME: This description might be partly inaccurate and should be extended, along with
+//! adding documentation to the corresponding Enzyme core code.
+//! FIXME: Rewrite the TypeTree logic in Enzyme core to reduce the need for the rustc frontend to
+//! provide typetree information.
+//! FIXME: We should also re-evaluate where we create TypeTrees from Rust types, since MIR
+//! representations of some types might not be accurate. For example a vector of floats might be
+//! represented as a vector of u8s in MIR in some cases.
+
+use std::fmt;
+
+use crate::expand::{Decodable, Encodable, HashStable_Generic};
+
+#[derive(Clone, Copy, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+pub enum Kind {
+    Anything,
+    Integer,
+    Pointer,
+    Half,
+    Float,
+    Double,
+    Unknown,
+}
+
+#[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+pub struct TypeTree(pub Vec<Type>);
+
+impl TypeTree {
+    pub fn new() -> Self {
+        Self(Vec::new())
+    }
+    pub fn all_ints() -> Self {
+        Self(vec![Type { offset: -1, size: 1, kind: Kind::Integer, child: TypeTree::new() }])
+    }
+    pub fn int(size: usize) -> Self {
+        let mut ints = Vec::with_capacity(size);
+        for i in 0..size {
+            ints.push(Type {
+                offset: i as isize,
+                size: 1,
+                kind: Kind::Integer,
+                child: TypeTree::new(),
+            });
+        }
+        Self(ints)
+    }
+}
+
+#[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+pub struct FncTree {
+    pub args: Vec<TypeTree>,
+    pub ret: TypeTree,
+}
+
+#[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+pub struct Type {
+    pub offset: isize,
+    pub size: usize,
+    pub kind: Kind,
+    pub child: TypeTree,
+}
+
+impl Type {
+    pub fn add_offset(self, add: isize) -> Self {
+        let offset = match self.offset {
+            -1 => add,
+            x => add + x,
+        };
+
+        Self { size: self.size, kind: self.kind, child: self.child, offset }
+    }
+}
+
+impl fmt::Display for Type {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        <Self as fmt::Debug>::fmt(self, f)
+    }
+}
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 6ce23a1479d..2afbd979c30 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -485,7 +485,7 @@ pub fn walk_ty<T: MutVisitor>(vis: &mut T, ty: &mut P<Ty>) {
         }
         TyKind::Slice(ty) => vis.visit_ty(ty),
         TyKind::Ptr(mt) => vis.visit_mt(mt),
-        TyKind::Ref(lt, mt) => {
+        TyKind::Ref(lt, mt) | TyKind::PinnedRef(lt, mt) => {
             visit_opt(lt, |lt| vis.visit_lifetime(lt));
             vis.visit_mt(mt);
         }
@@ -519,10 +519,6 @@ pub fn walk_ty<T: MutVisitor>(vis: &mut T, ty: &mut P<Ty>) {
             visit_vec(bounds, |bound| vis.visit_param_bound(bound, BoundKind::Impl));
         }
         TyKind::MacCall(mac) => vis.visit_mac_call(mac),
-        TyKind::AnonStruct(id, fields) | TyKind::AnonUnion(id, fields) => {
-            vis.visit_id(id);
-            fields.flat_map_in_place(|field| vis.flat_map_field_def(field));
-        }
     }
     visit_lazy_tts(vis, tokens);
     vis.visit_span(span);
@@ -917,7 +913,7 @@ fn walk_fn_ret_ty<T: MutVisitor>(vis: &mut T, fn_ret_ty: &mut FnRetTy) {
 
 fn walk_param_bound<T: MutVisitor>(vis: &mut T, pb: &mut GenericBound) {
     match pb {
-        GenericBound::Trait(ty, _modifier) => vis.visit_poly_trait_ref(ty),
+        GenericBound::Trait(trait_ref) => vis.visit_poly_trait_ref(trait_ref),
         GenericBound::Outlives(lifetime) => walk_lifetime(vis, lifetime),
         GenericBound::Use(args, span) => {
             for arg in args {
@@ -1038,7 +1034,7 @@ fn walk_trait_ref<T: MutVisitor>(vis: &mut T, TraitRef { path, ref_id }: &mut Tr
 }
 
 fn walk_poly_trait_ref<T: MutVisitor>(vis: &mut T, p: &mut PolyTraitRef) {
-    let PolyTraitRef { bound_generic_params, trait_ref, span } = p;
+    let PolyTraitRef { bound_generic_params, modifiers: _, trait_ref, span } = p;
     bound_generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param));
     vis.visit_trait_ref(trait_ref);
     vis.visit_span(span);
diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs
index 1a80a9ccdbf..ae1ca36a3ba 100644
--- a/compiler/rustc_ast/src/util/classify.rs
+++ b/compiler/rustc_ast/src/util/classify.rs
@@ -247,7 +247,9 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
                 break (mac.args.delim == Delimiter::Brace).then_some(mac);
             }
 
-            ast::TyKind::Ptr(mut_ty) | ast::TyKind::Ref(_, mut_ty) => {
+            ast::TyKind::Ptr(mut_ty)
+            | ast::TyKind::Ref(_, mut_ty)
+            | ast::TyKind::PinnedRef(_, mut_ty) => {
                 ty = &mut_ty.ty;
             }
 
@@ -263,7 +265,7 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
 
             ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => {
                 match bounds.last() {
-                    Some(ast::GenericBound::Trait(bound, _)) => {
+                    Some(ast::GenericBound::Trait(bound)) => {
                         match path_return_type(&bound.trait_ref.path) {
                             Some(trailing_ty) => ty = trailing_ty,
                             None => break None,
@@ -287,12 +289,6 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
             | ast::TyKind::Pat(..)
             | ast::TyKind::Dummy
             | ast::TyKind::Err(..) => break None,
-
-            // These end in brace, but cannot occur in a let-else statement.
-            // They are only parsed as fields of a data structure. For the
-            // purpose of denying trailing braces in the expression of a
-            // let-else, we can disregard these.
-            ast::TyKind::AnonStruct(..) | ast::TyKind::AnonUnion(..) => break None,
         }
     }
 }
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 9f9c3d8c392..207ec710650 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -329,7 +329,7 @@ pub fn walk_poly_trait_ref<'a, V>(visitor: &mut V, trait_ref: &'a PolyTraitRef)
 where
     V: Visitor<'a>,
 {
-    let PolyTraitRef { bound_generic_params, trait_ref, span: _ } = trait_ref;
+    let PolyTraitRef { bound_generic_params, modifiers: _, trait_ref, span: _ } = trait_ref;
     walk_list!(visitor, visit_generic_param, bound_generic_params);
     visitor.visit_trait_ref(trait_ref)
 }
@@ -499,7 +499,8 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result {
     match kind {
         TyKind::Slice(ty) | TyKind::Paren(ty) => try_visit!(visitor.visit_ty(ty)),
         TyKind::Ptr(MutTy { ty, mutbl: _ }) => try_visit!(visitor.visit_ty(ty)),
-        TyKind::Ref(opt_lifetime, MutTy { ty, mutbl: _ }) => {
+        TyKind::Ref(opt_lifetime, MutTy { ty, mutbl: _ })
+        | TyKind::PinnedRef(opt_lifetime, MutTy { ty, mutbl: _ }) => {
             visit_opt!(visitor, visit_lifetime, opt_lifetime, LifetimeCtxt::Ref);
             try_visit!(visitor.visit_ty(ty));
         }
@@ -535,9 +536,6 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result {
         TyKind::Err(_guar) => {}
         TyKind::MacCall(mac) => try_visit!(visitor.visit_mac_call(mac)),
         TyKind::Never | TyKind::CVarArgs => {}
-        TyKind::AnonStruct(_id, ref fields) | TyKind::AnonUnion(_id, ref fields) => {
-            walk_list!(visitor, visit_field_def, fields);
-        }
     }
     V::Result::output()
 }
@@ -723,7 +721,7 @@ impl WalkItemKind for ForeignItemKind {
 
 pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericBound) -> V::Result {
     match bound {
-        GenericBound::Trait(typ, _modifier) => visitor.visit_poly_trait_ref(typ),
+        GenericBound::Trait(trait_ref) => visitor.visit_poly_trait_ref(trait_ref),
         GenericBound::Outlives(lifetime) => visitor.visit_lifetime(lifetime, LifetimeCtxt::Bound),
         GenericBound::Use(args, _span) => {
             walk_list!(visitor, visit_precise_capturing_arg, args);
diff --git a/compiler/rustc_ast_ir/src/lib.rs b/compiler/rustc_ast_ir/src/lib.rs
index eeed5d36151..ff9d940ce9f 100644
--- a/compiler/rustc_ast_ir/src/lib.rs
+++ b/compiler/rustc_ast_ir/src/lib.rs
@@ -79,3 +79,10 @@ impl Mutability {
         matches!(self, Self::Not)
     }
 }
+
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)]
+#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))]
+pub enum Pinnedness {
+    Not,
+    Pinned,
+}
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 52372bbf991..ae1e1b3f8a2 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -640,7 +640,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     self.lower_span(span),
                     Some(self.allow_gen_future.clone()),
                 );
-                let resume_ty = self.make_lang_item_qpath(hir::LangItem::ResumeTy, unstable_span);
+                let resume_ty =
+                    self.make_lang_item_qpath(hir::LangItem::ResumeTy, unstable_span, None);
                 let input_ty = hir::Ty {
                     hir_id: self.next_id(),
                     kind: hir::TyKind::Path(resume_ty),
@@ -2065,7 +2066,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         lang_item: hir::LangItem,
         name: Symbol,
     ) -> hir::Expr<'hir> {
-        let qpath = self.make_lang_item_qpath(lang_item, self.lower_span(span));
+        let qpath = self.make_lang_item_qpath(lang_item, self.lower_span(span), None);
         let path = hir::ExprKind::Path(hir::QPath::TypeRelative(
             self.arena.alloc(self.ty(span, hir::TyKind::Path(qpath))),
             self.arena.alloc(hir::PathSegment::new(
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 1273b50dff8..ce744cc56e1 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -1504,8 +1504,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
             for bound in &bound_pred.bounds {
                 if !matches!(
                     *bound,
-                    GenericBound::Trait(_, TraitBoundModifiers {
-                        polarity: BoundPolarity::Maybe(_),
+                    GenericBound::Trait(PolyTraitRef {
+                        modifiers: TraitBoundModifiers { polarity: BoundPolarity::Maybe(_), .. },
                         ..
                     })
                 ) {
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index b26797f4203..00eafeb4d84 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -55,8 +55,8 @@ use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey};
 use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
 use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalDefIdMap};
 use rustc_hir::{
-    self as hir, ConstArg, GenericArg, HirId, ItemLocalMap, MissingLifetimeKind, ParamName,
-    TraitCandidate,
+    self as hir, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem, MissingLifetimeKind,
+    ParamName, TraitCandidate,
 };
 use rustc_index::{Idx, IndexSlice, IndexVec};
 use rustc_macros::extension;
@@ -765,8 +765,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         res
     }
 
-    fn make_lang_item_qpath(&mut self, lang_item: hir::LangItem, span: Span) -> hir::QPath<'hir> {
-        hir::QPath::Resolved(None, self.make_lang_item_path(lang_item, span, None))
+    fn make_lang_item_qpath(
+        &mut self,
+        lang_item: hir::LangItem,
+        span: Span,
+        args: Option<&'hir hir::GenericArgs<'hir>>,
+    ) -> hir::QPath<'hir> {
+        hir::QPath::Resolved(None, self.make_lang_item_path(lang_item, span, args))
     }
 
     fn make_lang_item_path(
@@ -1219,13 +1224,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 let bound = this.lower_poly_trait_ref(
                     &PolyTraitRef {
                         bound_generic_params: ThinVec::new(),
+                        modifiers: TraitBoundModifiers::NONE,
                         trait_ref: TraitRef { path: path.clone(), ref_id: t.id },
                         span: t.span,
                     },
                     itctx,
-                    TraitBoundModifiers::NONE,
                 );
-                let bound = (bound, hir::TraitBoundModifier::None);
                 let bounds = this.arena.alloc_from_iter([bound]);
                 let lifetime_bound = this.elided_dyn_bound(t.span);
                 (bounds, lifetime_bound)
@@ -1259,46 +1263,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let kind = match &t.kind {
             TyKind::Infer => hir::TyKind::Infer,
             TyKind::Err(guar) => hir::TyKind::Err(*guar),
-            // Lower the anonymous structs or unions in a nested lowering context.
-            //
-            // ```
-            // struct Foo {
-            //     _: union {
-            // //     ^__________________  <-- within the nested lowering context,
-            //         /* fields */ //   |     we lower all fields defined into an
-            //     } //                  |     owner node of struct or union item
-            // //  ^_____________________|
-            // }
-            // ```
-            TyKind::AnonStruct(node_id, fields) | TyKind::AnonUnion(node_id, fields) => {
-                // Here its `def_id` is created in `build_reduced_graph`.
-                let def_id = self.local_def_id(*node_id);
-                debug!(?def_id);
-                let owner_id = hir::OwnerId { def_id };
-                self.with_hir_id_owner(*node_id, |this| {
-                    let fields = this.arena.alloc_from_iter(
-                        fields.iter().enumerate().map(|f| this.lower_field_def(f)),
-                    );
-                    let span = t.span;
-                    let variant_data =
-                        hir::VariantData::Struct { fields, recovered: ast::Recovered::No };
-                    // FIXME: capture the generics from the outer adt.
-                    let generics = hir::Generics::empty();
-                    let kind = match t.kind {
-                        TyKind::AnonStruct(..) => hir::ItemKind::Struct(variant_data, generics),
-                        TyKind::AnonUnion(..) => hir::ItemKind::Union(variant_data, generics),
-                        _ => unreachable!(),
-                    };
-                    hir::OwnerNode::Item(this.arena.alloc(hir::Item {
-                        ident: Ident::new(kw::Empty, span),
-                        owner_id,
-                        kind,
-                        span: this.lower_span(span),
-                        vis_span: this.lower_span(span.shrink_to_lo()),
-                    }))
-                });
-                hir::TyKind::AnonAdt(hir::ItemId { owner_id })
-            }
             TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)),
             TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)),
             TyKind::Ref(region, mt) => {
@@ -1317,6 +1281,32 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 let lifetime = self.lower_lifetime(&region);
                 hir::TyKind::Ref(lifetime, self.lower_mt(mt, itctx))
             }
+            TyKind::PinnedRef(region, mt) => {
+                let region = region.unwrap_or_else(|| {
+                    let id = if let Some(LifetimeRes::ElidedAnchor { start, end }) =
+                        self.resolver.get_lifetime_res(t.id)
+                    {
+                        debug_assert_eq!(start.plus(1), end);
+                        start
+                    } else {
+                        self.next_node_id()
+                    };
+                    let span = self.tcx.sess.source_map().start_point(t.span).shrink_to_hi();
+                    Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id }
+                });
+                let lifetime = self.lower_lifetime(&region);
+                let kind = hir::TyKind::Ref(lifetime, self.lower_mt(mt, itctx));
+                let span = self.lower_span(t.span);
+                let arg = hir::Ty { kind, span, hir_id: self.next_id() };
+                let args = self.arena.alloc(hir::GenericArgs {
+                    args: self.arena.alloc([hir::GenericArg::Type(self.arena.alloc(arg))]),
+                    constraints: &[],
+                    parenthesized: hir::GenericArgsParentheses::No,
+                    span_ext: span,
+                });
+                let path = self.make_lang_item_qpath(LangItem::Pin, span, Some(args));
+                hir::TyKind::Path(path)
+            }
             TyKind::BareFn(f) => {
                 let generic_params = self.lower_lifetime_binder(t.id, &f.generic_params);
                 hir::TyKind::BareFn(self.arena.alloc(hir::BareFnTy {
@@ -1366,16 +1356,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                             // We can safely ignore constness here since AST validation
                             // takes care of rejecting invalid modifier combinations and
                             // const trait bounds in trait object types.
-                            GenericBound::Trait(ty, modifiers) => {
-                                // Still, don't pass along the constness here; we don't want to
-                                // synthesize any host effect args, it'd only cause problems.
-                                let modifiers = TraitBoundModifiers {
-                                    constness: BoundConstness::Never,
-                                    ..*modifiers
-                                };
-                                let trait_ref = this.lower_poly_trait_ref(ty, itctx, modifiers);
-                                let polarity = this.lower_trait_bound_modifiers(modifiers);
-                                Some((trait_ref, polarity))
+                            GenericBound::Trait(ty) => {
+                                let trait_ref = this.lower_poly_trait_ref(ty, itctx);
+                                Some(trait_ref)
                             }
                             GenericBound::Outlives(lifetime) => {
                                 if lifetime_bound.is_none() {
@@ -1573,11 +1556,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         // Feature gate for RPITIT + use<..>
         match origin {
             rustc_hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl: Some(_), .. } => {
-                if let Some(span) = bounds.iter().find_map(|bound| match *bound {
-                    ast::GenericBound::Use(_, span) => Some(span),
-                    _ => None,
-                }) {
-                    self.tcx.dcx().emit_err(errors::NoPreciseCapturesOnRpitit { span });
+                if !self.tcx.features().precise_capturing_in_traits
+                    && let Some(span) = bounds.iter().find_map(|bound| match *bound {
+                        ast::GenericBound::Use(_, span) => Some(span),
+                        _ => None,
+                    })
+                {
+                    let mut diag =
+                        self.tcx.dcx().create_err(errors::NoPreciseCapturesOnRpitit { span });
+                    add_feature_diagnostics(
+                        &mut diag,
+                        self.tcx.sess,
+                        sym::precise_capturing_in_traits,
+                    );
+                    diag.emit();
                 }
             }
             _ => {}
@@ -1882,10 +1874,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     // Given we are only considering `ImplicitSelf` types, we needn't consider
                     // the case where we have a mutable pattern to a reference as that would
                     // no longer be an `ImplicitSelf`.
-                    TyKind::Ref(_, mt) if mt.ty.kind.is_implicit_self() => match mt.mutbl {
-                        hir::Mutability::Not => hir::ImplicitSelfKind::RefImm,
-                        hir::Mutability::Mut => hir::ImplicitSelfKind::RefMut,
-                    },
+                    TyKind::Ref(_, mt) | TyKind::PinnedRef(_, mt)
+                        if mt.ty.kind.is_implicit_self() =>
+                    {
+                        match mt.mutbl {
+                            hir::Mutability::Not => hir::ImplicitSelfKind::RefImm,
+                            hir::Mutability::Mut => hir::ImplicitSelfKind::RefMut,
+                        }
+                    }
                     _ => hir::ImplicitSelfKind::None,
                 }
             }),
@@ -1995,21 +1991,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             span_ext: DUMMY_SP,
         });
 
-        hir::GenericBound::Trait(
-            hir::PolyTraitRef {
-                bound_generic_params: &[],
-                trait_ref: hir::TraitRef {
-                    path: self.make_lang_item_path(
-                        trait_lang_item,
-                        opaque_ty_span,
-                        Some(bound_args),
-                    ),
-                    hir_ref_id: self.next_id(),
-                },
-                span: opaque_ty_span,
+        hir::GenericBound::Trait(hir::PolyTraitRef {
+            bound_generic_params: &[],
+            modifiers: hir::TraitBoundModifier::None,
+            trait_ref: hir::TraitRef {
+                path: self.make_lang_item_path(trait_lang_item, opaque_ty_span, Some(bound_args)),
+                hir_ref_id: self.next_id(),
             },
-            hir::TraitBoundModifier::None,
-        )
+            span: opaque_ty_span,
+        })
     }
 
     #[instrument(level = "trace", skip(self))]
@@ -2019,10 +2009,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         itctx: ImplTraitContext,
     ) -> hir::GenericBound<'hir> {
         match tpb {
-            GenericBound::Trait(p, modifiers) => hir::GenericBound::Trait(
-                self.lower_poly_trait_ref(p, itctx, *modifiers),
-                self.lower_trait_bound_modifiers(*modifiers),
-            ),
+            GenericBound::Trait(p) => hir::GenericBound::Trait(self.lower_poly_trait_ref(p, itctx)),
             GenericBound::Outlives(lifetime) => {
                 hir::GenericBound::Outlives(self.lower_lifetime(lifetime))
             }
@@ -2226,12 +2213,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         &mut self,
         p: &PolyTraitRef,
         itctx: ImplTraitContext,
-        modifiers: ast::TraitBoundModifiers,
     ) -> hir::PolyTraitRef<'hir> {
         let bound_generic_params =
             self.lower_lifetime_binder(p.trait_ref.ref_id, &p.bound_generic_params);
-        let trait_ref = self.lower_trait_ref(modifiers, &p.trait_ref, itctx);
-        hir::PolyTraitRef { bound_generic_params, trait_ref, span: self.lower_span(p.span) }
+        let trait_ref = self.lower_trait_ref(p.modifiers, &p.trait_ref, itctx);
+        let modifiers = self.lower_trait_bound_modifiers(p.modifiers);
+        hir::PolyTraitRef {
+            bound_generic_params,
+            modifiers,
+            trait_ref,
+            span: self.lower_span(p.span),
+        }
     }
 
     fn lower_mt(&mut self, mt: &MutTy, itctx: ImplTraitContext) -> hir::MutTy<'hir> {
@@ -2671,10 +2663,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     Res::Def(DefKind::Trait | DefKind::TraitAlias, _) => {
                         let principal = hir::PolyTraitRef {
                             bound_generic_params: &[],
+                            modifiers: hir::TraitBoundModifier::None,
                             trait_ref: hir::TraitRef { path, hir_ref_id: hir_id },
                             span: self.lower_span(span),
                         };
-                        let principal = (principal, hir::TraitBoundModifier::None);
 
                         // The original ID is taken by the `PolyTraitRef`,
                         // so the `Ty` itself needs a different one.
diff --git a/compiler/rustc_ast_lowering/src/lifetime_collector.rs b/compiler/rustc_ast_lowering/src/lifetime_collector.rs
index 1e82ba5db8a..fe64160fb4d 100644
--- a/compiler/rustc_ast_lowering/src/lifetime_collector.rs
+++ b/compiler/rustc_ast_lowering/src/lifetime_collector.rs
@@ -95,7 +95,7 @@ impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> {
                 visit::walk_ty(self, t);
                 self.current_binders.pop();
             }
-            TyKind::Ref(None, _) => {
+            TyKind::Ref(None, _) | TyKind::PinnedRef(None, _) => {
                 self.record_elided_anchor(t.id, t.span);
                 visit::walk_ty(self, t);
             }
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index c361c35b723..92acaaa5f36 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -1,7 +1,3 @@
-ast_passes_anon_struct_or_union_not_allowed =
-    anonymous {$struct_or_union}s are not allowed outside of unnamed struct or union fields
-    .label = anonymous {$struct_or_union} declared here
-
 ast_passes_assoc_const_without_body =
     associated constant in `impl` without body
     .suggestion = provide a definition for the constant
@@ -66,12 +62,12 @@ ast_passes_equality_in_where = equality constraints are not yet supported in `wh
 
 ast_passes_extern_block_suggestion = if you meant to declare an externally defined function, use an `extern` block
 
-ast_passes_extern_fn_qualifiers = functions in `extern` blocks cannot have qualifiers
+ast_passes_extern_fn_qualifiers = functions in `extern` blocks cannot have `{$kw}` qualifier
     .label = in this `extern` block
-    .suggestion = remove this qualifier
+    .suggestion = remove the `{$kw}` qualifier
 
-ast_passes_extern_invalid_safety = items in unadorned `extern` blocks cannot have safety qualifiers
-    .suggestion = add unsafe to this `extern` block
+ast_passes_extern_invalid_safety = items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers
+    .suggestion = add `unsafe` to this `extern` block
 
 ast_passes_extern_item_ascii = items in `extern` blocks cannot use non-ascii identifiers
     .label = in this `extern` block
@@ -160,14 +156,6 @@ ast_passes_inherent_cannot_be = inherent impls cannot be {$annotation}
     .type = inherent impl for this type
     .only_trait = only trait implementations may be annotated with {$annotation}
 
-ast_passes_invalid_unnamed_field =
-    unnamed fields are not allowed outside of structs or unions
-    .label = unnamed field declared here
-
-ast_passes_invalid_unnamed_field_ty =
-    unnamed fields can only have struct or union types
-    .label = not a struct or union
-
 ast_passes_item_invalid_safety = items outside of `unsafe extern {"{ }"}` cannot be declared with `safe` safety qualifier
     .suggestion = remove safe from this item
 
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 5bdd9b6eda8..20a4f2120dc 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -244,9 +244,6 @@ impl<'a> AstValidator<'a> {
                     }
                 }
             }
-            TyKind::AnonStruct(_, ref fields) | TyKind::AnonUnion(_, ref fields) => {
-                walk_list!(self, visit_struct_field_def, fields)
-            }
             _ => visit::walk_ty(self, t),
         }
     }
@@ -255,7 +252,6 @@ impl<'a> AstValidator<'a> {
         if let Some(ident) = field.ident
             && ident.name == kw::Underscore
         {
-            self.check_unnamed_field_ty(&field.ty, ident.span);
             self.visit_vis(&field.vis);
             self.visit_ident(ident);
             self.visit_ty_common(&field.ty);
@@ -294,39 +290,6 @@ impl<'a> AstValidator<'a> {
         }
     }
 
-    fn check_unnamed_field_ty(&self, ty: &Ty, span: Span) {
-        if matches!(
-            &ty.kind,
-            // We already checked for `kw::Underscore` before calling this function,
-            // so skip the check
-            TyKind::AnonStruct(..) | TyKind::AnonUnion(..)
-            // If the anonymous field contains a Path as type, we can't determine
-            // if the path is a valid struct or union, so skip the check
-            | TyKind::Path(..)
-        ) {
-            return;
-        }
-        self.dcx().emit_err(errors::InvalidUnnamedFieldTy { span, ty_span: ty.span });
-    }
-
-    fn deny_anon_struct_or_union(&self, ty: &Ty) {
-        let struct_or_union = match &ty.kind {
-            TyKind::AnonStruct(..) => "struct",
-            TyKind::AnonUnion(..) => "union",
-            _ => return,
-        };
-        self.dcx().emit_err(errors::AnonStructOrUnionNotAllowed { struct_or_union, span: ty.span });
-    }
-
-    fn deny_unnamed_field(&self, field: &FieldDef) {
-        if let Some(ident) = field.ident
-            && ident.name == kw::Underscore
-        {
-            self.dcx()
-                .emit_err(errors::InvalidUnnamedField { span: field.span, ident_span: ident.span });
-        }
-    }
-
     fn check_trait_fn_not_const(&self, constness: Const, parent: &TraitOrTraitImpl) {
         let Const::Yes(span) = constness else {
             return;
@@ -561,21 +524,24 @@ impl<'a> AstValidator<'a> {
         // Deconstruct to ensure exhaustiveness
         FnHeader { safety: _, coroutine_kind, constness, ext }: FnHeader,
     ) {
-        let report_err = |span| {
-            self.dcx()
-                .emit_err(errors::FnQualifierInExtern { span, block: self.current_extern_span() });
+        let report_err = |span, kw| {
+            self.dcx().emit_err(errors::FnQualifierInExtern {
+                span,
+                kw,
+                block: self.current_extern_span(),
+            });
         };
         match coroutine_kind {
-            Some(knd) => report_err(knd.span()),
+            Some(kind) => report_err(kind.span(), kind.as_str()),
             None => (),
         }
         match constness {
-            Const::Yes(span) => report_err(span),
+            Const::Yes(span) => report_err(span, "const"),
             Const::No => (),
         }
         match ext {
             Extern::None => (),
-            Extern::Implicit(span) | Extern::Explicit(_, span) => report_err(span),
+            Extern::Implicit(span) | Extern::Explicit(_, span) => report_err(span, "extern"),
         }
     }
 
@@ -890,15 +856,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
 
     fn visit_ty(&mut self, ty: &'a Ty) {
         self.visit_ty_common(ty);
-        self.deny_anon_struct_or_union(ty);
         self.walk_ty(ty)
     }
 
-    fn visit_field_def(&mut self, field: &'a FieldDef) {
-        self.deny_unnamed_field(field);
-        visit::walk_field_def(self, field)
-    }
-
     fn visit_item(&mut self, item: &'a Item) {
         if item.attrs.iter().any(|attr| attr.is_proc_macro_attr()) {
             self.has_proc_macro_decls = true;
@@ -1303,7 +1263,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     if !bound_pred.bound_generic_params.is_empty() {
                         for bound in &bound_pred.bounds {
                             match bound {
-                                GenericBound::Trait(t, _) => {
+                                GenericBound::Trait(t) => {
                                     if !t.bound_generic_params.is_empty() {
                                         self.dcx()
                                             .emit_err(errors::NestedLifetimes { span: t.span });
@@ -1323,8 +1283,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
 
     fn visit_param_bound(&mut self, bound: &'a GenericBound, ctxt: BoundKind) {
         match bound {
-            GenericBound::Trait(trait_ref, modifiers) => {
-                match (ctxt, modifiers.constness, modifiers.polarity) {
+            GenericBound::Trait(trait_ref) => {
+                match (ctxt, trait_ref.modifiers.constness, trait_ref.modifiers.polarity) {
                     (BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_))
                         if !self.features.more_maybe_bounds =>
                     {
@@ -1364,7 +1324,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 }
 
                 // Negative trait bounds are not allowed to have associated constraints
-                if let BoundPolarity::Negative(_) = modifiers.polarity
+                if let BoundPolarity::Negative(_) = trait_ref.modifiers.polarity
                     && let Some(segment) = trait_ref.trait_ref.path.segments.last()
                 {
                     match segment.args.as_deref() {
@@ -1712,7 +1672,9 @@ fn deny_equality_constraints(
             }),
         ) {
             for bound in bounds {
-                if let GenericBound::Trait(poly, TraitBoundModifiers::NONE) = bound {
+                if let GenericBound::Trait(poly) = bound
+                    && poly.modifiers == TraitBoundModifiers::NONE
+                {
                     if full_path.segments[..full_path.segments.len() - 1]
                         .iter()
                         .map(|segment| segment.ident.name)
@@ -1740,7 +1702,9 @@ fn deny_equality_constraints(
             ) {
                 if ident == potential_param.ident {
                     for bound in bounds {
-                        if let ast::GenericBound::Trait(poly, TraitBoundModifiers::NONE) = bound {
+                        if let ast::GenericBound::Trait(poly) = bound
+                            && poly.modifiers == TraitBoundModifiers::NONE
+                        {
                             suggest(poly, potential_assoc, predicate);
                         }
                     }
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 5cce47ce7bd..4ca1acde1e2 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -295,6 +295,7 @@ pub(crate) struct FnQualifierInExtern {
     pub span: Span,
     #[label]
     pub block: Span,
+    pub kw: &'static str,
 }
 
 #[derive(Diagnostic)]
@@ -815,33 +816,6 @@ pub(crate) struct NegativeBoundWithParentheticalNotation {
 }
 
 #[derive(Diagnostic)]
-#[diag(ast_passes_invalid_unnamed_field_ty)]
-pub(crate) struct InvalidUnnamedFieldTy {
-    #[primary_span]
-    pub span: Span,
-    #[label]
-    pub ty_span: Span,
-}
-
-#[derive(Diagnostic)]
-#[diag(ast_passes_invalid_unnamed_field)]
-pub(crate) struct InvalidUnnamedField {
-    #[primary_span]
-    pub span: Span,
-    #[label]
-    pub ident_span: Span,
-}
-
-#[derive(Diagnostic)]
-#[diag(ast_passes_anon_struct_or_union_not_allowed)]
-pub(crate) struct AnonStructOrUnionNotAllowed {
-    #[primary_span]
-    #[label]
-    pub span: Span,
-    pub struct_or_union: &'static str,
-}
-
-#[derive(Diagnostic)]
 #[diag(ast_passes_match_arm_with_no_body)]
 pub(crate) struct MatchArmWithNoBody {
     #[primary_span]
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 42d1ef5c558..f2773dcdc60 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -541,12 +541,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
     gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
     gate_all!(explicit_tail_calls, "`become` expression is experimental");
     gate_all!(generic_const_items, "generic const items are experimental");
-    gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented");
     gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
     gate_all!(postfix_match, "postfix match is experimental");
     gate_all!(mut_ref, "mutable by-reference bindings are experimental");
     gate_all!(global_registration, "global registration is experimental");
     gate_all!(return_type_notation, "return type notation is experimental");
+    gate_all!(pin_ergonomics, "pinned reference syntax is experimental");
 
     if !visitor.features.never_patterns {
         if let Some(spans) = spans.get(&sym::never_patterns) {
@@ -666,7 +666,7 @@ fn check_incompatible_features(sess: &Session, features: &Features) {
 }
 
 fn check_new_solver_banned_features(sess: &Session, features: &Features) {
-    if !sess.opts.unstable_opts.next_solver.is_some_and(|n| n.globally) {
+    if !sess.opts.unstable_opts.next_solver.globally {
         return;
     }
 
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 884cc413bd5..2cdec2138ad 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -8,7 +8,6 @@ mod item;
 
 use std::borrow::Cow;
 
-use ast::TraitBoundModifiers;
 use rustc_ast::attr::AttrIdGenerator;
 use rustc_ast::ptr::P;
 use rustc_ast::token::{
@@ -1163,6 +1162,12 @@ impl<'a> State<'a> {
                 self.print_opt_lifetime(lifetime);
                 self.print_mt(mt, false);
             }
+            ast::TyKind::PinnedRef(lifetime, mt) => {
+                self.word("&");
+                self.print_opt_lifetime(lifetime);
+                self.word("pin ");
+                self.print_mt(mt, true);
+            }
             ast::TyKind::Never => {
                 self.word("!");
             }
@@ -1174,14 +1179,6 @@ impl<'a> State<'a> {
                 }
                 self.pclose();
             }
-            ast::TyKind::AnonStruct(_, fields) => {
-                self.head("struct");
-                self.print_record_struct_body(fields, ty.span);
-            }
-            ast::TyKind::AnonUnion(_, fields) => {
-                self.head("union");
-                self.print_record_struct_body(fields, ty.span);
-            }
             ast::TyKind::Paren(typ) => {
                 self.popen();
                 self.print_type(typ);
@@ -1261,6 +1258,27 @@ impl<'a> State<'a> {
 
     fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) {
         self.print_formal_generic_params(&t.bound_generic_params);
+
+        let ast::TraitBoundModifiers { constness, asyncness, polarity } = t.modifiers;
+        match constness {
+            ast::BoundConstness::Never => {}
+            ast::BoundConstness::Always(_) | ast::BoundConstness::Maybe(_) => {
+                self.word_space(constness.as_str());
+            }
+        }
+        match asyncness {
+            ast::BoundAsyncness::Normal => {}
+            ast::BoundAsyncness::Async(_) => {
+                self.word_space(asyncness.as_str());
+            }
+        }
+        match polarity {
+            ast::BoundPolarity::Positive => {}
+            ast::BoundPolarity::Negative(_) | ast::BoundPolarity::Maybe(_) => {
+                self.word(polarity.as_str());
+            }
+        }
+
         self.print_trait_ref(&t.trait_ref)
     }
 
@@ -1748,31 +1766,7 @@ impl<'a> State<'a> {
             }
 
             match bound {
-                GenericBound::Trait(
-                    tref,
-                    TraitBoundModifiers { constness, asyncness, polarity },
-                ) => {
-                    match constness {
-                        ast::BoundConstness::Never => {}
-                        ast::BoundConstness::Always(_) | ast::BoundConstness::Maybe(_) => {
-                            self.word_space(constness.as_str());
-                        }
-                    }
-
-                    match asyncness {
-                        ast::BoundAsyncness::Normal => {}
-                        ast::BoundAsyncness::Async(_) => {
-                            self.word_space(asyncness.as_str());
-                        }
-                    }
-
-                    match polarity {
-                        ast::BoundPolarity::Positive => {}
-                        ast::BoundPolarity::Negative(_) | ast::BoundPolarity::Maybe(_) => {
-                            self.word(polarity.as_str());
-                        }
-                    }
-
+                GenericBound::Trait(tref) => {
                     self.print_poly_trait_ref(tref);
                 }
                 GenericBound::Outlives(lt) => self.print_lifetime(*lt),
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index c1db4d07dfc..bbb17497684 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -723,7 +723,13 @@ pub fn eval_condition(
                     }
 
                     mis.iter().fold(true, |res, mi| {
-                        let mut mi = mi.meta_item().unwrap().clone();
+                        let Some(mut mi) = mi.meta_item().cloned() else {
+                            dcx.emit_err(session_diagnostics::CfgPredicateIdentifier {
+                                span: mi.span(),
+                            });
+                            return false;
+                        };
+
                         if let [seg, ..] = &mut mi.path.segments[..] {
                             seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name));
                         }
diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs
index 5ea48d3fc7e..89ff12c1479 100644
--- a/compiler/rustc_borrowck/src/dataflow.rs
+++ b/compiler/rustc_borrowck/src/dataflow.rs
@@ -1,13 +1,11 @@
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::graph;
 use rustc_index::bit_set::BitSet;
-use rustc_middle::mir::{
-    self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges,
-};
+use rustc_middle::mir::{self, BasicBlock, Body, Location, Place, TerminatorEdges};
 use rustc_middle::ty::{RegionVid, TyCtxt};
 use rustc_mir_dataflow::fmt::DebugWithContext;
 use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces};
-use rustc_mir_dataflow::{Analysis, AnalysisDomain, Forward, GenKill, Results, ResultsVisitable};
+use rustc_mir_dataflow::{Analysis, Forward, GenKill, Results, ResultsVisitable};
 use tracing::debug;
 
 use crate::{BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, places_conflict};
@@ -22,9 +20,9 @@ pub(crate) struct BorrowckResults<'a, 'tcx> {
 /// The transient state of the dataflow analyses used by the borrow checker.
 #[derive(Debug)]
 pub(crate) struct BorrowckDomain<'a, 'tcx> {
-    pub(crate) borrows: <Borrows<'a, 'tcx> as AnalysisDomain<'tcx>>::Domain,
-    pub(crate) uninits: <MaybeUninitializedPlaces<'a, 'tcx> as AnalysisDomain<'tcx>>::Domain,
-    pub(crate) ever_inits: <EverInitializedPlaces<'a, 'tcx> as AnalysisDomain<'tcx>>::Domain,
+    pub(crate) borrows: <Borrows<'a, 'tcx> as Analysis<'tcx>>::Domain,
+    pub(crate) uninits: <MaybeUninitializedPlaces<'a, 'tcx> as Analysis<'tcx>>::Domain,
+    pub(crate) ever_inits: <EverInitializedPlaces<'a, 'tcx> as Analysis<'tcx>>::Domain,
 }
 
 impl<'a, 'tcx> ResultsVisitable<'tcx> for BorrowckResults<'a, 'tcx> {
@@ -427,7 +425,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
     /// That means they went out of a nonlexical scope
     fn kill_loans_out_of_scope_at_location(
         &self,
-        trans: &mut impl GenKill<BorrowIndex>,
+        trans: &mut <Self as Analysis<'tcx>>::Domain,
         location: Location,
     ) {
         // NOTE: The state associated with a given `location`
@@ -447,7 +445,11 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
     }
 
     /// Kill any borrows that conflict with `place`.
-    fn kill_borrows_on_place(&self, trans: &mut impl GenKill<BorrowIndex>, place: Place<'tcx>) {
+    fn kill_borrows_on_place(
+        &self,
+        trans: &mut <Self as Analysis<'tcx>>::Domain,
+        place: Place<'tcx>,
+    ) {
         debug!("kill_borrows_on_place: place={:?}", place);
 
         let other_borrows_of_local = self
@@ -486,7 +488,14 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
     }
 }
 
-impl<'tcx> rustc_mir_dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
+/// Forward dataflow computation of the set of borrows that are in scope at a particular location.
+/// - we gen the introduced loans
+/// - we kill loans on locals going out of (regular) scope
+/// - we kill the loans going out of their region's NLL scope: in NLL terms, the frontier where a
+///   region stops containing the CFG points reachable from the issuing location.
+/// - we also kill loans of conflicting places when overwriting a shared path: e.g. borrows of
+///   `a.b.c` when `a` is overwritten.
+impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
     type Domain = BitSet<BorrowIndex>;
 
     const NAME: &'static str = "borrows";
@@ -500,34 +509,19 @@ impl<'tcx> rustc_mir_dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
         // no borrows of code region_scopes have been taken prior to
         // function execution, so this method has no effect.
     }
-}
-
-/// Forward dataflow computation of the set of borrows that are in scope at a particular location.
-/// - we gen the introduced loans
-/// - we kill loans on locals going out of (regular) scope
-/// - we kill the loans going out of their region's NLL scope: in NLL terms, the frontier where a
-///   region stops containing the CFG points reachable from the issuing location.
-/// - we also kill loans of conflicting places when overwriting a shared path: e.g. borrows of
-///   `a.b.c` when `a` is overwritten.
-impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
-    type Idx = BorrowIndex;
-
-    fn domain_size(&self, _: &mir::Body<'tcx>) -> usize {
-        self.borrow_set.len()
-    }
 
-    fn before_statement_effect(
+    fn apply_before_statement_effect(
         &mut self,
-        trans: &mut impl GenKill<Self::Idx>,
+        trans: &mut Self::Domain,
         _statement: &mir::Statement<'tcx>,
         location: Location,
     ) {
         self.kill_loans_out_of_scope_at_location(trans, location);
     }
 
-    fn statement_effect(
+    fn apply_statement_effect(
         &mut self,
-        trans: &mut impl GenKill<Self::Idx>,
+        trans: &mut Self::Domain,
         stmt: &mir::Statement<'tcx>,
         location: Location,
     ) {
@@ -573,7 +567,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
         }
     }
 
-    fn before_terminator_effect(
+    fn apply_before_terminator_effect(
         &mut self,
         trans: &mut Self::Domain,
         _terminator: &mir::Terminator<'tcx>,
@@ -582,7 +576,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
         self.kill_loans_out_of_scope_at_location(trans, location);
     }
 
-    fn terminator_effect<'mir>(
+    fn apply_terminator_effect<'mir>(
         &mut self,
         trans: &mut Self::Domain,
         terminator: &'mir mir::Terminator<'tcx>,
@@ -599,14 +593,6 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
         }
         terminator.edges()
     }
-
-    fn call_return_effect(
-        &mut self,
-        _trans: &mut Self::Domain,
-        _block: mir::BasicBlock,
-        _return_places: CallReturnPlaces<'_, 'tcx>,
-    ) {
-    }
 }
 
 impl<C> DebugWithContext<C> for BorrowIndex {}
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index 98417e8c7a3..3871816777c 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -706,7 +706,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 suggestions.push((
                     pat_span,
                     format!("consider removing the {to_remove}"),
-                    suggestion.to_string(),
+                    suggestion,
                 ));
             }
         }
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 39175b406a4..6333d59a1bc 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -254,7 +254,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         debug!(?hrtb_bounds);
 
         hrtb_bounds.iter().for_each(|bound| {
-            let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }, _) = bound else {
+            let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }) = bound else {
                 return;
             };
             diag.span_note(*trait_span, fluent::borrowck_limitations_implies_static);
@@ -277,7 +277,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     return;
                 };
                 bounds.iter().for_each(|bd| {
-                    if let Trait(PolyTraitRef { trait_ref: tr_ref, .. }, _) = bd
+                    if let Trait(PolyTraitRef { trait_ref: tr_ref, .. }) = bd
                         && let Def(_, res_defid) = tr_ref.path.res
                         && res_defid == trait_res_defid // trait id matches
                         && let TyKind::Path(Resolved(_, path)) = bounded_ty.kind
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index 1a5f9bdb154..b4b8373ac97 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -837,7 +837,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
                 hir_ty
             );
         };
-        if let hir::OpaqueTy { bounds: [hir::GenericBound::Trait(trait_ref, _)], .. } = opaque_ty
+        if let hir::OpaqueTy { bounds: [hir::GenericBound::Trait(trait_ref)], .. } = opaque_ty
             && let Some(segment) = trait_ref.trait_ref.path.segments.last()
             && let Some(args) = segment.args
             && let [constraint] = args.constraints
diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs
index f3207c26bfc..3674053409b 100644
--- a/compiler/rustc_borrowck/src/nll.rs
+++ b/compiler/rustc_borrowck/src/nll.rs
@@ -165,7 +165,7 @@ pub(crate) fn compute_regions<'a, 'tcx>(
         universe_causes,
         type_tests,
         liveness_constraints,
-        elements.clone(),
+        elements,
     );
 
     // If requested: dump NLL facts, and run legacy polonius analysis.
diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs
index 519ba0b9e0c..679e111caa9 100644
--- a/compiler/rustc_borrowck/src/places_conflict.rs
+++ b/compiler/rustc_borrowck/src/places_conflict.rs
@@ -3,7 +3,7 @@
 //! we can prove overlap one way or another. Essentially, we treat `Overlap` as
 //! a monoid and report a conflict if the product ends up not being `Disjoint`.
 //!
-//! At each step, if we didn't run out of borrow or place, we know that our elements
+//! On each step, if we didn't run out of borrow or place, we know that our elements
 //! have the same type, and that they only overlap if they are the identical.
 //!
 //! For example, if we are comparing these:
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 82aeca66693..238d7d0749a 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -18,6 +18,7 @@ use rustc_infer::infer::region_constraints::RegionConstraintData;
 use rustc_infer::infer::{
     BoundRegion, BoundRegionConversionTime, InferCtxt, NllRegionVariableOrigin,
 };
+use rustc_infer::traits::PredicateObligations;
 use rustc_middle::mir::tcx::PlaceTy;
 use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
@@ -40,7 +41,6 @@ use rustc_span::source_map::Spanned;
 use rustc_span::symbol::sym;
 use rustc_span::{DUMMY_SP, Span};
 use rustc_target::abi::{FIRST_VARIANT, FieldIdx};
-use rustc_trait_selection::traits::PredicateObligation;
 use rustc_trait_selection::traits::query::type_op::custom::{
     CustomTypeOp, scrape_region_constraints,
 };
@@ -2940,7 +2940,7 @@ impl NormalizeLocation for Location {
 pub(super) struct InstantiateOpaqueType<'tcx> {
     pub base_universe: Option<ty::UniverseIndex>,
     pub region_constraints: Option<RegionConstraintData<'tcx>>,
-    pub obligations: Vec<PredicateObligation<'tcx>>,
+    pub obligations: PredicateObligations<'tcx>,
 }
 
 impl<'tcx> TypeOp<'tcx> for InstantiateOpaqueType<'tcx> {
diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
index bf079e813f1..cfc14d146bd 100644
--- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs
+++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
@@ -11,6 +11,7 @@ use rustc_middle::span_bug;
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::ty::fold::FnMutDelegate;
+use rustc_middle::ty::relate::combine::{super_combine_consts, super_combine_tys};
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::symbol::sym;
 use rustc_span::{Span, Symbol};
@@ -362,7 +363,7 @@ impl<'b, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'b, 'tcx> {
                 &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
                 &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
             ) if a_def_id == b_def_id || infcx.next_trait_solver() => {
-                infcx.super_combine_tys(self, a, b).map(|_| ()).or_else(|err| {
+                super_combine_tys(&infcx.infcx, self, a, b).map(|_| ()).or_else(|err| {
                     // This behavior is only there for the old solver, the new solver
                     // shouldn't ever fail. Instead, it unconditionally emits an
                     // alias-relate goal.
@@ -385,7 +386,7 @@ impl<'b, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'b, 'tcx> {
                 debug!(?a, ?b, ?self.ambient_variance);
 
                 // Will also handle unification of `IntVar` and `FloatVar`.
-                self.type_checker.infcx.super_combine_tys(self, a, b)?;
+                super_combine_tys(&self.type_checker.infcx.infcx, self, a, b)?;
             }
         }
 
@@ -422,7 +423,7 @@ impl<'b, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'b, 'tcx> {
         assert!(!a.has_non_region_infer(), "unexpected inference var {:?}", a);
         assert!(!b.has_non_region_infer(), "unexpected inference var {:?}", b);
 
-        self.type_checker.infcx.super_combine_consts(self, a, b)
+        super_combine_consts(&self.type_checker.infcx.infcx, self, a, b)
     }
 
     #[instrument(skip(self), level = "trace")]
diff --git a/compiler/rustc_builtin_macros/Cargo.toml b/compiler/rustc_builtin_macros/Cargo.toml
index 21b87be4b81..ef48486f6f1 100644
--- a/compiler/rustc_builtin_macros/Cargo.toml
+++ b/compiler/rustc_builtin_macros/Cargo.toml
@@ -3,6 +3,10 @@ name = "rustc_builtin_macros"
 version = "0.0.0"
 edition = "2021"
 
+
+[lints.rust]
+unexpected_cfgs = { level = "warn", check-cfg = ['cfg(llvm_enzyme)'] }
+
 [lib]
 doctest = false
 
diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
index 77cb8dc63c4..6ebc2fd870c 100644
--- a/compiler/rustc_builtin_macros/messages.ftl
+++ b/compiler/rustc_builtin_macros/messages.ftl
@@ -69,6 +69,15 @@ builtin_macros_assert_requires_boolean = macro requires a boolean expression as
 builtin_macros_assert_requires_expression = macro requires an expression as an argument
     .suggestion = try removing semicolon
 
+builtin_macros_autodiff = autodiff must be applied to function
+builtin_macros_autodiff_missing_config = autodiff requires at least a name and mode
+builtin_macros_autodiff_mode = unknown Mode: `{$mode}`. Use `Forward` or `Reverse`
+builtin_macros_autodiff_mode_activity = {$act} can not be used in {$mode} Mode
+builtin_macros_autodiff_not_build = this rustc version does not support autodiff
+builtin_macros_autodiff_number_activities = expected {$expected} activities, but found {$found}
+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_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/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs
new file mode 100644
index 00000000000..66bb11ca522
--- /dev/null
+++ b/compiler/rustc_builtin_macros/src/autodiff.rs
@@ -0,0 +1,820 @@
+//! This module contains the implementation of the `#[autodiff]` attribute.
+//! Currently our linter isn't smart enough to see that each import is used in one of the two
+//! configs (autodiff enabled or disabled), so we have to add cfg's to each import.
+//! FIXME(ZuseZ4): Remove this once we have a smarter linter.
+
+#[cfg(llvm_enzyme)]
+mod llvm_enzyme {
+    use std::str::FromStr;
+    use std::string::String;
+
+    use rustc_ast::expand::autodiff_attrs::{
+        AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ty_for_activity,
+    };
+    use rustc_ast::ptr::P;
+    use rustc_ast::token::{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,
+    };
+    use rustc_expand::base::{Annotatable, ExtCtxt};
+    use rustc_span::symbol::{Ident, kw, sym};
+    use rustc_span::{Span, Symbol};
+    use thin_vec::{ThinVec, thin_vec};
+    use tracing::{debug, trace};
+
+    use crate::errors;
+
+    // If we have a default `()` return type or explicitley `()` return type,
+    // then we often can skip doing some work.
+    fn has_ret(ty: &FnRetTy) -> bool {
+        match ty {
+            FnRetTy::Ty(ty) => !ty.kind.is_unit(),
+            FnRetTy::Default(_) => false,
+        }
+    }
+    fn first_ident(x: &MetaItemInner) -> rustc_span::symbol::Ident {
+        let segments = &x.meta_item().unwrap().path.segments;
+        assert!(segments.len() == 1);
+        segments[0].ident
+    }
+
+    fn name(x: &MetaItemInner) -> String {
+        first_ident(x).name.to_string()
+    }
+
+    pub(crate) fn from_ast(
+        ecx: &mut ExtCtxt<'_>,
+        meta_item: &ThinVec<MetaItemInner>,
+        has_ret: bool,
+    ) -> AutoDiffAttrs {
+        let dcx = ecx.sess.dcx();
+        let mode = name(&meta_item[1]);
+        let Ok(mode) = DiffMode::from_str(&mode) else {
+            dcx.emit_err(errors::AutoDiffInvalidMode { span: meta_item[1].span(), mode });
+            return AutoDiffAttrs::error();
+        };
+        let mut activities: Vec<DiffActivity> = vec![];
+        let mut errors = false;
+        for x in &meta_item[2..] {
+            let activity_str = name(&x);
+            let res = DiffActivity::from_str(&activity_str);
+            match res {
+                Ok(x) => activities.push(x),
+                Err(_) => {
+                    dcx.emit_err(errors::AutoDiffUnknownActivity {
+                        span: x.span(),
+                        act: activity_str,
+                    });
+                    errors = true;
+                }
+            };
+        }
+        if errors {
+            return AutoDiffAttrs::error();
+        }
+
+        // If a return type exist, we need to split the last activity,
+        // otherwise we return None as placeholder.
+        let (ret_activity, input_activity) = if has_ret {
+            let Some((last, rest)) = activities.split_last() else {
+                unreachable!(
+                    "should not be reachable because we counted the number of activities previously"
+                );
+            };
+            (last, rest)
+        } else {
+            (&DiffActivity::None, activities.as_slice())
+        };
+
+        AutoDiffAttrs { mode, ret_activity: *ret_activity, input_activity: input_activity.to_vec() }
+    }
+
+    /// We expand the autodiff macro to generate a new placeholder function which passes
+    /// type-checking and can be called by users. The function body of the placeholder function will
+    /// later be replaced on LLVM-IR level, so the design of the body is less important and for now
+    /// should just prevent early inlining and optimizations which alter the function signature.
+    /// The exact signature of the generated function depends on the configuration provided by the
+    /// user, but here is an example:
+    ///
+    /// ```
+    /// #[autodiff(cos_box, Reverse, Duplicated, Active)]
+    /// fn sin(x: &Box<f32>) -> f32 {
+    ///     f32::sin(**x)
+    /// }
+    /// ```
+    /// which becomes expanded to:
+    /// ```
+    /// #[rustc_autodiff]
+    /// #[inline(never)]
+    /// fn sin(x: &Box<f32>) -> f32 {
+    ///     f32::sin(**x)
+    /// }
+    /// #[rustc_autodiff(Reverse, Duplicated, Active)]
+    /// #[inline(never)]
+    /// fn cos_box(x: &Box<f32>, dx: &mut Box<f32>, dret: f32) -> f32 {
+    ///     unsafe {
+    ///         asm!("NOP");
+    ///     };
+    ///     ::core::hint::black_box(sin(x));
+    ///     ::core::hint::black_box((dx, dret));
+    ///     ::core::hint::black_box(sin(x))
+    /// }
+    /// ```
+    /// FIXME(ZuseZ4): Once autodiff is enabled by default, make this a doc comment which is checked
+    /// in CI.
+    pub(crate) fn expand(
+        ecx: &mut ExtCtxt<'_>,
+        expand_span: Span,
+        meta_item: &ast::MetaItem,
+        mut item: Annotatable,
+    ) -> Vec<Annotatable> {
+        let dcx = ecx.sess.dcx();
+        // first get the annotable item:
+        let (sig, is_impl): (FnSig, bool) = match &item {
+            Annotatable::Item(ref iitem) => {
+                let sig = match &iitem.kind {
+                    ItemKind::Fn(box ast::Fn { sig, .. }) => sig,
+                    _ => {
+                        dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
+                        return vec![item];
+                    }
+                };
+                (sig.clone(), false)
+            }
+            Annotatable::AssocItem(ref assoc_item, _) => {
+                let sig = match &assoc_item.kind {
+                    ast::AssocItemKind::Fn(box ast::Fn { sig, .. }) => sig,
+                    _ => {
+                        dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
+                        return vec![item];
+                    }
+                };
+                (sig.clone(), true)
+            }
+            _ => {
+                dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
+                return vec![item];
+            }
+        };
+
+        let meta_item_vec: ThinVec<MetaItemInner> = match meta_item.kind {
+            ast::MetaItemKind::List(ref vec) => vec.clone(),
+            _ => {
+                dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
+                return vec![item];
+            }
+        };
+
+        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(ref iitem) => (iitem.vis.clone(), iitem.ident.clone()),
+            Annotatable::AssocItem(ref assoc_item, _) => {
+                (assoc_item.vis.clone(), assoc_item.ident.clone())
+            }
+            _ => {
+                dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
+                return vec![item];
+            }
+        };
+
+        // 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];
+        } 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));
+            }
+        }
+        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));
+        }
+        let ts: TokenStream = TokenStream::from_iter(ts);
+
+        let x: AutoDiffAttrs = from_ast(ecx, &meta_item_vec, has_ret);
+        if !x.is_active() {
+            // We encountered an error, so we return the original item.
+            // This allows us to potentially parse other attributes.
+            return vec![item];
+        }
+        let span = ecx.with_def_site_ctxt(expand_span);
+
+        let n_active: u32 = x
+            .input_activity
+            .iter()
+            .filter(|a| **a == DiffActivity::Active || **a == DiffActivity::ActiveOnly)
+            .count() as u32;
+        let (d_sig, new_args, idents, errored) = gen_enzyme_decl(ecx, &sig, &x, span);
+        let new_decl_span = d_sig.span;
+        let d_body = gen_enzyme_body(
+            ecx,
+            &x,
+            n_active,
+            &sig,
+            &d_sig,
+            primal,
+            &new_args,
+            span,
+            sig_span,
+            new_decl_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,
+            generics: Generics::default(),
+            body: Some(d_body),
+        });
+        let mut rustc_ad_attr =
+            P(ast::NormalAttr::from_ident(Ident::with_dummy_span(sym::rustc_autodiff)));
+
+        let ts2: Vec<TokenTree> = vec![TokenTree::Token(
+            Token::new(TokenKind::Ident(sym::never, false.into()), span),
+            Spacing::Joint,
+        )];
+        let never_arg = ast::DelimArgs {
+            dspan: ast::tokenstream::DelimSpan::from_single(span),
+            delim: ast::token::Delimiter::Parenthesis,
+            tokens: ast::tokenstream::TokenStream::from_iter(ts2),
+        };
+        let inline_item = ast::AttrItem {
+            unsafety: ast::Safety::Default,
+            path: ast::Path::from_ident(Ident::with_dummy_span(sym::inline)),
+            args: ast::AttrArgs::Delimited(never_arg),
+            tokens: None,
+        };
+        let inline_never_attr = P(ast::NormalAttr { item: inline_item, tokens: None });
+        let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id();
+        let attr: ast::Attribute = ast::Attribute {
+            kind: ast::AttrKind::Normal(rustc_ad_attr.clone()),
+            id: new_id,
+            style: ast::AttrStyle::Outer,
+            span,
+        };
+        let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id();
+        let inline_never: ast::Attribute = ast::Attribute {
+            kind: ast::AttrKind::Normal(inline_never_attr),
+            id: new_id,
+            style: ast::AttrStyle::Outer,
+            span,
+        };
+
+        // Don't add it multiple times:
+        let orig_annotatable: Annotatable = match item {
+            Annotatable::Item(ref mut iitem) => {
+                if !iitem.attrs.iter().any(|a| a.id == attr.id) {
+                    iitem.attrs.push(attr.clone());
+                }
+                if !iitem.attrs.iter().any(|a| a.id == inline_never.id) {
+                    iitem.attrs.push(inline_never.clone());
+                }
+                Annotatable::Item(iitem.clone())
+            }
+            Annotatable::AssocItem(ref mut assoc_item, i @ Impl) => {
+                if !assoc_item.attrs.iter().any(|a| a.id == attr.id) {
+                    assoc_item.attrs.push(attr.clone());
+                }
+                if !assoc_item.attrs.iter().any(|a| a.id == inline_never.id) {
+                    assoc_item.attrs.push(inline_never.clone());
+                }
+                Annotatable::AssocItem(assoc_item.clone(), i)
+            }
+            _ => {
+                unreachable!("annotatable kind checked previously")
+            }
+        };
+        // Now update for d_fn
+        rustc_ad_attr.item.args = rustc_ast::AttrArgs::Delimited(rustc_ast::DelimArgs {
+            dspan: DelimSpan::dummy(),
+            delim: rustc_ast::token::Delimiter::Parenthesis,
+            tokens: ts,
+        });
+        let d_attr: ast::Attribute = ast::Attribute {
+            kind: ast::AttrKind::Normal(rustc_ad_attr.clone()),
+            id: new_id,
+            style: ast::AttrStyle::Outer,
+            span,
+        };
+
+        let d_annotatable = if is_impl {
+            let assoc_item: AssocItemKind = ast::AssocItemKind::Fn(asdf);
+            let d_fn = P(ast::AssocItem {
+                attrs: thin_vec![d_attr.clone(), inline_never],
+                id: ast::DUMMY_NODE_ID,
+                span,
+                vis,
+                ident: d_ident,
+                kind: assoc_item,
+                tokens: None,
+            });
+            Annotatable::AssocItem(d_fn, Impl)
+        } else {
+            let mut d_fn = ecx.item(
+                span,
+                d_ident,
+                thin_vec![d_attr.clone(), inline_never],
+                ItemKind::Fn(asdf),
+            );
+            d_fn.vis = vis;
+            Annotatable::Item(d_fn)
+        };
+
+        return vec![orig_annotatable, d_annotatable];
+    }
+
+    // shadow arguments (the extra ones which were not in the original (primal) function), in reverse mode must be
+    // mutable references or ptrs, because Enzyme will write into them.
+    fn assure_mut_ref(ty: &ast::Ty) -> ast::Ty {
+        let mut ty = ty.clone();
+        match ty.kind {
+            TyKind::Ptr(ref mut mut_ty) => {
+                mut_ty.mutbl = ast::Mutability::Mut;
+            }
+            TyKind::Ref(_, ref mut mut_ty) => {
+                mut_ty.mutbl = ast::Mutability::Mut;
+            }
+            _ => {
+                panic!("unsupported type: {:?}", ty);
+            }
+        }
+        ty
+    }
+
+    /// We only want this function to type-check, since we will replace the body
+    /// later on llvm level. Using `loop {}` does not cover all return types anymore,
+    /// so instead we build something that should pass. We also add a inline_asm
+    /// line, as one more barrier for rustc to prevent inlining of this function.
+    /// FIXME(ZuseZ4): We still have cases of incorrect inlining across modules, see
+    /// <https://github.com/EnzymeAD/rust/issues/173>, so this isn't sufficient.
+    /// It also triggers an Enzyme crash if we due to a bug ever try to differentiate
+    /// this function (which should never happen, since it is only a placeholder).
+    /// Finally, we also add back_box usages of all input arguments, to prevent rustc
+    /// from optimizing any arguments away.
+    fn gen_enzyme_body(
+        ecx: &ExtCtxt<'_>,
+        x: &AutoDiffAttrs,
+        n_active: u32,
+        sig: &ast::FnSig,
+        d_sig: &ast::FnSig,
+        primal: Ident,
+        new_names: &[String],
+        span: Span,
+        sig_span: Span,
+        new_decl_span: Span,
+        idents: Vec<Ident>,
+        errored: bool,
+    ) -> P<ast::Block> {
+        let blackbox_path = ecx.std_path(&[sym::hint, sym::black_box]);
+        let noop = ast::InlineAsm {
+            asm_macro: ast::AsmMacro::Asm,
+            template: vec![ast::InlineAsmTemplatePiece::String("NOP".into())],
+            template_strs: Box::new([]),
+            operands: vec![],
+            clobber_abis: vec![],
+            options: ast::InlineAsmOptions::PURE | ast::InlineAsmOptions::NOMEM,
+            line_spans: vec![],
+        };
+        let noop_expr = ecx.expr_asm(span, P(noop));
+        let unsf = ast::BlockCheckMode::Unsafe(ast::UnsafeSource::CompilerGenerated);
+        let unsf_block = ast::Block {
+            stmts: thin_vec![ecx.stmt_semi(noop_expr)],
+            id: ast::DUMMY_NODE_ID,
+            tokens: None,
+            rules: unsf,
+            span,
+            could_be_bare_literal: false,
+        };
+        let unsf_expr = ecx.expr_block(P(unsf_block));
+        let blackbox_call_expr = ecx.expr_path(ecx.path(span, blackbox_path));
+        let primal_call = gen_primal_call(ecx, span, primal, idents);
+        let black_box_primal_call =
+            ecx.expr_call(new_decl_span, blackbox_call_expr.clone(), thin_vec![
+                primal_call.clone()
+            ]);
+        let tup_args = new_names
+            .iter()
+            .map(|arg| ecx.expr_path(ecx.path_ident(span, Ident::from_str(arg))))
+            .collect();
+
+        let black_box_remaining_args =
+            ecx.expr_call(sig_span, blackbox_call_expr.clone(), thin_vec![
+                ecx.expr_tuple(sig_span, tup_args)
+            ]);
+
+        let mut body = ecx.block(span, ThinVec::new());
+        body.stmts.push(ecx.stmt_semi(unsf_expr));
+
+        // This uses primal args which won't be available if we errored before
+        if !errored {
+            body.stmts.push(ecx.stmt_semi(black_box_primal_call.clone()));
+        }
+        body.stmts.push(ecx.stmt_semi(black_box_remaining_args));
+
+        if !has_ret(&d_sig.decl.output) {
+            // there is no return type that we have to match, () works fine.
+            return body;
+        }
+
+        // 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();
+
+        if primal_ret && n_active == 0 && x.mode.is_rev() {
+            // We only have the primal ret.
+            body.stmts.push(ecx.stmt_expr(black_box_primal_call.clone()));
+            return body;
+        }
+
+        if !primal_ret && n_active == 1 {
+            // Again no tuple return, so return default float val.
+            let ty = match d_sig.decl.output {
+                FnRetTy::Ty(ref ty) => ty.clone(),
+                FnRetTy::Default(span) => {
+                    panic!("Did not expect Default ret ty: {:?}", span);
+                }
+            };
+            let arg = ty.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![]);
+            body.stmts.push(ecx.stmt_expr(default_call_expr));
+            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.clone());
+        }
+
+        // Now construct default placeholder for each active float.
+        // Is there something nicer than f32::default() and f64::default()?
+        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));
+                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;
+            }
+            [arg] => {
+                ret = ecx
+                    .expr_call(new_decl_span, blackbox_call_expr.clone(), thin_vec![arg.clone()]);
+            }
+            args => {
+                let ret_tuple: P<ast::Expr> = ecx.expr_tuple(span, args.into());
+                ret =
+                    ecx.expr_call(new_decl_span, blackbox_call_expr.clone(), thin_vec![ret_tuple]);
+            }
+        }
+        assert!(has_ret(&d_sig.decl.output));
+        body.stmts.push(ecx.stmt_expr(ret));
+
+        body
+    }
+
+    fn gen_primal_call(
+        ecx: &ExtCtxt<'_>,
+        span: Span,
+        primal: Ident,
+        idents: Vec<Ident>,
+    ) -> P<ast::Expr> {
+        let has_self = idents.len() > 0 && idents[0].name == kw::SelfLower;
+        if has_self {
+            let args: ThinVec<_> =
+                idents[1..].iter().map(|arg| ecx.expr_path(ecx.path_ident(span, *arg))).collect();
+            let self_expr = ecx.expr_self(span);
+            ecx.expr_method_call(span, self_expr, primal, args.clone())
+        } else {
+            let args: ThinVec<_> =
+                idents.iter().map(|arg| ecx.expr_path(ecx.path_ident(span, *arg))).collect();
+            let primal_call_expr = ecx.expr_path(ecx.path_ident(span, primal));
+            ecx.expr_call(span, primal_call_expr, args)
+        }
+    }
+
+    // Generate the new function declaration. Const arguments are kept as is. Duplicated arguments must
+    // be pointers or references. Those receive a shadow argument, which is a mutable reference/pointer.
+    // Active arguments must be scalars. Their shadow argument is added to the return type (and will be
+    // zero-initialized by Enzyme).
+    // Each argument of the primal function (and the return type if existing) must be annotated with an
+    // activity.
+    //
+    // Error handling: If the user provides an invalid configuration (incorrect numbers, types, or
+    // both), we emit an error and return the original signature. This allows us to continue parsing.
+    fn gen_enzyme_decl(
+        ecx: &ExtCtxt<'_>,
+        sig: &ast::FnSig,
+        x: &AutoDiffAttrs,
+        span: Span,
+    ) -> (ast::FnSig, Vec<String>, Vec<Ident>, bool) {
+        let dcx = ecx.sess.dcx();
+        let has_ret = has_ret(&sig.decl.output);
+        let sig_args = sig.decl.inputs.len() + if has_ret { 1 } else { 0 };
+        let num_activities = x.input_activity.len() + if x.has_ret_activity() { 1 } else { 0 };
+        if sig_args != num_activities {
+            dcx.emit_err(errors::AutoDiffInvalidNumberActivities {
+                span,
+                expected: sig_args,
+                found: num_activities,
+            });
+            // This is not the right signature, but we can continue parsing.
+            return (sig.clone(), vec![], vec![], true);
+        }
+        assert!(sig.decl.inputs.len() == x.input_activity.len());
+        assert!(has_ret == x.has_ret_activity());
+        let mut d_decl = sig.decl.clone();
+        let mut d_inputs = Vec::new();
+        let mut new_inputs = Vec::new();
+        let mut idents = Vec::new();
+        let mut act_ret = ThinVec::new();
+
+        // We have two loops, a first one just to check the activities and types and possibly report
+        // multiple errors in one compilation session.
+        let mut errors = false;
+        for (arg, activity) in sig.decl.inputs.iter().zip(x.input_activity.iter()) {
+            if !valid_input_activity(x.mode, *activity) {
+                dcx.emit_err(errors::AutoDiffInvalidApplicationModeAct {
+                    span,
+                    mode: x.mode.to_string(),
+                    act: activity.to_string(),
+                });
+                errors = true;
+            }
+            if !valid_ty_for_activity(&arg.ty, *activity) {
+                dcx.emit_err(errors::AutoDiffInvalidTypeForActivity {
+                    span: arg.ty.span,
+                    act: activity.to_string(),
+                });
+                errors = true;
+            }
+        }
+        if errors {
+            // This is not the right signature, but we can continue parsing.
+            return (sig.clone(), new_inputs, idents, true);
+        }
+        let unsafe_activities = x
+            .input_activity
+            .iter()
+            .any(|&act| matches!(act, DiffActivity::DuplicatedOnly | DiffActivity::DualOnly));
+        for (arg, activity) in sig.decl.inputs.iter().zip(x.input_activity.iter()) {
+            d_inputs.push(arg.clone());
+            match activity {
+                DiffActivity::Active => {
+                    act_ret.push(arg.ty.clone());
+                }
+                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);
+                }
+                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);
+                }
+                DiffActivity::Const => {
+                    // Nothing to do here.
+                }
+                DiffActivity::None | DiffActivity::FakeActivitySize => {
+                    panic!("Should not happen");
+                }
+            }
+            if let PatKind::Ident(_, ident, _) = arg.pat.kind {
+                idents.push(ident.clone());
+            } else {
+                panic!("not an ident?");
+            }
+        }
+
+        let active_only_ret = x.ret_activity == DiffActivity::ActiveOnly;
+        if active_only_ret {
+            assert!(x.mode.is_rev());
+        }
+
+        // If we return a scalar in the primal and the scalar is active,
+        // then add it as last arg to the inputs.
+        if x.mode.is_rev() {
+            match x.ret_activity {
+                DiffActivity::Active | DiffActivity::ActiveOnly => {
+                    let ty = match d_decl.output {
+                        FnRetTy::Ty(ref ty) => ty.clone(),
+                        FnRetTy::Default(span) => {
+                            panic!("Did not expect Default ret ty: {:?}", span);
+                        }
+                    };
+                    let name = "dret".to_string();
+                    let ident = Ident::from_str_and_span(&name, ty.span);
+                    let shadow_arg = ast::Param {
+                        attrs: ThinVec::new(),
+                        ty: ty.clone(),
+                        pat: P(ast::Pat {
+                            id: ast::DUMMY_NODE_ID,
+                            kind: PatKind::Ident(BindingMode::NONE, ident, None),
+                            span: ty.span,
+                            tokens: None,
+                        }),
+                        id: ast::DUMMY_NODE_ID,
+                        span: ty.span,
+                        is_placeholder: false,
+                    };
+                    d_inputs.push(shadow_arg);
+                    new_inputs.push(name);
+                }
+                _ => {}
+            }
+        }
+        d_decl.inputs = d_inputs.into();
+
+        if x.mode.is_fwd() {
+            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);
+                    }
+                };
+                // 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.
+            }
+        }
+
+        // If we use ActiveOnly, drop the original return value.
+        d_decl.output =
+            if active_only_ret { FnRetTy::Default(span) } else { d_decl.output.clone() };
+
+        trace!("act_ret: {:?}", act_ret);
+
+        // If we have an active input scalar, add it's gradient to the
+        // return type. This might require changing the return type to a
+        // tuple.
+        if act_ret.len() > 0 {
+            let ret_ty = match d_decl.output {
+                FnRetTy::Ty(ref ty) => {
+                    if !active_only_ret {
+                        act_ret.insert(0, ty.clone());
+                    }
+                    let kind = TyKind::Tup(act_ret);
+                    P(rustc_ast::Ty { kind, id: ty.id, span: ty.span, tokens: None })
+                }
+                FnRetTy::Default(span) => {
+                    if act_ret.len() == 1 {
+                        act_ret[0].clone()
+                    } else {
+                        let kind = TyKind::Tup(act_ret.iter().map(|arg| arg.clone()).collect());
+                        P(rustc_ast::Ty { kind, id: ast::DUMMY_NODE_ID, span, tokens: None })
+                    }
+                }
+            };
+            d_decl.output = FnRetTy::Ty(ret_ty);
+        }
+
+        let mut d_header = sig.header.clone();
+        if unsafe_activities {
+            d_header.safety = rustc_ast::Safety::Unsafe(span);
+        }
+        let d_sig = FnSig { header: d_header, decl: d_decl, span };
+        trace!("Generated signature: {:?}", d_sig);
+        (d_sig, new_inputs, idents, false)
+    }
+}
+
+#[cfg(not(llvm_enzyme))]
+mod ad_fallback {
+    use rustc_ast::ast;
+    use rustc_expand::base::{Annotatable, ExtCtxt};
+    use rustc_span::Span;
+
+    use crate::errors;
+    pub(crate) fn expand(
+        ecx: &mut ExtCtxt<'_>,
+        _expand_span: Span,
+        meta_item: &ast::MetaItem,
+        item: Annotatable,
+    ) -> Vec<Annotatable> {
+        ecx.sess.dcx().emit_err(errors::AutoDiffSupportNotBuild { span: meta_item.span });
+        return vec![item];
+    }
+}
+
+#[cfg(not(llvm_enzyme))]
+pub(crate) use ad_fallback::expand;
+#[cfg(llvm_enzyme)]
+pub(crate) use llvm_enzyme::expand;
diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs
index dde696fca61..f79227d52a8 100644
--- a/compiler/rustc_builtin_macros/src/deriving/clone.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs
@@ -113,7 +113,7 @@ fn cs_clone_simple(
                 // Already produced an assertion for this type.
                 // Anonymous structs or unions must be eliminated as they cannot be
                 // type parameters.
-            } else if !field.ty.kind.is_anon_adt() {
+            } else {
                 // let _: AssertParamIsClone<FieldTy>;
                 super::assert_ty_bounds(cx, &mut stmts, field.ty.clone(), field.span, &[
                     sym::clone,
diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs
index d5ee5e430d5..e884c0ec718 100644
--- a/compiler/rustc_builtin_macros/src/deriving/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs
@@ -124,8 +124,6 @@ fn assert_ty_bounds(
     span: Span,
     assert_path: &[Symbol],
 ) {
-    // Deny anonymous structs or unions to avoid weird errors.
-    assert!(!ty.kind.is_anon_adt(), "Anonymous structs or unions cannot be type parameters");
     // Generate statement `let _: assert_path<ty>;`.
     let span = cx.with_def_site_ctxt(span);
     let assert_path = cx.path_all(span, true, cx.std_path(assert_path), vec![GenericArg::Type(ty)]);
diff --git a/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs b/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs
index fab1906eecd..731945f5cbf 100644
--- a/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs
@@ -312,7 +312,7 @@ pub(crate) fn expand_deriving_smart_ptr(
     impl_generics.params.insert(pointee_param_idx + 1, extra_param);
 
     // Add the impl blocks for `DispatchFromDyn` and `CoerceUnsized`.
-    let gen_args = vec![GenericArg::Type(alt_self_type.clone())];
+    let gen_args = vec![GenericArg::Type(alt_self_type)];
     add_impl_block(impl_generics.clone(), sym::DispatchFromDyn, gen_args.clone());
     add_impl_block(impl_generics.clone(), sym::CoerceUnsized, gen_args);
 }
@@ -333,12 +333,12 @@ fn contains_maybe_sized_bound_on_pointee(predicates: &[WherePredicate], pointee:
 }
 
 fn is_maybe_sized_bound(bound: &GenericBound) -> bool {
-    if let GenericBound::Trait(
-        trait_ref,
-        TraitBoundModifiers { polarity: ast::BoundPolarity::Maybe(_), .. },
-    ) = bound
+    if let GenericBound::Trait(trait_ref) = bound
+        && let TraitBoundModifiers { polarity: ast::BoundPolarity::Maybe(_), .. } =
+            trait_ref.modifiers
+        && is_sized_marker(&trait_ref.trait_ref.path)
     {
-        is_sized_marker(&trait_ref.trait_ref.path)
+        true
     } else {
         false
     }
diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs
index 25583cda5a2..43e2bf1796f 100644
--- a/compiler/rustc_builtin_macros/src/env.rs
+++ b/compiler/rustc_builtin_macros/src/env.rs
@@ -15,7 +15,7 @@ use rustc_span::symbol::{Ident, Symbol, kw, sym};
 use thin_vec::thin_vec;
 
 use crate::errors;
-use crate::util::{expr_to_string, get_exprs_from_tts, get_single_str_from_tts};
+use crate::util::{expr_to_string, get_exprs_from_tts, get_single_expr_from_tts};
 
 fn lookup_env<'cx>(cx: &'cx ExtCtxt<'_>, var: Symbol) -> Result<Symbol, VarError> {
     let var = var.as_str();
@@ -32,19 +32,28 @@ pub(crate) fn expand_option_env<'cx>(
     sp: Span,
     tts: TokenStream,
 ) -> MacroExpanderResult<'cx> {
-    let ExpandResult::Ready(mac) = get_single_str_from_tts(cx, sp, tts, "option_env!") else {
+    let ExpandResult::Ready(mac_expr) = get_single_expr_from_tts(cx, sp, tts, "option_env!") else {
+        return ExpandResult::Retry(());
+    };
+    let var_expr = match mac_expr {
+        Ok(var_expr) => var_expr,
+        Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)),
+    };
+    let ExpandResult::Ready(mac) =
+        expr_to_string(cx, var_expr.clone(), "argument must be a string literal")
+    else {
         return ExpandResult::Retry(());
     };
     let var = match mac {
-        Ok(var) => var,
+        Ok((var, _)) => var,
         Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)),
     };
 
     let sp = cx.with_def_site_ctxt(sp);
-    let value = lookup_env(cx, var).ok();
-    cx.sess.psess.env_depinfo.borrow_mut().insert((var, value));
+    let value = lookup_env(cx, var);
+    cx.sess.psess.env_depinfo.borrow_mut().insert((var, value.as_ref().ok().copied()));
     let e = match value {
-        None => {
+        Err(VarError::NotPresent) => {
             let lt = cx.lifetime(sp, Ident::new(kw::StaticLifetime, sp));
             cx.expr_path(cx.path_all(
                 sp,
@@ -58,7 +67,18 @@ pub(crate) fn expand_option_env<'cx>(
                 ))],
             ))
         }
-        Some(value) => {
+        Err(VarError::NotUnicode(_)) => {
+            let ExprKind::Lit(token::Lit {
+                kind: LitKind::Str | LitKind::StrRaw(..), symbol, ..
+            }) = &var_expr.kind
+            else {
+                unreachable!("`expr_to_string` ensures this is a string lit")
+            };
+
+            let guar = cx.dcx().emit_err(errors::EnvNotUnicode { span: sp, var: *symbol });
+            return ExpandResult::Ready(DummyResult::any(sp, guar));
+        }
+        Ok(value) => {
             cx.expr_call_global(sp, cx.std_path(&[sym::option, sym::Option, sym::Some]), thin_vec![
                 cx.expr_str(sp, value)
             ])
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index 639c2aa231c..f8e65661e52 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -145,6 +145,78 @@ pub(crate) struct AllocMustStatics {
     pub(crate) span: Span,
 }
 
+#[cfg(llvm_enzyme)]
+pub(crate) use autodiff::*;
+
+#[cfg(llvm_enzyme)]
+mod autodiff {
+    use super::*;
+    #[derive(Diagnostic)]
+    #[diag(builtin_macros_autodiff_missing_config)]
+    pub(crate) struct AutoDiffMissingConfig {
+        #[primary_span]
+        pub(crate) span: Span,
+    }
+    #[derive(Diagnostic)]
+    #[diag(builtin_macros_autodiff_unknown_activity)]
+    pub(crate) struct AutoDiffUnknownActivity {
+        #[primary_span]
+        pub(crate) span: Span,
+        pub(crate) act: String,
+    }
+    #[derive(Diagnostic)]
+    #[diag(builtin_macros_autodiff_ty_activity)]
+    pub(crate) struct AutoDiffInvalidTypeForActivity {
+        #[primary_span]
+        pub(crate) span: Span,
+        pub(crate) act: String,
+    }
+    #[derive(Diagnostic)]
+    #[diag(builtin_macros_autodiff_number_activities)]
+    pub(crate) struct AutoDiffInvalidNumberActivities {
+        #[primary_span]
+        pub(crate) span: Span,
+        pub(crate) expected: usize,
+        pub(crate) found: usize,
+    }
+    #[derive(Diagnostic)]
+    #[diag(builtin_macros_autodiff_mode_activity)]
+    pub(crate) struct AutoDiffInvalidApplicationModeAct {
+        #[primary_span]
+        pub(crate) span: Span,
+        pub(crate) mode: String,
+        pub(crate) act: String,
+    }
+
+    #[derive(Diagnostic)]
+    #[diag(builtin_macros_autodiff_mode)]
+    pub(crate) struct AutoDiffInvalidMode {
+        #[primary_span]
+        pub(crate) span: Span,
+        pub(crate) mode: String,
+    }
+
+    #[derive(Diagnostic)]
+    #[diag(builtin_macros_autodiff)]
+    pub(crate) struct AutoDiffInvalidApplication {
+        #[primary_span]
+        pub(crate) span: Span,
+    }
+}
+
+#[cfg(not(llvm_enzyme))]
+pub(crate) use ad_fallback::*;
+#[cfg(not(llvm_enzyme))]
+mod ad_fallback {
+    use super::*;
+    #[derive(Diagnostic)]
+    #[diag(builtin_macros_autodiff_not_build)]
+    pub(crate) struct AutoDiffSupportNotBuild {
+        #[primary_span]
+        pub(crate) span: Span,
+    }
+}
+
 #[derive(Diagnostic)]
 #[diag(builtin_macros_concat_bytes_invalid)]
 pub(crate) struct ConcatBytesInvalid {
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index 9501e93bad5..32730ac3867 100644
--- a/compiler/rustc_builtin_macros/src/format.rs
+++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -195,12 +195,26 @@ fn make_format_args(
                                     Applicability::MaybeIncorrect,
                                 );
                             } else {
-                                let sugg_fmt = match args.explicit_args().len() {
-                                    0 => "{}".to_string(),
-                                    count => {
-                                        format!("{}{{}}", "{} ".repeat(count))
+                                // `{}` or `()`
+                                let should_suggest = |kind: &ExprKind| -> bool {
+                                    match kind {
+                                        ExprKind::Block(b, None) if b.stmts.is_empty() => true,
+                                        ExprKind::Tup(v) if v.is_empty() => true,
+                                        _ => false,
                                     }
                                 };
+
+                                let mut sugg_fmt = String::new();
+                                for kind in std::iter::once(&efmt.kind)
+                                    .chain(args.explicit_args().into_iter().map(|a| &a.expr.kind))
+                                {
+                                    sugg_fmt.push_str(if should_suggest(kind) {
+                                        "{:?} "
+                                    } else {
+                                        "{} "
+                                    });
+                                }
+                                sugg_fmt = sugg_fmt.trim_end().to_string();
                                 err.span_suggestion(
                                     unexpanded_fmt_span.shrink_to_lo(),
                                     "you might be missing a string literal to format with",
diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index ebe5e2b5442..377d7f542cf 100644
--- a/compiler/rustc_builtin_macros/src/lib.rs
+++ b/compiler/rustc_builtin_macros/src/lib.rs
@@ -5,6 +5,7 @@
 #![allow(internal_features)]
 #![allow(rustc::diagnostic_outside_of_impl)]
 #![allow(rustc::untranslatable_diagnostic)]
+#![cfg_attr(not(bootstrap), feature(autodiff))]
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![doc(rust_logo)]
 #![feature(assert_matches)]
@@ -29,6 +30,7 @@ use crate::deriving::*;
 
 mod alloc_error_handler;
 mod assert;
+mod autodiff;
 mod cfg;
 mod cfg_accessible;
 mod cfg_eval;
@@ -106,6 +108,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
 
     register_attr! {
         alloc_error_handler: alloc_error_handler::expand,
+        autodiff: autodiff::expand,
         bench: test::expand_bench,
         cfg_accessible: cfg_accessible::Expander,
         cfg_eval: cfg_eval::expand,
diff --git a/compiler/rustc_builtin_macros/src/util.rs b/compiler/rustc_builtin_macros/src/util.rs
index d7b03a43ecb..2a28dfaf3c4 100644
--- a/compiler/rustc_builtin_macros/src/util.rs
+++ b/compiler/rustc_builtin_macros/src/util.rs
@@ -171,6 +171,30 @@ pub(crate) fn get_single_str_spanned_from_tts(
     tts: TokenStream,
     name: &str,
 ) -> ExpandResult<Result<(Symbol, Span), ErrorGuaranteed>, ()> {
+    let ExpandResult::Ready(ret) = get_single_expr_from_tts(cx, span, tts, name) else {
+        return ExpandResult::Retry(());
+    };
+    let ret = match ret {
+        Ok(ret) => ret,
+        Err(e) => return ExpandResult::Ready(Err(e)),
+    };
+    expr_to_spanned_string(cx, ret, "argument must be a string literal").map(|res| {
+        res.map_err(|err| match err {
+            Ok((err, _)) => err.emit(),
+            Err(guar) => guar,
+        })
+        .map(|(symbol, _style, span)| (symbol, span))
+    })
+}
+
+/// Interpreting `tts` as a comma-separated sequence of expressions,
+/// expect exactly one expression, or emit an error and return `Err`.
+pub(crate) fn get_single_expr_from_tts(
+    cx: &mut ExtCtxt<'_>,
+    span: Span,
+    tts: TokenStream,
+    name: &str,
+) -> ExpandResult<Result<P<ast::Expr>, ErrorGuaranteed>, ()> {
     let mut p = cx.new_parser_from_tts(tts);
     if p.token == token::Eof {
         let guar = cx.dcx().emit_err(errors::OnlyOneArgument { span, name });
@@ -185,13 +209,7 @@ pub(crate) fn get_single_str_spanned_from_tts(
     if p.token != token::Eof {
         cx.dcx().emit_err(errors::OnlyOneArgument { span, name });
     }
-    expr_to_spanned_string(cx, ret, "argument must be a string literal").map(|res| {
-        res.map_err(|err| match err {
-            Ok((err, _)) => err.emit(),
-            Err(guar) => guar,
-        })
-        .map(|(symbol, _style, span)| (symbol, span))
-    })
+    ExpandResult::Ready(Ok(ret))
 }
 
 /// Extracts comma-separated expressions from `tts`.
diff --git a/compiler/rustc_codegen_cranelift/src/discriminant.rs b/compiler/rustc_codegen_cranelift/src/discriminant.rs
index e7ac084558a..d462dcd63a9 100644
--- a/compiler/rustc_codegen_cranelift/src/discriminant.rs
+++ b/compiler/rustc_codegen_cranelift/src/discriminant.rs
@@ -3,7 +3,8 @@
 //! Adapted from <https://github.com/rust-lang/rust/blob/31c0645b9d2539f47eecb096142474b29dc542f7/compiler/rustc_codegen_ssa/src/mir/place.rs>
 //! (<https://github.com/rust-lang/rust/pull/104535>)
 
-use rustc_target::abi::{Int, TagEncoding, Variants};
+use rustc_abi::Primitive::Int;
+use rustc_abi::{TagEncoding, Variants};
 
 use crate::prelude::*;
 
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
index 19e5adc2538..35f0ccff3f9 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
@@ -328,6 +328,9 @@ fn codegen_float_intrinsic_call<'tcx>(
         sym::fabsf64 => ("fabs", 1, fx.tcx.types.f64, types::F64),
         sym::fmaf32 => ("fmaf", 3, fx.tcx.types.f32, types::F32),
         sym::fmaf64 => ("fma", 3, fx.tcx.types.f64, types::F64),
+        // FIXME: calling `fma` from libc without FMA target feature uses expensive sofware emulation
+        sym::fmuladdf32 => ("fmaf", 3, fx.tcx.types.f32, types::F32), // TODO: use cranelift intrinsic analogous to llvm.fmuladd.f32
+        sym::fmuladdf64 => ("fma", 3, fx.tcx.types.f64, types::F64), // TODO: use cranelift intrinsic analogous to llvm.fmuladd.f64
         sym::copysignf32 => ("copysignf", 2, fx.tcx.types.f32, types::F32),
         sym::copysignf64 => ("copysign", 2, fx.tcx.types.f64, types::F64),
         sym::floorf32 => ("floorf", 1, fx.tcx.types.f32, types::F32),
@@ -381,7 +384,7 @@ fn codegen_float_intrinsic_call<'tcx>(
 
     let layout = fx.layout_of(ty);
     let res = match intrinsic {
-        sym::fmaf32 | sym::fmaf64 => {
+        sym::fmaf32 | sym::fmaf64 | sym::fmuladdf32 | sym::fmuladdf64 => {
             CValue::by_val(fx.bcx.ins().fma(args[0], args[1], args[2]), layout)
         }
         sym::copysignf32 | sym::copysignf64 => {
diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs
index f6b7981395a..b6f9ce8fc29 100644
--- a/compiler/rustc_codegen_cranelift/src/lib.rs
+++ b/compiler/rustc_codegen_cranelift/src/lib.rs
@@ -15,6 +15,7 @@
 extern crate jobserver;
 #[macro_use]
 extern crate rustc_middle;
+extern crate rustc_abi;
 extern crate rustc_ast;
 extern crate rustc_codegen_ssa;
 extern crate rustc_data_structures;
diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl
index 0235384445e..bbae59ea7a5 100644
--- a/compiler/rustc_codegen_gcc/messages.ftl
+++ b/compiler/rustc_codegen_gcc/messages.ftl
@@ -8,9 +8,6 @@ codegen_gcc_invalid_minimum_alignment =
 codegen_gcc_lto_not_supported =
     LTO is not supported. You may get a linker error.
 
-codegen_gcc_tied_target_features = the target features {$features} must all be either enabled or disabled together
-    .help = add the missing features in a `target_feature` attribute
-
 codegen_gcc_unwinding_inline_asm =
     GCC backend does not support unwinding from inline asm
 
diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs
index 5fdf2680aac..d20e13e15b9 100644
--- a/compiler/rustc_codegen_gcc/src/attributes.rs
+++ b/compiler/rustc_codegen_gcc/src/attributes.rs
@@ -7,11 +7,9 @@ use rustc_attr::InstructionSetAttr;
 #[cfg(feature = "master")]
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::ty;
-use rustc_span::symbol::sym;
 
 use crate::context::CodegenCx;
-use crate::errors::TiedTargetFeatures;
-use crate::gcc_util::{check_tied_features, to_gcc_features};
+use crate::gcc_util::to_gcc_features;
 
 /// Get GCC attribute for the provided inline heuristic.
 #[cfg(feature = "master")]
@@ -72,26 +70,10 @@ pub fn from_fn_attrs<'gcc, 'tcx>(
         }
     }
 
-    let function_features = codegen_fn_attrs
+    let mut function_features = codegen_fn_attrs
         .target_features
         .iter()
         .map(|features| features.name.as_str())
-        .collect::<Vec<&str>>();
-
-    if let Some(features) = check_tied_features(
-        cx.tcx.sess,
-        &function_features.iter().map(|features| (*features, true)).collect(),
-    ) {
-        let span = cx
-            .tcx
-            .get_attr(instance.def_id(), sym::target_feature)
-            .map_or_else(|| cx.tcx.def_span(instance.def_id()), |a| a.span);
-        cx.tcx.dcx().create_err(TiedTargetFeatures { features: features.join(", "), span }).emit();
-        return;
-    }
-
-    let mut function_features = function_features
-        .iter()
         .flat_map(|feat| to_gcc_features(cx.tcx.sess, feat).into_iter())
         .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match *x {
             InstructionSetAttr::ArmA32 => "-thumb-mode", // TODO(antoyo): support removing feature.
diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs
index acd53588831..b611f9ba8bc 100644
--- a/compiler/rustc_codegen_gcc/src/builder.rs
+++ b/compiler/rustc_codegen_gcc/src/builder.rs
@@ -7,6 +7,8 @@ use gccjit::{
     BinaryOp, Block, ComparisonOp, Context, Function, LValue, Location, RValue, ToRValue, Type,
     UnaryOp,
 };
+use rustc_abi as abi;
+use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout, WrappingRange};
 use rustc_apfloat::{Float, Round, Status, ieee};
 use rustc_codegen_ssa::MemFlags;
 use rustc_codegen_ssa::common::{
@@ -28,7 +30,6 @@ use rustc_middle::ty::{Instance, ParamEnv, Ty, TyCtxt};
 use rustc_span::Span;
 use rustc_span::def_id::DefId;
 use rustc_target::abi::call::FnAbi;
-use rustc_target::abi::{self, Align, HasDataLayout, Size, TargetDataLayout, WrappingRange};
 use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, Target, WasmCAbi};
 
 use crate::common::{SignType, TypeReflection, type_is_pointer};
@@ -998,12 +999,12 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
         ) {
             let vr = scalar.valid_range(bx);
             match scalar.primitive() {
-                abi::Int(..) => {
+                abi::Primitive::Int(..) => {
                     if !scalar.is_always_valid(bx) {
                         bx.range_metadata(load, vr);
                     }
                 }
-                abi::Pointer(_) if vr.start < vr.end && !vr.contains(0) => {
+                abi::Primitive::Pointer(_) if vr.start < vr.end && !vr.contains(0) => {
                     bx.nonnull_metadata(load);
                 }
                 _ => {}
diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs
index 726b126e727..0d3e7083d56 100644
--- a/compiler/rustc_codegen_gcc/src/common.rs
+++ b/compiler/rustc_codegen_gcc/src/common.rs
@@ -1,11 +1,13 @@
 use gccjit::{LValue, RValue, ToRValue, Type};
+use rustc_abi as abi;
+use rustc_abi::HasDataLayout;
+use rustc_abi::Primitive::Pointer;
 use rustc_codegen_ssa::traits::{
     BaseTypeCodegenMethods, ConstCodegenMethods, MiscCodegenMethods, StaticCodegenMethods,
 };
 use rustc_middle::mir::Mutability;
 use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar};
 use rustc_middle::ty::layout::LayoutOf;
-use rustc_target::abi::{self, HasDataLayout, Pointer};
 
 use crate::consts::const_alloc_to_gcc;
 use crate::context::CodegenCx;
diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs
index 6bada3d334c..dc1895f437b 100644
--- a/compiler/rustc_codegen_gcc/src/errors.rs
+++ b/compiler/rustc_codegen_gcc/src/errors.rs
@@ -1,9 +1,6 @@
-use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::Span;
 
-use crate::fluent_generated as fluent;
-
 #[derive(Diagnostic)]
 #[diag(codegen_gcc_unknown_ctarget_feature_prefix)]
 #[note]
@@ -46,15 +43,6 @@ pub(crate) struct InvalidMinimumAlignment {
 }
 
 #[derive(Diagnostic)]
-#[diag(codegen_gcc_tied_target_features)]
-#[help]
-pub(crate) struct TiedTargetFeatures {
-    #[primary_span]
-    pub span: Span,
-    pub features: String,
-}
-
-#[derive(Diagnostic)]
 #[diag(codegen_gcc_copy_bitcode)]
 pub(crate) struct CopyBitcode {
     pub err: std::io::Error,
@@ -78,27 +66,3 @@ pub(crate) struct LtoDylib;
 pub(crate) struct LtoBitcodeFromRlib {
     pub gcc_err: String,
 }
-
-pub(crate) struct TargetFeatureDisableOrEnable<'a> {
-    pub features: &'a [&'a str],
-    pub span: Option<Span>,
-    pub missing_features: Option<MissingFeatures>,
-}
-
-#[derive(Subdiagnostic)]
-#[help(codegen_gcc_missing_features)]
-pub(crate) struct MissingFeatures;
-
-impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> {
-    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
-        let mut diag = Diag::new(dcx, level, fluent::codegen_gcc_target_feature_disable_or_enable);
-        if let Some(span) = self.span {
-            diag.span(span);
-        };
-        if let Some(missing_features) = self.missing_features {
-            diag.subdiagnostic(missing_features);
-        }
-        diag.arg("features", self.features.join(", "));
-        diag
-    }
-}
diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs
index 01dd1a8856a..3104088e0d5 100644
--- a/compiler/rustc_codegen_gcc/src/gcc_util.rs
+++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs
@@ -1,15 +1,14 @@
 #[cfg(feature = "master")]
 use gccjit::Context;
+use rustc_codegen_ssa::codegen_attrs::check_tied_features;
+use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_middle::bug;
 use rustc_session::Session;
 use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES;
 use smallvec::{SmallVec, smallvec};
 
-use crate::errors::{
-    PossibleFeature, TargetFeatureDisableOrEnable, UnknownCTargetFeature,
-    UnknownCTargetFeaturePrefix,
-};
+use crate::errors::{PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix};
 
 /// The list of GCC features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
 /// `--target` and similar).
@@ -185,23 +184,6 @@ pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]>
     }
 }
 
-// Given a map from target_features to whether they are enabled or disabled,
-// ensure only valid combinations are allowed.
-pub fn check_tied_features(
-    sess: &Session,
-    features: &FxHashMap<&str, bool>,
-) -> Option<&'static [&'static str]> {
-    for tied in sess.target.tied_target_features() {
-        // Tied features must be set to the same value, or not set at all
-        let mut tied_iter = tied.iter();
-        let enabled = features.get(tied_iter.next().unwrap());
-        if tied_iter.any(|feature| enabled != features.get(feature)) {
-            return Some(tied);
-        }
-    }
-    None
-}
-
 fn arch_to_gcc(name: &str) -> &str {
     match name {
         "M68020" => "68020",
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
index 945eedf5556..972d6632140 100644
--- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
+++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
@@ -66,6 +66,9 @@ fn get_simple_intrinsic<'gcc, 'tcx>(
         sym::log2f64 => "log2",
         sym::fmaf32 => "fmaf",
         sym::fmaf64 => "fma",
+        // FIXME: calling `fma` from libc without FMA target feature uses expensive sofware emulation
+        sym::fmuladdf32 => "fmaf", // TODO: use gcc intrinsic analogous to llvm.fmuladd.f32
+        sym::fmuladdf64 => "fma",  // TODO: use gcc intrinsic analogous to llvm.fmuladd.f64
         sym::fabsf32 => "fabsf",
         sym::fabsf64 => "fabs",
         sym::minnumf32 => "fminf",
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
index a7a32e285d8..7486eefeb85 100644
--- a/compiler/rustc_codegen_gcc/src/lib.rs
+++ b/compiler/rustc_codegen_gcc/src/lib.rs
@@ -32,6 +32,7 @@ extern crate tempfile;
 extern crate tracing;
 
 // The rustc crates we need
+extern crate rustc_abi;
 extern crate rustc_apfloat;
 extern crate rustc_ast;
 extern crate rustc_attr;
diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs
index 5b0d862ae6d..183e9ddf8bf 100644
--- a/compiler/rustc_codegen_gcc/src/type_of.rs
+++ b/compiler/rustc_codegen_gcc/src/type_of.rs
@@ -1,6 +1,9 @@
 use std::fmt::Write;
 
 use gccjit::{Struct, Type};
+use rustc_abi as abi;
+use rustc_abi::Primitive::*;
+use rustc_abi::{Abi, FieldsShape, Integer, PointeeInfo, Size, Variants};
 use rustc_codegen_ssa::traits::{
     BaseTypeCodegenMethods, DerivedTypeCodegenMethods, LayoutTypeCodegenMethods,
 };
@@ -8,11 +11,8 @@ use rustc_middle::bug;
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, CoroutineArgsExt, Ty, TypeVisitableExt};
+use rustc_target::abi::TyAbiInterface;
 use rustc_target::abi::call::{CastTarget, FnAbi, Reg};
-use rustc_target::abi::{
-    self, Abi, FieldsShape, Float, Int, Integer, PointeeInfo, Pointer, Size, TyAbiInterface,
-    Variants,
-};
 
 use crate::abi::{FnAbiGcc, FnAbiGccExt, GccType};
 use crate::context::CodegenCx;
diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml
index a93baf88413..03a871297c4 100644
--- a/compiler/rustc_codegen_llvm/Cargo.toml
+++ b/compiler/rustc_codegen_llvm/Cargo.toml
@@ -14,6 +14,7 @@ libc = "0.2"
 measureme = "11"
 object = { version = "0.36.3", default-features = false, features = ["std", "read"] }
 rustc-demangle = "0.1.21"
+rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_attr = { path = "../rustc_attr" }
 rustc_codegen_ssa = { path = "../rustc_codegen_ssa" }
diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl
index df2198df14b..0950e4bb26b 100644
--- a/compiler/rustc_codegen_llvm/messages.ftl
+++ b/compiler/rustc_codegen_llvm/messages.ftl
@@ -33,9 +33,6 @@ codegen_llvm_lto_proc_macro = lto cannot be used for `proc-macro` crate type wit
 codegen_llvm_mismatch_data_layout =
     data-layout for target `{$rustc_target}`, `{$rustc_layout}`, differs from LLVM target's `{$llvm_target}` default layout, `{$llvm_layout}`
 
-codegen_llvm_missing_features =
-    add the missing features in a `target_feature` attribute
-
 codegen_llvm_multiple_source_dicompileunit = multiple source DICompileUnits found
 codegen_llvm_multiple_source_dicompileunit_with_llvm_err = multiple source DICompileUnits found: {$llvm_err}
 
@@ -63,9 +60,6 @@ codegen_llvm_serialize_module_with_llvm_err = failed to serialize module {$name}
 codegen_llvm_symbol_already_defined =
     symbol `{$symbol_name}` is already defined
 
-codegen_llvm_target_feature_disable_or_enable =
-    the target features {$features} must all be either enabled or disabled together
-
 codegen_llvm_target_machine = could not create LLVM TargetMachine for triple: {$triple}
 codegen_llvm_target_machine_with_llvm_err = could not create LLVM TargetMachine for triple: {$triple}: {$llvm_err}
 
diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs
index 6a29eb5fa04..2fe5ed32daa 100644
--- a/compiler/rustc_codegen_llvm/src/abi.rs
+++ b/compiler/rustc_codegen_llvm/src/abi.rs
@@ -1,6 +1,9 @@
 use std::cmp;
 
 use libc::c_uint;
+use rustc_abi as abi;
+use rustc_abi::Primitive::Int;
+use rustc_abi::{HasDataLayout, Size};
 use rustc_codegen_ssa::MemFlags;
 use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
 use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue};
@@ -11,7 +14,6 @@ pub(crate) use rustc_middle::ty::layout::{WIDE_PTR_ADDR, WIDE_PTR_EXTRA};
 use rustc_middle::{bug, ty};
 use rustc_session::config;
 pub(crate) use rustc_target::abi::call::*;
-use rustc_target::abi::{self, HasDataLayout, Int, Size};
 use rustc_target::spec::SanitizerSet;
 pub(crate) use rustc_target::spec::abi::Abi;
 use smallvec::SmallVec;
diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs
index 82ca3f519f7..d1d7d0cf4ce 100644
--- a/compiler/rustc_codegen_llvm/src/asm.rs
+++ b/compiler/rustc_codegen_llvm/src/asm.rs
@@ -154,7 +154,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
                     // We prefer the latter because it matches the behavior of
                     // Clang.
                     if late && matches!(reg, InlineAsmRegOrRegClass::Reg(_)) {
-                        constraints.push(reg_to_llvm(reg, Some(&in_value.layout)).to_string());
+                        constraints.push(reg_to_llvm(reg, Some(&in_value.layout)));
                     } else {
                         constraints.push(format!("{}", op_idx[&idx]));
                     }
@@ -542,57 +542,16 @@ fn xmm_reg_index(reg: InlineAsmReg) -> Option<u32> {
 
 /// If the register is an AArch64 integer register then return its index.
 fn a64_reg_index(reg: InlineAsmReg) -> Option<u32> {
-    use AArch64InlineAsmReg::*;
-    // Unlike `a64_vreg_index`, we can't subtract `x0` to get the u32 because
-    // `x19` and `x29` are missing and the integer constants for the
-    // `x0`..`x30` enum variants don't all match the register number. E.g. the
-    // integer constant for `x18` is 18, but the constant for `x20` is 19.
-    Some(match reg {
-        InlineAsmReg::AArch64(r) => match r {
-            x0 => 0,
-            x1 => 1,
-            x2 => 2,
-            x3 => 3,
-            x4 => 4,
-            x5 => 5,
-            x6 => 6,
-            x7 => 7,
-            x8 => 8,
-            x9 => 9,
-            x10 => 10,
-            x11 => 11,
-            x12 => 12,
-            x13 => 13,
-            x14 => 14,
-            x15 => 15,
-            x16 => 16,
-            x17 => 17,
-            x18 => 18,
-            // x19 is reserved
-            x20 => 20,
-            x21 => 21,
-            x22 => 22,
-            x23 => 23,
-            x24 => 24,
-            x25 => 25,
-            x26 => 26,
-            x27 => 27,
-            x28 => 28,
-            // x29 is reserved
-            x30 => 30,
-            _ => return None,
-        },
-        _ => return None,
-    })
+    match reg {
+        InlineAsmReg::AArch64(r) => r.reg_index(),
+        _ => None,
+    }
 }
 
 /// If the register is an AArch64 vector register then return its index.
 fn a64_vreg_index(reg: InlineAsmReg) -> Option<u32> {
-    use AArch64InlineAsmReg::*;
     match reg {
-        InlineAsmReg::AArch64(reg) if reg as u32 >= v0 as u32 && reg as u32 <= v31 as u32 => {
-            Some(reg as u32 - v0 as u32)
-        }
+        InlineAsmReg::AArch64(reg) => reg.vreg_index(),
         _ => None,
     }
 }
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs
index 489259da856..2c5ec9dad59 100644
--- a/compiler/rustc_codegen_llvm/src/attributes.rs
+++ b/compiler/rustc_codegen_llvm/src/attributes.rs
@@ -6,12 +6,11 @@ use rustc_hir::def_id::DefId;
 use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, PatchableFunctionEntry};
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::config::{BranchProtection, FunctionReturn, OptLevel, PAuthKey, PacRet};
-use rustc_span::symbol::sym;
 use rustc_target::spec::{FramePointer, SanitizerSet, StackProbeType, StackProtector};
 use smallvec::SmallVec;
 
 use crate::context::CodegenCx;
-use crate::errors::{MissingFeatures, SanitizerMemtagRequiresMte, TargetFeatureDisableOrEnable};
+use crate::errors::SanitizerMemtagRequiresMte;
 use crate::llvm::AttributePlace::Function;
 use crate::llvm::{self, AllocKindFlags, Attribute, AttributeKind, AttributePlace, MemoryEffects};
 use crate::value::Value;
@@ -502,26 +501,6 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
     let function_features =
         codegen_fn_attrs.target_features.iter().map(|f| f.name.as_str()).collect::<Vec<&str>>();
 
-    if let Some(f) = llvm_util::check_tied_features(
-        cx.tcx.sess,
-        &function_features.iter().map(|f| (*f, true)).collect(),
-    ) {
-        let span = cx
-            .tcx
-            .get_attrs(instance.def_id(), sym::target_feature)
-            .next()
-            .map_or_else(|| cx.tcx.def_span(instance.def_id()), |a| a.span);
-        cx.tcx
-            .dcx()
-            .create_err(TargetFeatureDisableOrEnable {
-                features: f,
-                span: Some(span),
-                missing_features: Some(MissingFeatures),
-            })
-            .emit();
-        return;
-    }
-
     let function_features = function_features
         .iter()
         // Convert to LLVMFeatures and filter out unavailable ones
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 30d7ba4421b..dbf5298d64b 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -3,6 +3,8 @@ use std::ops::Deref;
 use std::{iter, ptr};
 
 use libc::{c_char, c_uint};
+use rustc_abi as abi;
+use rustc_abi::{Align, Size, WrappingRange};
 use rustc_codegen_ssa::MemFlags;
 use rustc_codegen_ssa::common::{IntPredicate, RealPredicate, SynchronizationScope, TypeKind};
 use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
@@ -20,7 +22,6 @@ use rustc_sanitizers::{cfi, kcfi};
 use rustc_session::config::OptLevel;
 use rustc_span::Span;
 use rustc_target::abi::call::FnAbi;
-use rustc_target::abi::{self, Align, Size, WrappingRange};
 use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target};
 use smallvec::SmallVec;
 use tracing::{debug, instrument};
@@ -505,12 +506,12 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
             }
 
             match scalar.primitive() {
-                abi::Int(..) => {
+                abi::Primitive::Int(..) => {
                     if !scalar.is_always_valid(bx) {
                         bx.range_metadata(load, scalar.valid_range(bx));
                     }
                 }
-                abi::Pointer(_) => {
+                abi::Primitive::Pointer(_) => {
                     if !scalar.valid_range(bx).contains(0) {
                         bx.nonnull_metadata(load);
                     }
@@ -521,7 +522,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
                         }
                     }
                 }
-                abi::Float(_) => {}
+                abi::Primitive::Float(_) => {}
             }
         }
 
diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs
index 31d59905446..0ced37b53a8 100644
--- a/compiler/rustc_codegen_llvm/src/common.rs
+++ b/compiler/rustc_codegen_llvm/src/common.rs
@@ -1,7 +1,11 @@
 //! Code that is useful in various codegen modules.
 
 use libc::{c_char, c_uint};
+use rustc_abi as abi;
+use rustc_abi::Primitive::Pointer;
+use rustc_abi::{AddressSpace, HasDataLayout};
 use rustc_ast::Mutability;
+use rustc_codegen_ssa::common::TypeKind;
 use rustc_codegen_ssa::traits::*;
 use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher};
 use rustc_hir::def_id::DefId;
@@ -9,7 +13,6 @@ use rustc_middle::bug;
 use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::cstore::DllImport;
-use rustc_target::abi::{self, AddressSpace, HasDataLayout, Pointer};
 use tracing::debug;
 
 use crate::consts::const_alloc_to_llvm;
@@ -144,6 +147,10 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
     }
 
     fn const_int(&self, t: &'ll Type, i: i64) -> &'ll Value {
+        debug_assert!(
+            self.type_kind(t) == TypeKind::Integer,
+            "only allows integer types in const_int"
+        );
         unsafe { llvm::LLVMConstInt(t, i as u64, True) }
     }
 
@@ -174,10 +181,18 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
     }
 
     fn const_uint(&self, t: &'ll Type, i: u64) -> &'ll Value {
+        debug_assert!(
+            self.type_kind(t) == TypeKind::Integer,
+            "only allows integer types in const_uint"
+        );
         unsafe { llvm::LLVMConstInt(t, i, False) }
     }
 
     fn const_uint_big(&self, t: &'ll Type, u: u128) -> &'ll Value {
+        debug_assert!(
+            self.type_kind(t) == TypeKind::Integer,
+            "only allows integer types in const_uint_big"
+        );
         unsafe {
             let words = [u as u64, (u >> 64) as u64];
             llvm::LLVMConstIntOfArbitraryPrecision(t, 2, words.as_ptr())
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index 81b82840472..2f830d6f941 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -138,6 +138,16 @@ pub(crate) unsafe fn create_module<'ll>(
         }
     }
 
+    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
+            // pointer kinds used in Windows.
+            // See https://github.com/llvm/llvm-project/pull/111879
+            target_data_layout =
+                target_data_layout.replace("-p270:32:32-p271:32:32-p272:64:64", "");
+        }
+    }
+
     // Ensure the data-layout values hardcoded remain the defaults.
     {
         let tm = crate::back::write::create_informational_target_machine(tcx.sess, false);
@@ -884,6 +894,11 @@ impl<'ll> CodegenCx<'ll, '_> {
         ifn!("llvm.fma.f64", fn(t_f64, t_f64, t_f64) -> t_f64);
         ifn!("llvm.fma.f128", fn(t_f128, t_f128, t_f128) -> t_f128);
 
+        ifn!("llvm.fmuladd.f16", fn(t_f16, t_f16, t_f16) -> t_f16);
+        ifn!("llvm.fmuladd.f32", fn(t_f32, t_f32, t_f32) -> t_f32);
+        ifn!("llvm.fmuladd.f64", fn(t_f64, t_f64, t_f64) -> t_f64);
+        ifn!("llvm.fmuladd.f128", fn(t_f128, t_f128, t_f128) -> t_f128);
+
         ifn!("llvm.fabs.f16", fn(t_f16) -> t_f16);
         ifn!("llvm.fabs.f32", fn(t_f32) -> t_f32);
         ifn!("llvm.fabs.f64", fn(t_f64) -> t_f64);
@@ -1187,10 +1202,11 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'_, 'tcx> {
         span: Span,
         fn_abi_request: FnAbiRequest<'tcx>,
     ) -> ! {
-        if let FnAbiError::Layout(LayoutError::SizeOverflow(_)) = err {
-            self.tcx.dcx().emit_fatal(Spanned { span, node: err })
-        } else {
-            match fn_abi_request {
+        match err {
+            FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::Cycle(_)) => {
+                self.tcx.dcx().emit_fatal(Spanned { span, node: err });
+            }
+            _ => match fn_abi_request {
                 FnAbiRequest::OfFnPtr { sig, extra_args } => {
                     span_bug!(span, "`fn_abi_of_fn_ptr({sig}, {extra_args:?})` failed: {err:?}",);
                 }
@@ -1200,7 +1216,7 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'_, 'tcx> {
                         "`fn_abi_of_instance({instance}, {extra_args:?})` failed: {err:?}",
                     );
                 }
-            }
+            },
         }
     }
 }
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
index 267a2244916..c2c261da79b 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
@@ -14,29 +14,20 @@ use crate::coverageinfo::ffi::CounterMappingRegion;
 use crate::coverageinfo::map_data::{FunctionCoverage, FunctionCoverageCollector};
 use crate::{coverageinfo, llvm};
 
-/// Generates and exports the Coverage Map.
+/// Generates and exports the coverage map, which is embedded in special
+/// linker sections in the final binary.
 ///
-/// Rust Coverage Map generation supports LLVM Coverage Mapping Format versions
-/// 6 and 7 (encoded as 5 and 6 respectively), as described at
-/// [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/18.0-2024-02-13/llvm/docs/CoverageMappingFormat.rst).
-/// These versions are supported by the LLVM coverage tools (`llvm-profdata` and `llvm-cov`)
-/// distributed in the `llvm-tools-preview` rustup component.
-///
-/// Consequently, Rust's bundled version of Clang also generates Coverage Maps compliant with
-/// the same version. Clang's implementation of Coverage Map generation was referenced when
-/// implementing this Rust version, and though the format documentation is very explicit and
-/// detailed, some undocumented details in Clang's implementation (that may or may not be important)
-/// were also replicated for Rust's Coverage Map.
+/// Those sections are then read and understood by LLVM's `llvm-cov` tool,
+/// which is distributed in the `llvm-tools` rustup component.
 pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
     let tcx = cx.tcx;
 
     // Ensure that LLVM is using a version of the coverage mapping format that
     // agrees with our Rust-side code. Expected versions (encoded as n-1) are:
-    // - `CovMapVersion::Version6` (5) used by LLVM 13-17
-    // - `CovMapVersion::Version7` (6) used by LLVM 18
+    // - `CovMapVersion::Version7` (6) used by LLVM 18-19
     let covmap_version = {
         let llvm_covmap_version = coverageinfo::mapping_version();
-        let expected_versions = 5..=6;
+        let expected_versions = 6..=6;
         assert!(
             expected_versions.contains(&llvm_covmap_version),
             "Coverage mapping version exposed by `llvm-wrapper` is out of sync; \
diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs
index 7be44dd51b5..33258cb46fa 100644
--- a/compiler/rustc_codegen_llvm/src/declare.rs
+++ b/compiler/rustc_codegen_llvm/src/declare.rs
@@ -84,10 +84,9 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
         unnamed: llvm::UnnamedAddr,
         fn_type: &'ll Type,
     ) -> &'ll Value {
-        // Declare C ABI functions with the visibility used by C by default.
-        let visibility = Visibility::from_generic(self.tcx.sess.default_visibility());
-
-        declare_raw_fn(self, name, llvm::CCallConv, unnamed, visibility, fn_type)
+        // Visibility should always be default for declarations, otherwise the linker may report an
+        // error.
+        declare_raw_fn(self, name, llvm::CCallConv, unnamed, Visibility::Default, fn_type)
     }
 
     /// Declare an entry Function
diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs
index bb481d2a308..0d436e1891e 100644
--- a/compiler/rustc_codegen_llvm/src/errors.rs
+++ b/compiler/rustc_codegen_llvm/src/errors.rs
@@ -80,30 +80,6 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for ParseTargetMachineConfig<'_> {
     }
 }
 
-pub(crate) struct TargetFeatureDisableOrEnable<'a> {
-    pub features: &'a [&'a str],
-    pub span: Option<Span>,
-    pub missing_features: Option<MissingFeatures>,
-}
-
-#[derive(Subdiagnostic)]
-#[help(codegen_llvm_missing_features)]
-pub(crate) struct MissingFeatures;
-
-impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> {
-    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
-        let mut diag = Diag::new(dcx, level, fluent::codegen_llvm_target_feature_disable_or_enable);
-        if let Some(span) = self.span {
-            diag.span(span);
-        };
-        if let Some(missing_features) = self.missing_features {
-            diag.subdiagnostic(missing_features);
-        }
-        diag.arg("features", self.features.join(", "));
-        diag
-    }
-}
-
 #[derive(Diagnostic)]
 #[diag(codegen_llvm_lto_disallowed)]
 pub(crate) struct LtoDisallowed;
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index 30c6f08e894..bfe623e7fc3 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -86,6 +86,11 @@ fn get_simple_intrinsic<'ll>(
         sym::fmaf64 => "llvm.fma.f64",
         sym::fmaf128 => "llvm.fma.f128",
 
+        sym::fmuladdf16 => "llvm.fmuladd.f16",
+        sym::fmuladdf32 => "llvm.fmuladd.f32",
+        sym::fmuladdf64 => "llvm.fmuladd.f64",
+        sym::fmuladdf128 => "llvm.fmuladd.f128",
+
         sym::fabsf16 => "llvm.fabs.f16",
         sym::fabsf32 => "llvm.fabs.f32",
         sym::fabsf64 => "llvm.fabs.f64",
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index bd847cd0068..57936215ff1 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -6,6 +6,7 @@ use std::{ptr, slice, str};
 
 use libc::c_int;
 use rustc_codegen_ssa::base::wants_wasm_eh;
+use rustc_codegen_ssa::codegen_attrs::check_tied_features;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::small_c_str::SmallCStr;
 use rustc_data_structures::unord::UnordSet;
@@ -19,8 +20,8 @@ use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATU
 
 use crate::back::write::create_informational_target_machine;
 use crate::errors::{
-    FixedX18InvalidArch, InvalidTargetFeaturePrefix, PossibleFeature, TargetFeatureDisableOrEnable,
-    UnknownCTargetFeature, UnknownCTargetFeaturePrefix, UnstableCTargetFeature,
+    FixedX18InvalidArch, InvalidTargetFeaturePrefix, PossibleFeature, UnknownCTargetFeature,
+    UnknownCTargetFeaturePrefix, UnstableCTargetFeature,
 };
 use crate::llvm;
 
@@ -247,7 +248,9 @@ 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", "sve-b16b16") => Some(LLVMFeature::new("b16b16")),
+        // 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")),
         ("aarch64", "flagm2") => Some(LLVMFeature::new("altnzcv")),
         // Rust ties fp and neon together.
         ("aarch64", "neon") => {
@@ -276,25 +279,6 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
     }
 }
 
-/// Given a map from target_features to whether they are enabled or disabled,
-/// ensure only valid combinations are allowed.
-pub(crate) fn check_tied_features(
-    sess: &Session,
-    features: &FxHashMap<&str, bool>,
-) -> Option<&'static [&'static str]> {
-    if !features.is_empty() {
-        for tied in sess.target.tied_target_features() {
-            // Tied features must be set to the same value, or not set at all
-            let mut tied_iter = tied.iter();
-            let enabled = features.get(tied_iter.next().unwrap());
-            if tied_iter.any(|f| enabled != features.get(f)) {
-                return Some(tied);
-            }
-        }
-    }
-    None
-}
-
 /// Used to generate cfg variables and apply features
 /// Must express features in the way Rust understands them
 pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
@@ -685,7 +669,7 @@ pub(crate) fn global_llvm_features(
         features.extend(feats);
 
         if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) {
-            sess.dcx().emit_err(TargetFeatureDisableOrEnable {
+            sess.dcx().emit_err(rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable {
                 features: f,
                 span: None,
                 missing_features: None,
diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs
index 7071dd86ee0..1af666f818b 100644
--- a/compiler/rustc_codegen_llvm/src/type_of.rs
+++ b/compiler/rustc_codegen_llvm/src/type_of.rs
@@ -1,11 +1,12 @@
 use std::fmt::Write;
 
+use rustc_abi::Primitive::{Float, Int, Pointer};
+use rustc_abi::{Abi, Align, FieldsShape, Scalar, Size, Variants};
 use rustc_codegen_ssa::traits::*;
 use rustc_middle::bug;
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
 use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
 use rustc_middle::ty::{self, CoroutineArgsExt, Ty, TypeVisitableExt};
-use rustc_target::abi::{Abi, Align, FieldsShape, Float, Int, Pointer, Scalar, Size, Variants};
 use tracing::debug;
 
 use crate::common::*;
diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml
index 58baf40b581..dffb7a7271e 100644
--- a/compiler/rustc_codegen_ssa/Cargo.toml
+++ b/compiler/rustc_codegen_ssa/Cargo.toml
@@ -14,6 +14,7 @@ itertools = "0.12"
 jobserver = "0.1.28"
 pathdiff = "0.2.0"
 regex = "1.4"
+rustc_abi = { path = "../rustc_abi" }
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_attr = { path = "../rustc_attr" }
diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index f02b0f72674..d07274920fe 100644
--- a/compiler/rustc_codegen_ssa/messages.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
@@ -183,6 +183,8 @@ codegen_ssa_metadata_object_file_write = error writing metadata object file: {$e
 
 codegen_ssa_missing_cpp_build_tool_component = or a necessary component may be missing from the "C++ build tools" workload
 
+codegen_ssa_missing_features = add the missing features in a `target_feature` attribute
+
 codegen_ssa_missing_memory_ordering = Atomic intrinsic missing memory ordering
 
 codegen_ssa_missing_query_depgraph =
@@ -238,6 +240,9 @@ codegen_ssa_stripping_debug_info_failed = stripping debug info with `{$util}` fa
 
 codegen_ssa_symbol_file_write_failure = failed to write symbols file: {$error}
 
+codegen_ssa_target_feature_disable_or_enable =
+    the target features {$features} must all be either enabled or disabled together
+
 codegen_ssa_target_feature_safe_trait = `#[target_feature(..)]` cannot be applied to safe trait method
     .label = cannot be applied to safe trait method
     .label_def = not an `unsafe` function
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index e7b1c63a822..34dc599e4fd 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -1087,7 +1087,9 @@ fn link_natively(
     let strip = sess.opts.cg.strip;
 
     if sess.target.is_like_osx {
-        let stripcmd = "/usr/bin/strip";
+        // Use system `strip` when running on host macOS.
+        // <https://github.com/rust-lang/rust/pull/130781>
+        let stripcmd = if cfg!(target_os = "macos") { "/usr/bin/strip" } else { "strip" };
         match (strip, crate_type) {
             (Strip::Debuginfo, _) => {
                 strip_symbols_with_external_utility(sess, stripcmd, out_filename, Some("-S"))
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index 3f3d305da01..c4bb82d0dd7 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -404,12 +404,14 @@ 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.is_ld {
+            if self.is_cc() {
+                // `-dynamiclib` makes `cc` pass `-dylib` to the linker.
                 self.cc_arg("-dynamiclib");
+            } else {
+                self.link_arg("-dylib");
+                // Clang also sets `-dynamic`, but that's implied by `-dylib`, so unnecessary.
             }
 
-            self.link_arg("-dylib");
-
             // Note that the `osx_rpath_install_name` option here is a hack
             // purely to support bootstrap right now, we should get a more
             // principled solution at some point to force the compiler to pass
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index e22dd8373bd..e3d11cfaf4f 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -2164,8 +2164,14 @@ fn msvc_imps_needed(tcx: TyCtxt<'_>) -> bool {
             && tcx.sess.opts.cg.prefer_dynamic)
     );
 
+    // We need to generate _imp__ symbol if we are generating an rlib or we include one
+    // 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.target.is_like_windows &&
-        tcx.crate_types().iter().any(|ct| *ct == CrateType::Rlib) &&
+    can_have_static_objects   &&
     // ThinLTO can't handle this workaround in all cases, so we don't
     // emit the `__imp_` symbols. Instead we make them unnecessary by disallowing
     // dynamic linking when linker plugin LTO is enabled.
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index 8d634ca4655..d536419ab3c 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -1,5 +1,6 @@
 use rustc_ast::{MetaItemInner, MetaItemKind, ast, attr};
 use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr, list_contains_name};
+use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::codes::*;
 use rustc_errors::{DiagMessage, SubdiagMessage, struct_span_code_err};
 use rustc_hir as hir;
@@ -13,13 +14,13 @@ use rustc_middle::middle::codegen_fn_attrs::{
 use rustc_middle::mir::mono::Linkage;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{self as ty, TyCtxt};
-use rustc_session::lint;
 use rustc_session::parse::feature_err;
+use rustc_session::{Session, lint};
 use rustc_span::symbol::Ident;
 use rustc_span::{Span, sym};
 use rustc_target::spec::{SanitizerSet, abi};
 
-use crate::errors;
+use crate::errors::{self, MissingFeatures, TargetFeatureDisableOrEnable};
 use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature};
 
 fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage {
@@ -662,9 +663,49 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
         }
     }
 
+    if let Some(features) = check_tied_features(
+        tcx.sess,
+        &codegen_fn_attrs
+            .target_features
+            .iter()
+            .map(|features| (features.name.as_str(), true))
+            .collect(),
+    ) {
+        let span = tcx
+            .get_attrs(did, sym::target_feature)
+            .next()
+            .map_or_else(|| tcx.def_span(did), |a| a.span);
+        tcx.dcx()
+            .create_err(TargetFeatureDisableOrEnable {
+                features,
+                span: Some(span),
+                missing_features: Some(MissingFeatures),
+            })
+            .emit();
+    }
+
     codegen_fn_attrs
 }
 
+/// Given a map from target_features to whether they are enabled or disabled, ensure only valid
+/// combinations are allowed.
+pub fn check_tied_features(
+    sess: &Session,
+    features: &FxHashMap<&str, bool>,
+) -> Option<&'static [&'static str]> {
+    if !features.is_empty() {
+        for tied in sess.target.tied_target_features() {
+            // Tied features must be set to the same value, or not set at all
+            let mut tied_iter = tied.iter();
+            let enabled = features.get(tied_iter.next().unwrap());
+            if tied_iter.any(|f| enabled != features.get(f)) {
+                return Some(tied);
+            }
+        }
+    }
+    None
+}
+
 /// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller
 /// applied to the method prototype.
 fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index ab909abcead..d67cf0e3a6d 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -9,7 +9,7 @@ use rustc_errors::codes::*;
 use rustc_errors::{
     Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level,
 };
-use rustc_macros::Diagnostic;
+use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_middle::ty::Ty;
 use rustc_middle::ty::layout::LayoutError;
 use rustc_span::{Span, Symbol};
@@ -1068,3 +1068,27 @@ pub(crate) struct ErrorCreatingImportLibrary<'a> {
     pub lib_name: &'a str,
     pub error: String,
 }
+
+pub struct TargetFeatureDisableOrEnable<'a> {
+    pub features: &'a [&'a str],
+    pub span: Option<Span>,
+    pub missing_features: Option<MissingFeatures>,
+}
+
+#[derive(Subdiagnostic)]
+#[help(codegen_ssa_missing_features)]
+pub struct MissingFeatures;
+
+impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
+        let mut diag = Diag::new(dcx, level, fluent::codegen_ssa_target_feature_disable_or_enable);
+        if let Some(span) = self.span {
+            diag.span(span);
+        };
+        if let Some(missing_features) = self.missing_features {
+            diag.subdiagnostic(missing_features);
+        }
+        diag.arg("features", self.features.join(", "));
+        diag
+    }
+}
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index be9a6d9a90e..e3553dc03e1 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -992,10 +992,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 match op.val {
                     Pair(data_ptr, meta) => {
                         // In the case of Rc<Self>, we need to explicitly pass a
-                        // *mut RcBox<Self> with a Scalar (not ScalarPair) ABI. This is a hack
+                        // *mut RcInner<Self> with a Scalar (not ScalarPair) ABI. This is a hack
                         // that is understood elsewhere in the compiler as a method on
                         // `dyn Trait`.
-                        // To get a `*mut RcBox<Self>`, we just keep unwrapping newtypes until
+                        // To get a `*mut RcInner<Self>`, we just keep unwrapping newtypes until
                         // we get a value of a built-in pointer type.
                         //
                         // This is also relevant for `Pin<&mut Self>`, where we need to peel the
diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs
index 0bcd7d6d081..88ceff327d0 100644
--- a/compiler/rustc_codegen_ssa/src/mir/operand.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs
@@ -3,12 +3,13 @@ use std::fmt;
 
 use arrayvec::ArrayVec;
 use either::Either;
+use rustc_abi as abi;
+use rustc_abi::{Abi, Align, Size};
 use rustc_middle::bug;
 use rustc_middle::mir::interpret::{Pointer, Scalar, alloc_range};
 use rustc_middle::mir::{self, ConstValue};
 use rustc_middle::ty::Ty;
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
-use rustc_target::abi::{self, Abi, Align, Size};
 use tracing::debug;
 
 use super::place::{PlaceRef, PlaceValue};
@@ -207,7 +208,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
             match alloc.0.read_scalar(
                 bx,
                 alloc_range(start, size),
-                /*read_provenance*/ matches!(s.primitive(), abi::Pointer(_)),
+                /*read_provenance*/ matches!(s.primitive(), abi::Primitive::Pointer(_)),
             ) {
                 Ok(val) => bx.scalar_to_backend(val, s, ty),
                 Err(_) => bx.const_poison(ty),
diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs
index 0b764ae7747..a7d5541481a 100644
--- a/compiler/rustc_codegen_ssa/src/mir/place.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/place.rs
@@ -1,10 +1,10 @@
+use rustc_abi::Primitive::{Int, Pointer};
+use rustc_abi::{Align, FieldsShape, Size, TagEncoding, Variants};
 use rustc_middle::mir::tcx::PlaceTy;
 use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
 use rustc_middle::ty::{self, Ty};
 use rustc_middle::{bug, mir};
-use rustc_target::abi::{
-    Align, FieldsShape, Int, Pointer, Size, TagEncoding, VariantIdx, Variants,
-};
+use rustc_target::abi::VariantIdx;
 use tracing::{debug, instrument};
 
 use super::operand::OperandValue;
diff --git a/compiler/rustc_const_eval/Cargo.toml b/compiler/rustc_const_eval/Cargo.toml
index c4f8841d71c..41136019a88 100644
--- a/compiler/rustc_const_eval/Cargo.toml
+++ b/compiler/rustc_const_eval/Cargo.toml
@@ -6,6 +6,7 @@ edition = "2021"
 [dependencies]
 # tidy-alphabetical-start
 either = "1"
+rustc_abi = { path = "../rustc_abi" }
 rustc_apfloat = "0.2.0"
 rustc_ast = { path = "../rustc_ast" }
 rustc_attr = { path = "../rustc_attr" }
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl
index 73a9a1569f6..24dbe688f36 100644
--- a/compiler/rustc_const_eval/messages.ftl
+++ b/compiler/rustc_const_eval/messages.ftl
@@ -134,14 +134,16 @@ const_eval_incompatible_return_types =
 const_eval_incompatible_types =
     calling a function with argument of type {$callee_ty} passing data of type {$caller_ty}
 
-const_eval_interior_mutable_data_refer =
+const_eval_interior_mutable_ref_escaping =
     {const_eval_const_context}s cannot refer to interior mutable data
     .label = this borrow of an interior mutable value may end up in the final value
     .help = to fix this, the value can be extracted to a separate `static` item and then referenced
     .teach_note =
-        A constant containing interior mutable data behind a reference can allow you to modify that data.
-        This would make multiple uses of a constant to be able to see different values and allow circumventing
-        the `Send` and `Sync` requirements for shared mutable data, which is unsound.
+        References that escape into the final value of a constant or static must be immutable.
+        This is to avoid accidentally creating shared mutable state.
+
+
+        If you really want global mutable state, try using an interior mutable `static` or a `static mut`.
 
 const_eval_intern_kind = {$kind ->
     [static] static
@@ -229,6 +231,24 @@ const_eval_modified_global =
 
 const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}
 
+const_eval_mutable_raw_escaping =
+    raw mutable pointers are not allowed in the final value of {const_eval_const_context}s
+    .teach_note =
+        Pointers that escape into the final value of a constant or static must be immutable.
+        This is to avoid accidentally creating shared mutable state.
+
+
+        If you really want global mutable state, try using an interior mutable `static` or a `static mut`.
+
+const_eval_mutable_ref_escaping =
+    mutable references are not allowed in the final value of {const_eval_const_context}s
+    .teach_note =
+        References that escape into the final value of a constant or static must be immutable.
+        This is to avoid accidentally creating shared mutable state.
+
+
+        If you really want global mutable state, try using an interior mutable `static` or a `static mut`.
+
 const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead
 const_eval_non_const_fmt_macro_call =
     cannot call non-const formatting macro in {const_eval_const_context}s
@@ -364,30 +384,11 @@ const_eval_unallowed_fn_pointer_call = function pointer calls are not allowed in
 const_eval_unallowed_heap_allocations =
     allocations are not allowed in {const_eval_const_context}s
     .label = allocation not allowed in {const_eval_const_context}s
-    .teach_note = The value of statics and constants must be known at compile time, and they live for the entire lifetime of a program. Creating a boxed value allocates memory on the heap at runtime, and therefore cannot be done at compile time.
+    .teach_note =
+        The runtime heap is not yet available at compile-time, so no runtime heap allocations can be created.
 
 const_eval_unallowed_inline_asm =
     inline assembly is not allowed in {const_eval_const_context}s
-const_eval_unallowed_mutable_raw =
-    raw mutable pointers are not allowed in the final value of {const_eval_const_context}s
-    .teach_note =
-        References in statics and constants may only refer to immutable values.
-
-
-        Statics are shared everywhere, and if they refer to mutable data one might violate memory
-        safety since holding multiple mutable references to shared data is not allowed.
-
-
-        If you really want global mutable state, try using static mut or a global UnsafeCell.
-
-const_eval_unallowed_mutable_refs =
-    mutable references are not allowed in the final value of {const_eval_const_context}s
-    .teach_note =
-        Statics are shared everywhere, and if they refer to mutable data one might violate memory
-        safety since holding multiple mutable references to shared data is not allowed.
-
-
-        If you really want global mutable state, try using static mut or a global UnsafeCell.
 
 const_eval_unallowed_op_in_const_context =
     {$msg}
diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index 2cbf242fcf2..463a66d4e2e 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -666,6 +666,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                     }
                 }
 
+                // This can be called on stable via the `vec!` macro.
                 if tcx.is_lang_item(callee, LangItem::ExchangeMalloc) {
                     self.check_op(ops::HeapAllocation);
                     return;
diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs
index 6eb33c29e1d..5c4a899f28a 100644
--- a/compiler/rustc_const_eval/src/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/check_consts/ops.rs
@@ -402,7 +402,7 @@ impl<'tcx> NonConstOp<'tcx> for EscapingCellBorrow {
         DiagImportance::Secondary
     }
     fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
-        ccx.dcx().create_err(errors::InteriorMutableDataRefer {
+        ccx.dcx().create_err(errors::InteriorMutableRefEscaping {
             span,
             opt_help: matches!(ccx.const_kind(), hir::ConstContext::Static(_)),
             kind: ccx.const_kind(),
@@ -430,12 +430,12 @@ impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow {
 
     fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
         match self.0 {
-            hir::BorrowKind::Raw => ccx.tcx.dcx().create_err(errors::UnallowedMutableRaw {
+            hir::BorrowKind::Raw => ccx.tcx.dcx().create_err(errors::MutableRawEscaping {
                 span,
                 kind: ccx.const_kind(),
                 teach: ccx.tcx.sess.teach(E0764),
             }),
-            hir::BorrowKind::Ref => ccx.dcx().create_err(errors::UnallowedMutableRefs {
+            hir::BorrowKind::Ref => ccx.dcx().create_err(errors::MutableRefEscaping {
                 span,
                 kind: ccx.const_kind(),
                 teach: ccx.tcx.sess.teach(E0764),
diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs
index eb5024c36f4..74eb6b37fbb 100644
--- a/compiler/rustc_const_eval/src/check_consts/resolver.rs
+++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs
@@ -11,7 +11,7 @@ use rustc_middle::mir::{
     self, BasicBlock, CallReturnPlaces, Local, Location, Statement, StatementKind, TerminatorEdges,
 };
 use rustc_mir_dataflow::fmt::DebugWithContext;
-use rustc_mir_dataflow::{Analysis, AnalysisDomain, JoinSemiLattice};
+use rustc_mir_dataflow::{Analysis, JoinSemiLattice};
 
 use super::{ConstCx, Qualif, qualifs};
 
@@ -310,7 +310,7 @@ impl JoinSemiLattice for State {
     }
 }
 
-impl<'tcx, Q> AnalysisDomain<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
+impl<'tcx, Q> Analysis<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
 where
     Q: Qualif,
 {
@@ -328,12 +328,7 @@ where
     fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut Self::Domain) {
         self.transfer_function(state).initialize_state();
     }
-}
 
-impl<'tcx, Q> Analysis<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
-where
-    Q: Qualif,
-{
     fn apply_statement_effect(
         &mut self,
         state: &mut Self::Domain,
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 4aec74595bc..2db43a0f787 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -140,7 +140,7 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxIndexMap<K, V> {
 
     #[inline(always)]
     fn filter_map_collect<T>(&self, mut f: impl FnMut(&K, &V) -> Option<T>) -> Vec<T> {
-        self.iter().filter_map(move |(k, v)| f(k, &*v)).collect()
+        self.iter().filter_map(move |(k, v)| f(k, v)).collect()
     }
 
     #[inline(always)]
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index c60bacb8506..c943236affc 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -118,8 +118,8 @@ pub(crate) struct UnstableConstFn {
 }
 
 #[derive(Diagnostic)]
-#[diag(const_eval_unallowed_mutable_refs, code = E0764)]
-pub(crate) struct UnallowedMutableRefs {
+#[diag(const_eval_mutable_ref_escaping, code = E0764)]
+pub(crate) struct MutableRefEscaping {
     #[primary_span]
     pub span: Span,
     pub kind: ConstContext,
@@ -128,8 +128,8 @@ pub(crate) struct UnallowedMutableRefs {
 }
 
 #[derive(Diagnostic)]
-#[diag(const_eval_unallowed_mutable_raw, code = E0764)]
-pub(crate) struct UnallowedMutableRaw {
+#[diag(const_eval_mutable_raw_escaping, code = E0764)]
+pub(crate) struct MutableRawEscaping {
     #[primary_span]
     pub span: Span,
     pub kind: ConstContext,
@@ -181,8 +181,8 @@ pub(crate) struct UnallowedInlineAsm {
 }
 
 #[derive(Diagnostic)]
-#[diag(const_eval_interior_mutable_data_refer, code = E0492)]
-pub(crate) struct InteriorMutableDataRefer {
+#[diag(const_eval_interior_mutable_ref_escaping, code = E0492)]
+pub(crate) struct InteriorMutableRefEscaping {
     #[primary_span]
     #[label]
     pub span: Span,
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index 30b5a8d70bc..1def3d08328 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -334,19 +334,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     {
         use rustc_type_ir::TyKind::*;
 
-        fn adjust_nan<
-            'tcx,
-            M: Machine<'tcx>,
-            F1: rustc_apfloat::Float + FloatConvert<F2>,
-            F2: rustc_apfloat::Float,
-        >(
-            ecx: &InterpCx<'tcx, M>,
-            f1: F1,
-            f2: F2,
-        ) -> F2 {
-            if f2.is_nan() { M::generate_nan(ecx, &[f1]) } else { f2 }
-        }
-
         match *dest_ty.kind() {
             // float -> uint
             Uint(t) => {
@@ -367,11 +354,17 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             }
             // float -> float
             Float(fty) => match fty {
-                FloatTy::F16 => Scalar::from_f16(adjust_nan(self, f, f.convert(&mut false).value)),
-                FloatTy::F32 => Scalar::from_f32(adjust_nan(self, f, f.convert(&mut false).value)),
-                FloatTy::F64 => Scalar::from_f64(adjust_nan(self, f, f.convert(&mut false).value)),
+                FloatTy::F16 => {
+                    Scalar::from_f16(self.adjust_nan(f.convert(&mut false).value, &[f]))
+                }
+                FloatTy::F32 => {
+                    Scalar::from_f32(self.adjust_nan(f.convert(&mut false).value, &[f]))
+                }
+                FloatTy::F64 => {
+                    Scalar::from_f64(self.adjust_nan(f.convert(&mut false).value, &[f]))
+                }
                 FloatTy::F128 => {
-                    Scalar::from_f128(adjust_nan(self, f, f.convert(&mut false).value))
+                    Scalar::from_f128(self.adjust_nan(f.convert(&mut false).value, &[f]))
                 }
             },
             // That's it.
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index 5165f95afd5..02dd7821ef6 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -599,6 +599,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     pub fn generate_stacktrace(&self) -> Vec<FrameInfo<'tcx>> {
         Frame::generate_stacktrace_from_stack(self.stack())
     }
+
+    pub fn adjust_nan<F1, F2>(&self, f: F2, inputs: &[F1]) -> F2
+    where
+        F1: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F2>,
+        F2: rustc_apfloat::Float,
+    {
+        if f.is_nan() { M::generate_nan(self, inputs) } else { f }
+    }
 }
 
 #[doc(hidden)]
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 52780cc6a3a..540898ec645 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -4,6 +4,7 @@
 
 use std::assert_matches::assert_matches;
 
+use rustc_apfloat::ieee::{Double, Half, Quad, Single};
 use rustc_hir::def_id::DefId;
 use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
 use rustc_middle::ty::layout::{LayoutOf as _, TyAndLayout, ValidityRequirement};
@@ -438,6 +439,26 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 self.write_scalar(Scalar::from_target_usize(align.bytes(), self), dest)?;
             }
 
+            sym::minnumf16 => self.float_min_intrinsic::<Half>(args, dest)?,
+            sym::minnumf32 => self.float_min_intrinsic::<Single>(args, dest)?,
+            sym::minnumf64 => self.float_min_intrinsic::<Double>(args, dest)?,
+            sym::minnumf128 => self.float_min_intrinsic::<Quad>(args, dest)?,
+
+            sym::maxnumf16 => self.float_max_intrinsic::<Half>(args, dest)?,
+            sym::maxnumf32 => self.float_max_intrinsic::<Single>(args, dest)?,
+            sym::maxnumf64 => self.float_max_intrinsic::<Double>(args, dest)?,
+            sym::maxnumf128 => self.float_max_intrinsic::<Quad>(args, dest)?,
+
+            sym::copysignf16 => self.float_copysign_intrinsic::<Half>(args, dest)?,
+            sym::copysignf32 => self.float_copysign_intrinsic::<Single>(args, dest)?,
+            sym::copysignf64 => self.float_copysign_intrinsic::<Double>(args, dest)?,
+            sym::copysignf128 => self.float_copysign_intrinsic::<Quad>(args, dest)?,
+
+            sym::fabsf16 => self.float_abs_intrinsic::<Half>(args, dest)?,
+            sym::fabsf32 => self.float_abs_intrinsic::<Single>(args, dest)?,
+            sym::fabsf64 => self.float_abs_intrinsic::<Double>(args, dest)?,
+            sym::fabsf128 => self.float_abs_intrinsic::<Quad>(args, dest)?,
+
             // Unsupported intrinsic: skip the return_to_block below.
             _ => return interp_ok(false),
         }
@@ -697,4 +718,63 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         let rhs_bytes = get_bytes(self, rhs)?;
         interp_ok(Scalar::from_bool(lhs_bytes == rhs_bytes))
     }
+
+    fn float_min_intrinsic<F>(
+        &mut self,
+        args: &[OpTy<'tcx, M::Provenance>],
+        dest: &MPlaceTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, ()>
+    where
+        F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
+    {
+        let a: F = self.read_scalar(&args[0])?.to_float()?;
+        let b: F = self.read_scalar(&args[1])?.to_float()?;
+        let res = self.adjust_nan(a.min(b), &[a, b]);
+        self.write_scalar(res, dest)?;
+        interp_ok(())
+    }
+
+    fn float_max_intrinsic<F>(
+        &mut self,
+        args: &[OpTy<'tcx, M::Provenance>],
+        dest: &MPlaceTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, ()>
+    where
+        F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
+    {
+        let a: F = self.read_scalar(&args[0])?.to_float()?;
+        let b: F = self.read_scalar(&args[1])?.to_float()?;
+        let res = self.adjust_nan(a.max(b), &[a, b]);
+        self.write_scalar(res, dest)?;
+        interp_ok(())
+    }
+
+    fn float_copysign_intrinsic<F>(
+        &mut self,
+        args: &[OpTy<'tcx, M::Provenance>],
+        dest: &MPlaceTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, ()>
+    where
+        F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
+    {
+        let a: F = self.read_scalar(&args[0])?.to_float()?;
+        let b: F = self.read_scalar(&args[1])?.to_float()?;
+        // bitwise, no NaN adjustments
+        self.write_scalar(a.copy_sign(b), dest)?;
+        interp_ok(())
+    }
+
+    fn float_abs_intrinsic<F>(
+        &mut self,
+        args: &[OpTy<'tcx, M::Provenance>],
+        dest: &MPlaceTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, ()>
+    where
+        F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
+    {
+        let x: F = self.read_scalar(&args[0])?.to_float()?;
+        // bitwise, no NaN adjustments
+        self.write_scalar(x.abs(), dest)?;
+        interp_ok(())
+    }
 }
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index e6ab8ca12a8..7700eb792ef 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -993,11 +993,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         bytes
     }
 
-    /// Find leaked allocations. Allocations reachable from `static_roots` or a `Global` allocation
-    /// are not considered leaked, as well as leaks whose kind's `may_leak()` returns true.
-    pub fn find_leaked_allocations(
-        &self,
-        static_roots: &[AllocId],
+    /// Find leaked allocations, remove them from memory and return them. Allocations reachable from
+    /// `static_roots` or a `Global` allocation are not considered leaked, as well as leaks whose
+    /// kind's `may_leak()` returns true.
+    ///
+    /// This is highly destructive, no more execution can happen after this!
+    pub fn take_leaked_allocations(
+        &mut self,
+        static_roots: impl FnOnce(&Self) -> &[AllocId],
     ) -> Vec<(AllocId, MemoryKind<M::MemoryKind>, Allocation<M::Provenance, M::AllocExtra, M::Bytes>)>
     {
         // Collect the set of allocations that are *reachable* from `Global` allocations.
@@ -1008,7 +1011,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 self.memory.alloc_map.filter_map_collect(move |&id, &(kind, _)| {
                     if Some(kind) == global_kind { Some(id) } else { None }
                 });
-            todo.extend(static_roots);
+            todo.extend(static_roots(self));
             while let Some(id) = todo.pop() {
                 if reachable.insert(id) {
                     // This is a new allocation, add the allocation it points to `todo`.
@@ -1023,13 +1026,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         };
 
         // All allocations that are *not* `reachable` and *not* `may_leak` are considered leaking.
-        self.memory.alloc_map.filter_map_collect(|id, (kind, alloc)| {
-            if kind.may_leak() || reachable.contains(id) {
-                None
-            } else {
-                Some((*id, *kind, alloc.clone()))
-            }
-        })
+        let leaked: Vec<_> = self.memory.alloc_map.filter_map_collect(|&id, &(kind, _)| {
+            if kind.may_leak() || reachable.contains(&id) { None } else { Some(id) }
+        });
+        let mut result = Vec::new();
+        for &id in leaked.iter() {
+            let (kind, alloc) = self.memory.alloc_map.remove(&id).unwrap();
+            result.push((id, kind, alloc));
+        }
+        result
     }
 
     /// Runs the closure in "validation" mode, which means the machine's memory read hooks will be
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index 3b5af113e99..cd5e2aeca85 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -4,13 +4,14 @@
 use std::assert_matches::assert_matches;
 
 use either::{Either, Left, Right};
+use rustc_abi as abi;
+use rustc_abi::{Abi, HasDataLayout, Size};
 use rustc_hir::def::Namespace;
 use rustc_middle::mir::interpret::ScalarSizeMismatch;
 use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutOf, TyAndLayout};
 use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter};
 use rustc_middle::ty::{ConstInt, ScalarInt, Ty, TyCtxt};
 use rustc_middle::{bug, mir, span_bug, ty};
-use rustc_target::abi::{self, Abi, HasDataLayout, Size};
 use tracing::trace;
 
 use super::{
@@ -117,7 +118,7 @@ impl<Prov: Provenance> Immediate<Prov> {
         match (self, abi) {
             (Immediate::Scalar(scalar), Abi::Scalar(s)) => {
                 assert_eq!(scalar.size(), s.size(cx), "{msg}: scalar value has wrong size");
-                if !matches!(s.primitive(), abi::Pointer(..)) {
+                if !matches!(s.primitive(), abi::Primitive::Pointer(..)) {
                     // This is not a pointer, it should not carry provenance.
                     assert!(
                         matches!(scalar, Scalar::Int(..)),
@@ -131,7 +132,7 @@ impl<Prov: Provenance> Immediate<Prov> {
                     a.size(cx),
                     "{msg}: first component of scalar pair has wrong size"
                 );
-                if !matches!(a.primitive(), abi::Pointer(..)) {
+                if !matches!(a.primitive(), abi::Primitive::Pointer(..)) {
                     assert!(
                         matches!(a_val, Scalar::Int(..)),
                         "{msg}: first component of scalar pair should be an integer, but has provenance"
@@ -142,7 +143,7 @@ impl<Prov: Provenance> Immediate<Prov> {
                     b.size(cx),
                     "{msg}: second component of scalar pair has wrong size"
                 );
-                if !matches!(b.primitive(), abi::Pointer(..)) {
+                if !matches!(b.primitive(), abi::Primitive::Pointer(..)) {
                     assert!(
                         matches!(b_val, Scalar::Int(..)),
                         "{msg}: second component of scalar pair should be an integer, but has provenance"
@@ -572,7 +573,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 assert_eq!(size, mplace.layout.size, "abi::Scalar size does not match layout size");
                 let scalar = alloc.read_scalar(
                     alloc_range(Size::ZERO, size),
-                    /*read_provenance*/ matches!(s, abi::Pointer(_)),
+                    /*read_provenance*/ matches!(s, abi::Primitive::Pointer(_)),
                 )?;
                 Some(ImmTy::from_scalar(scalar, mplace.layout))
             }
@@ -588,11 +589,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields
                 let a_val = alloc.read_scalar(
                     alloc_range(Size::ZERO, a_size),
-                    /*read_provenance*/ matches!(a, abi::Pointer(_)),
+                    /*read_provenance*/ matches!(a, abi::Primitive::Pointer(_)),
                 )?;
                 let b_val = alloc.read_scalar(
                     alloc_range(b_offset, b_size),
-                    /*read_provenance*/ matches!(b, abi::Pointer(_)),
+                    /*read_provenance*/ matches!(b, abi::Primitive::Pointer(_)),
                 )?;
                 Some(ImmTy::from_immediate(Immediate::ScalarPair(a_val, b_val), mplace.layout))
             }
diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs
index 52cd9b898bb..b28ac68ac54 100644
--- a/compiler/rustc_const_eval/src/interpret/operator.rs
+++ b/compiler/rustc_const_eval/src/interpret/operator.rs
@@ -64,8 +64,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         use rustc_middle::mir::BinOp::*;
 
         // Performs appropriate non-deterministic adjustments of NaN results.
-        let adjust_nan =
-            |f: F| -> F { if f.is_nan() { M::generate_nan(self, &[l, r]) } else { f } };
+        let adjust_nan = |f: F| -> F { self.adjust_nan(f, &[l, r]) };
 
         match bin_op {
             Eq => ImmTy::from_bool(l == r, *self.tcx),
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index cbe8a043fba..39e2d3b4ebb 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -1,8 +1,6 @@
 // tidy-alphabetical-start
 #![allow(internal_features)]
 #![allow(rustc::diagnostic_outside_of_impl)]
-#![cfg_attr(not(bootstrap), feature(unqualified_local_imports))]
-#![cfg_attr(not(bootstrap), warn(unqualified_local_imports))]
 #![doc(rust_logo)]
 #![feature(assert_matches)]
 #![feature(box_patterns)]
@@ -15,7 +13,9 @@
 #![feature(strict_provenance)]
 #![feature(trait_alias)]
 #![feature(try_blocks)]
+#![feature(unqualified_local_imports)]
 #![feature(yeet_expr)]
+#![warn(unqualified_local_imports)]
 #![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs
index f225684d99f..fba2707922b 100644
--- a/compiler/rustc_data_structures/src/lib.rs
+++ b/compiler/rustc_data_structures/src/lib.rs
@@ -76,6 +76,7 @@ pub mod svh;
 pub mod sync;
 pub mod tagged_ptr;
 pub mod temp_dir;
+pub mod thinvec;
 pub mod transitive_relation;
 pub mod unhash;
 pub mod unord;
diff --git a/compiler/rustc_data_structures/src/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs
index cfe7dd13e80..34a2464972a 100644
--- a/compiler/rustc_data_structures/src/obligation_forest/mod.rs
+++ b/compiler/rustc_data_structures/src/obligation_forest/mod.rs
@@ -75,6 +75,7 @@ use std::fmt::Debug;
 use std::hash;
 use std::marker::PhantomData;
 
+use thin_vec::ThinVec;
 use tracing::debug;
 
 use crate::fx::{FxHashMap, FxHashSet};
@@ -141,7 +142,7 @@ pub trait ObligationProcessor {
 #[derive(Debug)]
 pub enum ProcessResult<O, E> {
     Unchanged,
-    Changed(Vec<O>),
+    Changed(ThinVec<O>),
     Error(E),
 }
 
@@ -402,9 +403,10 @@ impl<O: ForestObligation> ObligationForest<O> {
     }
 
     /// Returns the set of obligations that are in a pending state.
-    pub fn map_pending_obligations<P, F>(&self, f: F) -> Vec<P>
+    pub fn map_pending_obligations<P, F, R>(&self, f: F) -> R
     where
         F: Fn(&O) -> P,
+        R: FromIterator<P>,
     {
         self.nodes
             .iter()
diff --git a/compiler/rustc_data_structures/src/obligation_forest/tests.rs b/compiler/rustc_data_structures/src/obligation_forest/tests.rs
index 8391e98ba8b..739ef74e3f7 100644
--- a/compiler/rustc_data_structures/src/obligation_forest/tests.rs
+++ b/compiler/rustc_data_structures/src/obligation_forest/tests.rs
@@ -1,5 +1,7 @@
 use std::fmt;
 
+use thin_vec::thin_vec;
+
 use super::*;
 
 impl<'a> super::ForestObligation for &'a str {
@@ -101,9 +103,9 @@ fn push_pop() {
     //        |-> A.3
     let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
         |obligation| match *obligation {
-            "A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
+            "A" => ProcessResult::Changed(thin_vec!["A.1", "A.2", "A.3"]),
             "B" => ProcessResult::Error("B is for broken"),
-            "C" => ProcessResult::Changed(vec![]),
+            "C" => ProcessResult::Changed(thin_vec![]),
             "A.1" | "A.2" | "A.3" => ProcessResult::Unchanged,
             _ => unreachable!(),
         },
@@ -123,8 +125,8 @@ fn push_pop() {
         |obligation| match *obligation {
             "A.1" => ProcessResult::Unchanged,
             "A.2" => ProcessResult::Unchanged,
-            "A.3" => ProcessResult::Changed(vec!["A.3.i"]),
-            "D" => ProcessResult::Changed(vec!["D.1", "D.2"]),
+            "A.3" => ProcessResult::Changed(thin_vec!["A.3.i"]),
+            "D" => ProcessResult::Changed(thin_vec!["D.1", "D.2"]),
             "A.3.i" | "D.1" | "D.2" => ProcessResult::Unchanged,
             _ => unreachable!(),
         },
@@ -139,11 +141,11 @@ fn push_pop() {
     //        |-> D.2 |-> D.2.i
     let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
         |obligation| match *obligation {
-            "A.1" => ProcessResult::Changed(vec![]),
+            "A.1" => ProcessResult::Changed(thin_vec![]),
             "A.2" => ProcessResult::Error("A is for apple"),
-            "A.3.i" => ProcessResult::Changed(vec![]),
-            "D.1" => ProcessResult::Changed(vec!["D.1.i"]),
-            "D.2" => ProcessResult::Changed(vec!["D.2.i"]),
+            "A.3.i" => ProcessResult::Changed(thin_vec![]),
+            "D.1" => ProcessResult::Changed(thin_vec!["D.1.i"]),
+            "D.2" => ProcessResult::Changed(thin_vec!["D.2.i"]),
             "D.1.i" | "D.2.i" => ProcessResult::Unchanged,
             _ => unreachable!(),
         },
@@ -158,7 +160,7 @@ fn push_pop() {
     let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
         |obligation| match *obligation {
             "D.1.i" => ProcessResult::Error("D is for dumb"),
-            "D.2.i" => ProcessResult::Changed(vec![]),
+            "D.2.i" => ProcessResult::Changed(thin_vec![]),
             _ => panic!("unexpected obligation {:?}", obligation),
         },
         |_| {},
@@ -184,10 +186,10 @@ fn success_in_grandchildren() {
 
     let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
         |obligation| match *obligation {
-            "A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
-            "A.1" => ProcessResult::Changed(vec![]),
-            "A.2" => ProcessResult::Changed(vec!["A.2.i", "A.2.ii"]),
-            "A.3" => ProcessResult::Changed(vec![]),
+            "A" => ProcessResult::Changed(thin_vec!["A.1", "A.2", "A.3"]),
+            "A.1" => ProcessResult::Changed(thin_vec![]),
+            "A.2" => ProcessResult::Changed(thin_vec!["A.2.i", "A.2.ii"]),
+            "A.3" => ProcessResult::Changed(thin_vec![]),
             "A.2.i" | "A.2.ii" => ProcessResult::Unchanged,
             _ => unreachable!(),
         },
@@ -201,7 +203,7 @@ fn success_in_grandchildren() {
     let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
         |obligation| match *obligation {
             "A.2.i" => ProcessResult::Unchanged,
-            "A.2.ii" => ProcessResult::Changed(vec![]),
+            "A.2.ii" => ProcessResult::Changed(thin_vec![]),
             _ => unreachable!(),
         },
         |_| {},
@@ -211,7 +213,7 @@ fn success_in_grandchildren() {
 
     let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
         |obligation| match *obligation {
-            "A.2.i" => ProcessResult::Changed(vec!["A.2.i.a"]),
+            "A.2.i" => ProcessResult::Changed(thin_vec!["A.2.i.a"]),
             "A.2.i.a" => ProcessResult::Unchanged,
             _ => unreachable!(),
         },
@@ -222,7 +224,7 @@ fn success_in_grandchildren() {
 
     let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
         |obligation| match *obligation {
-            "A.2.i.a" => ProcessResult::Changed(vec![]),
+            "A.2.i.a" => ProcessResult::Changed(thin_vec![]),
             _ => unreachable!(),
         },
         |_| {},
@@ -247,7 +249,7 @@ fn to_errors_no_throw() {
     forest.register_obligation("A");
     let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
         |obligation| match *obligation {
-            "A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
+            "A" => ProcessResult::Changed(thin_vec!["A.1", "A.2", "A.3"]),
             "A.1" | "A.2" | "A.3" => ProcessResult::Unchanged,
             _ => unreachable!(),
         },
@@ -269,7 +271,7 @@ fn diamond() {
     forest.register_obligation("A");
     let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
         |obligation| match *obligation {
-            "A" => ProcessResult::Changed(vec!["A.1", "A.2"]),
+            "A" => ProcessResult::Changed(thin_vec!["A.1", "A.2"]),
             "A.1" | "A.2" => ProcessResult::Unchanged,
             _ => unreachable!(),
         },
@@ -280,8 +282,8 @@ fn diamond() {
 
     let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
         |obligation| match *obligation {
-            "A.1" => ProcessResult::Changed(vec!["D"]),
-            "A.2" => ProcessResult::Changed(vec!["D"]),
+            "A.1" => ProcessResult::Changed(thin_vec!["D"]),
+            "A.2" => ProcessResult::Changed(thin_vec!["D"]),
             "D" => ProcessResult::Unchanged,
             _ => unreachable!(),
         },
@@ -295,7 +297,7 @@ fn diamond() {
         |obligation| match *obligation {
             "D" => {
                 d_count += 1;
-                ProcessResult::Changed(vec![])
+                ProcessResult::Changed(thin_vec![])
             }
             _ => unreachable!(),
         },
@@ -313,7 +315,7 @@ fn diamond() {
     forest.register_obligation("A'");
     let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
         |obligation| match *obligation {
-            "A'" => ProcessResult::Changed(vec!["A'.1", "A'.2"]),
+            "A'" => ProcessResult::Changed(thin_vec!["A'.1", "A'.2"]),
             "A'.1" | "A'.2" => ProcessResult::Unchanged,
             _ => unreachable!(),
         },
@@ -324,8 +326,8 @@ fn diamond() {
 
     let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
         |obligation| match *obligation {
-            "A'.1" => ProcessResult::Changed(vec!["D'", "A'"]),
-            "A'.2" => ProcessResult::Changed(vec!["D'"]),
+            "A'.1" => ProcessResult::Changed(thin_vec!["D'", "A'"]),
+            "A'.2" => ProcessResult::Changed(thin_vec!["D'"]),
             "D'" | "A'" => ProcessResult::Unchanged,
             _ => unreachable!(),
         },
@@ -366,7 +368,7 @@ fn done_dependency() {
 
     let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
         |obligation| match *obligation {
-            "A: Sized" | "B: Sized" | "C: Sized" => ProcessResult::Changed(vec![]),
+            "A: Sized" | "B: Sized" | "C: Sized" => ProcessResult::Changed(thin_vec![]),
             _ => unreachable!(),
         },
         |_| {},
@@ -379,7 +381,9 @@ fn done_dependency() {
     forest.register_obligation("(A,B,C): Sized");
     let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
         |obligation| match *obligation {
-            "(A,B,C): Sized" => ProcessResult::Changed(vec!["A: Sized", "B: Sized", "C: Sized"]),
+            "(A,B,C): Sized" => {
+                ProcessResult::Changed(thin_vec!["A: Sized", "B: Sized", "C: Sized"])
+            }
             _ => unreachable!(),
         },
         |_| {},
@@ -399,10 +403,10 @@ fn orphan() {
 
     let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
         |obligation| match *obligation {
-            "A" => ProcessResult::Changed(vec!["D", "E"]),
+            "A" => ProcessResult::Changed(thin_vec!["D", "E"]),
             "B" => ProcessResult::Unchanged,
-            "C1" => ProcessResult::Changed(vec![]),
-            "C2" => ProcessResult::Changed(vec![]),
+            "C1" => ProcessResult::Changed(thin_vec![]),
+            "C2" => ProcessResult::Changed(thin_vec![]),
             "D" | "E" => ProcessResult::Unchanged,
             _ => unreachable!(),
         },
@@ -416,7 +420,7 @@ fn orphan() {
     let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
         |obligation| match *obligation {
             "D" | "E" => ProcessResult::Unchanged,
-            "B" => ProcessResult::Changed(vec!["D"]),
+            "B" => ProcessResult::Changed(thin_vec!["D"]),
             _ => unreachable!(),
         },
         |_| {},
@@ -459,7 +463,7 @@ fn simultaneous_register_and_error() {
     let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
         |obligation| match *obligation {
             "A" => ProcessResult::Error("An error"),
-            "B" => ProcessResult::Changed(vec!["A"]),
+            "B" => ProcessResult::Changed(thin_vec!["A"]),
             _ => unreachable!(),
         },
         |_| {},
@@ -474,7 +478,7 @@ fn simultaneous_register_and_error() {
     let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
         |obligation| match *obligation {
             "A" => ProcessResult::Error("An error"),
-            "B" => ProcessResult::Changed(vec!["A"]),
+            "B" => ProcessResult::Changed(thin_vec!["A"]),
             _ => unreachable!(),
         },
         |_| {},
diff --git a/compiler/rustc_data_structures/src/steal.rs b/compiler/rustc_data_structures/src/steal.rs
index aaa95f6b7f1..0d79d98b5d9 100644
--- a/compiler/rustc_data_structures/src/steal.rs
+++ b/compiler/rustc_data_structures/src/steal.rs
@@ -57,7 +57,7 @@ impl<T> Steal<T> {
     ///
     /// This should not be used within rustc as it leaks information not tracked
     /// by the query system, breaking incremental compilation.
-    #[cfg_attr(not(bootstrap), rustc_lint_untracked_query_information)]
+    #[rustc_lint_untracked_query_information]
     pub fn is_stolen(&self) -> bool {
         self.value.borrow().is_none()
     }
diff --git a/compiler/rustc_data_structures/src/thinvec.rs b/compiler/rustc_data_structures/src/thinvec.rs
new file mode 100644
index 00000000000..e60ac2cbc8b
--- /dev/null
+++ b/compiler/rustc_data_structures/src/thinvec.rs
@@ -0,0 +1,92 @@
+//! This is a copy-paste of `Vec::extract_if` for `ThinVec`.
+//!
+//! FIXME: <https://github.com/Gankra/thin-vec/pull/66> is merged, this can be removed.
+
+use std::{ptr, slice};
+
+use thin_vec::ThinVec;
+
+/// An iterator for [`ThinVec`] which uses a closure to determine if an element should be removed.
+#[must_use = "iterators are lazy and do nothing unless consumed"]
+pub struct ExtractIf<'a, T, F> {
+    vec: &'a mut ThinVec<T>,
+    /// The index of the item that will be inspected by the next call to `next`.
+    idx: usize,
+    /// The number of items that have been drained (removed) thus far.
+    del: usize,
+    /// The original length of `vec` prior to draining.
+    old_len: usize,
+    /// The filter test predicate.
+    pred: F,
+}
+
+impl<'a, T, F> ExtractIf<'a, T, F>
+where
+    F: FnMut(&mut T) -> bool,
+{
+    pub fn new(vec: &'a mut ThinVec<T>, filter: F) -> Self {
+        let old_len = vec.len();
+
+        // Guard against us getting leaked (leak amplification)
+        unsafe {
+            vec.set_len(0);
+        }
+
+        ExtractIf { vec, idx: 0, del: 0, old_len, pred: filter }
+    }
+}
+
+impl<T, F> Iterator for ExtractIf<'_, T, F>
+where
+    F: FnMut(&mut T) -> bool,
+{
+    type Item = T;
+    fn next(&mut self) -> Option<Self::Item> {
+        unsafe {
+            while self.idx < self.old_len {
+                let i = self.idx;
+                let v = slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len);
+                let drained = (self.pred)(&mut v[i]);
+                // Update the index *after* the predicate is called. If the index
+                // is updated prior and the predicate panics, the element at this
+                // index would be leaked.
+                self.idx += 1;
+                if drained {
+                    self.del += 1;
+                    return Some(ptr::read(&v[i]));
+                } else if self.del > 0 {
+                    let del = self.del;
+                    let src: *const T = &v[i];
+                    let dst: *mut T = &mut v[i - del];
+                    ptr::copy_nonoverlapping(src, dst, 1);
+                }
+            }
+            None
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (0, Some(self.old_len - self.idx))
+    }
+}
+
+impl<A, F> Drop for ExtractIf<'_, A, F> {
+    fn drop(&mut self) {
+        unsafe {
+            if self.idx < self.old_len && self.del > 0 {
+                // This is a pretty messed up state, and there isn't really an
+                // obviously right thing to do. We don't want to keep trying
+                // to execute `pred`, so we just backshift all the unprocessed
+                // elements and tell the vec that they still exist. The backshift
+                // is required to prevent a double-drop of the last successfully
+                // drained item prior to a panic in the predicate.
+                let ptr = self.vec.as_mut_ptr();
+                let src = ptr.add(self.idx);
+                let dst = src.sub(self.del);
+                let tail_len = self.old_len - self.idx;
+                src.copy_to(dst, tail_len);
+            }
+            self.vec.set_len(self.old_len - self.del);
+        }
+    }
+}
diff --git a/compiler/rustc_data_structures/src/unhash.rs b/compiler/rustc_data_structures/src/unhash.rs
index 48e21a9dab1..0617ef83aad 100644
--- a/compiler/rustc_data_structures/src/unhash.rs
+++ b/compiler/rustc_data_structures/src/unhash.rs
@@ -3,6 +3,7 @@ use std::hash::{BuildHasherDefault, Hasher};
 
 pub type UnhashMap<K, V> = HashMap<K, V, BuildHasherDefault<Unhasher>>;
 pub type UnhashSet<V> = HashSet<V, BuildHasherDefault<Unhasher>>;
+pub type UnindexMap<K, V> = indexmap::IndexMap<K, V, BuildHasherDefault<Unhasher>>;
 
 /// This no-op hasher expects only a single `write_u64` call. It's intended for
 /// map keys that already have hash-like quality, like `Fingerprint`.
diff --git a/compiler/rustc_error_codes/src/error_codes/E0793.md b/compiler/rustc_error_codes/src/error_codes/E0793.md
index b2e51e24e14..ccd1b43bd19 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0793.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0793.md
@@ -1,4 +1,4 @@
-An unaligned references to a field of a [packed] struct got created.
+An unaligned reference to a field of a [packed] struct got created.
 
 Erroneous code example:
 
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index b5945759d43..a673e2e3250 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -143,24 +143,25 @@ impl<'a> ExtCtxt<'a> {
         ast::TraitRef { path, ref_id: ast::DUMMY_NODE_ID }
     }
 
-    pub fn poly_trait_ref(&self, span: Span, path: ast::Path) -> ast::PolyTraitRef {
+    pub fn poly_trait_ref(&self, span: Span, path: ast::Path, is_const: bool) -> ast::PolyTraitRef {
         ast::PolyTraitRef {
             bound_generic_params: ThinVec::new(),
+            modifiers: ast::TraitBoundModifiers {
+                polarity: ast::BoundPolarity::Positive,
+                constness: if is_const {
+                    ast::BoundConstness::Maybe(DUMMY_SP)
+                } else {
+                    ast::BoundConstness::Never
+                },
+                asyncness: ast::BoundAsyncness::Normal,
+            },
             trait_ref: self.trait_ref(path),
             span,
         }
     }
 
     pub fn trait_bound(&self, path: ast::Path, is_const: bool) -> ast::GenericBound {
-        ast::GenericBound::Trait(self.poly_trait_ref(path.span, path), ast::TraitBoundModifiers {
-            polarity: ast::BoundPolarity::Positive,
-            constness: if is_const {
-                ast::BoundConstness::Maybe(DUMMY_SP)
-            } else {
-                ast::BoundConstness::Never
-            },
-            asyncness: ast::BoundAsyncness::Normal,
-        })
+        ast::GenericBound::Trait(self.poly_trait_ref(path.span, path, is_const))
     }
 
     pub fn lifetime(&self, span: Span, ident: Ident) -> ast::Lifetime {
@@ -220,6 +221,10 @@ impl<'a> ExtCtxt<'a> {
         self.stmt_local(local, span)
     }
 
+    pub fn stmt_semi(&self, expr: P<ast::Expr>) -> ast::Stmt {
+        ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Semi(expr) }
+    }
+
     pub fn stmt_local(&self, local: P<ast::Local>, span: Span) -> ast::Stmt {
         ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Let(local), span }
     }
@@ -287,6 +292,25 @@ impl<'a> ExtCtxt<'a> {
         self.expr(sp, ast::ExprKind::Paren(e))
     }
 
+    pub fn expr_method_call(
+        &self,
+        span: Span,
+        expr: P<ast::Expr>,
+        ident: Ident,
+        args: ThinVec<P<ast::Expr>>,
+    ) -> P<ast::Expr> {
+        let seg = ast::PathSegment::from_ident(ident);
+        self.expr(
+            span,
+            ast::ExprKind::MethodCall(Box::new(ast::MethodCall {
+                seg,
+                receiver: expr,
+                args,
+                span,
+            })),
+        )
+    }
+
     pub fn expr_call(
         &self,
         span: Span,
@@ -295,6 +319,12 @@ impl<'a> ExtCtxt<'a> {
     ) -> P<ast::Expr> {
         self.expr(span, ast::ExprKind::Call(expr, args))
     }
+    pub fn expr_loop(&self, sp: Span, block: P<ast::Block>) -> P<ast::Expr> {
+        self.expr(sp, ast::ExprKind::Loop(block, None, sp))
+    }
+    pub fn expr_asm(&self, sp: Span, expr: P<ast::InlineAsm>) -> P<ast::Expr> {
+        self.expr(sp, ast::ExprKind::InlineAsm(expr))
+    }
     pub fn expr_call_ident(
         &self,
         span: Span,
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs
index a850eb95620..0c5fe6e8d8b 100644
--- a/compiler/rustc_feature/src/accepted.rs
+++ b/compiler/rustc_feature/src/accepted.rs
@@ -116,7 +116,7 @@ declare_features! (
     /// Allows calling constructor functions in `const fn`.
     (accepted, const_constructor, "1.40.0", Some(61456)),
     /// Allows the definition of `const extern fn` and `const unsafe extern fn`.
-    (accepted, const_extern_fn, "CURRENT_RUSTC_VERSION", Some(64926)),
+    (accepted, const_extern_fn, "1.83.0", Some(64926)),
     /// Allows basic arithmetic on floating point types in a `const fn`.
     (accepted, const_fn_floating_point_arithmetic, "1.82.0", Some(57241)),
     /// Allows using and casting function pointers in a `const fn`.
@@ -144,15 +144,15 @@ declare_features! (
     /// Allows the use of `loop` and `while` in constants.
     (accepted, const_loop, "1.46.0", Some(52000)),
     /// Allows using `&mut` in constant functions.
-    (accepted, const_mut_refs, "CURRENT_RUSTC_VERSION", Some(57349)),
+    (accepted, const_mut_refs, "1.83.0", Some(57349)),
     /// Allows panicking during const eval (producing compile-time errors).
     (accepted, const_panic, "1.57.0", Some(51999)),
     /// Allows dereferencing raw pointers during const eval.
     (accepted, const_raw_ptr_deref, "1.58.0", Some(51911)),
     /// Allows references to types with interior mutability within constants
-    (accepted, const_refs_to_cell, "CURRENT_RUSTC_VERSION", Some(80384)),
+    (accepted, const_refs_to_cell, "1.83.0", Some(80384)),
     /// Allows creating pointers and references to `static` items in constants.
-    (accepted, const_refs_to_static, "CURRENT_RUSTC_VERSION", Some(119618)),
+    (accepted, const_refs_to_static, "1.83.0", Some(119618)),
     /// Allows implementing `Copy` for closures where possible (RFC 2132).
     (accepted, copy_closures, "1.26.0", Some(44490)),
     /// Allows `crate` in paths.
@@ -190,7 +190,7 @@ declare_features! (
     /// Allows explicit generic arguments specification with `impl Trait` present.
     (accepted, explicit_generic_args_with_impl_trait, "1.63.0", Some(83701)),
     /// Uses 2024 rules for matching `expr` fragments in macros. Also enables `expr_2021` fragment.
-    (accepted, expr_fragment_specifier_2024, "CURRENT_RUSTC_VERSION", Some(123742)),
+    (accepted, expr_fragment_specifier_2024, "1.83.0", Some(123742)),
     /// Allows arbitrary expressions in key-value attributes at parse time.
     (accepted, extended_key_value_attributes, "1.54.0", Some(78835)),
     /// Allows resolving absolute paths as paths from other crates.
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 17827b4e43b..477760a4597 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -752,6 +752,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         template!(NameValueStr: "transparent|semitransparent|opaque"), ErrorFollowing,
         EncodeCrossCrate::Yes, "used internally for testing macro hygiene",
     ),
+    rustc_attr!(
+        rustc_autodiff, Normal,
+        template!(Word, List: r#""...""#), DuplicatesOk,
+        EncodeCrossCrate::No, INTERNAL_UNSTABLE
+    ),
 
     // ==========================================================================
     // Internal attributes, Diagnostics related:
diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs
index 0cd0963d4e3..d797fee000d 100644
--- a/compiler/rustc_feature/src/removed.rs
+++ b/compiler/rustc_feature/src/removed.rs
@@ -154,6 +154,10 @@ declare_features! (
     /// then removed. But there was no utility storing it separately, so now
     /// it's in this list.
     (removed, no_stack_check, "1.0.0", None, None),
+    /// Allows making `dyn Trait` well-formed even if `Trait` is not dyn-compatible (object safe).
+    /// Renamed to `dyn_compatible_for_dispatch`.
+    (removed, object_safe_for_dispatch, "1.83.0", Some(43561),
+     Some("renamed to `dyn_compatible_for_dispatch`")),
     /// Allows using `#[on_unimplemented(..)]` on traits.
     /// (Moved to `rustc_attrs`.)
     (removed, on_unimplemented, "1.40.0", None, None),
@@ -216,6 +220,8 @@ declare_features! (
     (removed, test_removed_feature, "1.0.0", None, None),
     /// Allows using items which are missing stability attributes
     (removed, unmarked_api, "1.0.0", None, None),
+    /// Allows unnamed fields of struct and union type
+    (removed, unnamed_fields, "1.83.0", Some(49804), Some("feature needs redesign")),
     (removed, unsafe_no_drop_flag, "1.0.0", None, None),
     /// Allows `union` fields that don't implement `Copy` as long as they don't have any drop glue.
     (removed, untagged_unions, "1.13.0", Some(55149),
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 380e36fe405..1067156958d 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -228,7 +228,7 @@ declare_features! (
     /// Added for testing unstable lints; perma-unstable.
     (internal, test_unstable_lint, "1.60.0", None),
     /// Helps with formatting for `group_imports = "StdExternalCrate"`.
-    (unstable, unqualified_local_imports, "CURRENT_RUSTC_VERSION", None),
+    (unstable, unqualified_local_imports, "1.83.0", None),
     /// Use for stable + negative coherence and strict coherence depending on trait's
     /// rustc_strict_coherence value.
     (unstable, with_negative_coherence, "1.60.0", None),
@@ -259,6 +259,14 @@ declare_features! (
     (unstable, doc_notable_trait, "1.52.0", Some(45040)),
     /// Allows using the `may_dangle` attribute (RFC 1327).
     (unstable, dropck_eyepatch, "1.10.0", Some(34761)),
+    /// Allows making `dyn Trait` well-formed even if `Trait` is not dyn-compatible[^1].
+    /// In that case, `dyn Trait: Trait` does not hold. Moreover, coercions and
+    /// casts in safe Rust to `dyn Trait` for such a `Trait` is also forbidden.
+    ///
+    /// Renamed from `object_safe_for_dispatch`.
+    ///
+    /// [^1]: Formerly known as "object safe".
+    (unstable, dyn_compatible_for_dispatch, "1.83.0", Some(43561)),
     /// Allows using the `#[fundamental]` attribute.
     (unstable, fundamental, "1.0.0", Some(29635)),
     /// Allows using `#[link_name="llvm.*"]`.
@@ -352,7 +360,7 @@ declare_features! (
     /// Allows inherent and trait methods with arbitrary self types.
     (unstable, arbitrary_self_types, "1.23.0", Some(44874)),
     /// Allows inherent and trait methods with arbitrary self types that are raw pointers.
-    (unstable, arbitrary_self_types_pointers, "CURRENT_RUSTC_VERSION", Some(44874)),
+    (unstable, arbitrary_self_types_pointers, "1.83.0", Some(44874)),
     /// Enables experimental inline assembly support for additional architectures.
     (unstable, asm_experimental_arch, "1.58.0", Some(93335)),
     /// Allows using `label` operands in inline assembly.
@@ -372,7 +380,7 @@ declare_features! (
     /// Allows using C-variadics.
     (unstable, c_variadic, "1.34.0", Some(44930)),
     /// Allows the use of `#[cfg(<true/false>)]`.
-    (unstable, cfg_boolean_literals, "CURRENT_RUSTC_VERSION", Some(131204)),
+    (unstable, cfg_boolean_literals, "1.83.0", Some(131204)),
     /// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour.
     (unstable, cfg_overflow_checks, "1.71.0", Some(111466)),
     /// Provides the relocation model information as cfg entry
@@ -490,7 +498,7 @@ declare_features! (
     /// Allows `if let` guard in match arms.
     (unstable, if_let_guard, "1.47.0", Some(51114)),
     /// Rescoping temporaries in `if let` to align with Rust 2024.
-    (unstable, if_let_rescope, "CURRENT_RUSTC_VERSION", Some(124085)),
+    (unstable, if_let_rescope, "1.83.0", Some(124085)),
     /// Allows `impl Trait` to be used inside associated types (RFC 2515).
     (unstable, impl_trait_in_assoc_type, "1.70.0", Some(63063)),
     /// Allows `impl Trait` as output type in `Fn` traits in return position of functions.
@@ -546,13 +554,6 @@ declare_features! (
     (unstable, non_exhaustive_omitted_patterns_lint, "1.57.0", Some(89554)),
     /// Allows `for<T>` binders in where-clauses
     (incomplete, non_lifetime_binders, "1.69.0", Some(108185)),
-    /// Allows making `dyn Trait` well-formed even if `Trait` is not dyn-compatible[^1].
-    /// In that case, `dyn Trait: Trait` does not hold. Moreover, coercions and
-    /// casts in safe Rust to `dyn Trait` for such a `Trait` is also forbidden.
-    ///
-    /// [^1]: Formerly known as "object safe".
-    // FIXME(dyn_compat_renaming): Rename feature.
-    (unstable, object_safe_for_dispatch, "1.40.0", Some(43561)),
     /// Allows using enums in offset_of!
     (unstable, offset_of_enum, "1.75.0", Some(120141)),
     /// Allows using fields with slice type in offset_of!
@@ -562,9 +563,11 @@ declare_features! (
     /// Allows specifying nop padding on functions for dynamic patching.
     (unstable, patchable_function_entry, "1.81.0", Some(123115)),
     /// Experimental features that make `Pin` more ergonomic.
-    (incomplete, pin_ergonomics, "CURRENT_RUSTC_VERSION", Some(130494)),
+    (incomplete, pin_ergonomics, "1.83.0", Some(130494)),
     /// Allows postfix match `expr.match { ... }`
     (unstable, postfix_match, "1.79.0", Some(121618)),
+    /// Allows `use<..>` precise capturign on impl Trait in traits.
+    (unstable, precise_capturing_in_traits, "1.83.0", Some(130044)),
     /// Allows macro attributes on expressions, statements and non-inline modules.
     (unstable, proc_macro_hygiene, "1.30.0", Some(54727)),
     /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024.
@@ -620,8 +623,6 @@ declare_features! (
     /// Allows creation of instances of a struct by moving fields that have
     /// not changed from prior instances of the same struct (RFC #2528)
     (unstable, type_changing_struct_update, "1.58.0", Some(86555)),
-    /// Allows unnamed fields of struct and union type
-    (incomplete, unnamed_fields, "1.74.0", Some(49804)),
     /// Allows const generic parameters to be defined with types that
     /// are not `Sized`, e.g. `fn foo<const N: [u8]>() {`.
     (incomplete, unsized_const_params, "1.82.0", Some(95174)),
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 2ef6fa53f4e..009c6c4aea5 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1,10 +1,9 @@
 use std::fmt;
 
-use rustc_ast as ast;
 use rustc_ast::util::parser::ExprPrecedence;
 use rustc_ast::{
-    Attribute, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitKind,
-    TraitObjectSyntax, UintTy,
+    self as ast, Attribute, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label,
+    LitKind, TraitObjectSyntax, UintTy,
 };
 pub use rustc_ast::{
     BinOp, BinOpKind, BindingMode, BorrowKind, ByRef, CaptureBy, ImplPolarity, IsAuto, Movability,
@@ -520,7 +519,7 @@ pub enum TraitBoundModifier {
 
 #[derive(Clone, Copy, Debug, HashStable_Generic)]
 pub enum GenericBound<'hir> {
-    Trait(PolyTraitRef<'hir>, TraitBoundModifier),
+    Trait(PolyTraitRef<'hir>),
     Outlives(&'hir Lifetime),
     Use(&'hir [PreciseCapturingArg<'hir>], Span),
 }
@@ -528,7 +527,7 @@ pub enum GenericBound<'hir> {
 impl GenericBound<'_> {
     pub fn trait_ref(&self) -> Option<&TraitRef<'_>> {
         match self {
-            GenericBound::Trait(data, _) => Some(&data.trait_ref),
+            GenericBound::Trait(data) => Some(&data.trait_ref),
             _ => None,
         }
     }
@@ -2874,11 +2873,7 @@ pub enum TyKind<'hir> {
     OpaqueDef(&'hir OpaqueTy<'hir>, &'hir [GenericArg<'hir>]),
     /// A trait object type `Bound1 + Bound2 + Bound3`
     /// where `Bound` is a trait or a lifetime.
-    TraitObject(
-        &'hir [(PolyTraitRef<'hir>, TraitBoundModifier)],
-        &'hir Lifetime,
-        TraitObjectSyntax,
-    ),
+    TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax),
     /// Unused for now.
     Typeof(&'hir AnonConst),
     /// `TyKind::Infer` means the type should be inferred instead of it having been
@@ -3182,6 +3177,11 @@ pub struct PolyTraitRef<'hir> {
     /// The `'a` in `for<'a> Foo<&'a T>`.
     pub bound_generic_params: &'hir [GenericParam<'hir>],
 
+    /// The constness and polarity of the trait ref.
+    ///
+    /// The `async` modifier is lowered directly into a different trait for now.
+    pub modifiers: TraitBoundModifier,
+
     /// The `Foo<&'a T>` in `for<'a> Foo<&'a T>`.
     pub trait_ref: TraitRef<'hir>,
 
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 58916d05865..ffe519b0e7d 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -905,7 +905,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul
             try_visit!(visitor.visit_array_length(length));
         }
         TyKind::TraitObject(bounds, ref lifetime, _syntax) => {
-            for (bound, _modifier) in bounds {
+            for bound in bounds {
                 try_visit!(visitor.visit_poly_trait_ref(bound));
             }
             try_visit!(visitor.visit_lifetime(lifetime));
@@ -1160,7 +1160,7 @@ pub fn walk_param_bound<'v, V: Visitor<'v>>(
     bound: &'v GenericBound<'v>,
 ) -> V::Result {
     match *bound {
-        GenericBound::Trait(ref typ, _modifier) => visitor.visit_poly_trait_ref(typ),
+        GenericBound::Trait(ref typ) => visitor.visit_poly_trait_ref(typ),
         GenericBound::Outlives(ref lifetime) => visitor.visit_lifetime(lifetime),
         GenericBound::Use(args, _) => {
             walk_list!(visitor, visit_precise_capturing_arg, args);
diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs
index c1a4a4497c7..381062b1429 100644
--- a/compiler/rustc_hir/src/lib.rs
+++ b/compiler/rustc_hir/src/lib.rs
@@ -33,6 +33,7 @@ pub mod weak_lang_items;
 #[cfg(test)]
 mod tests;
 
+#[doc(no_inline)]
 pub use hir::*;
 pub use hir_id::*;
 pub use lang_items::{LangItem, LanguageItems};
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index c73826c489f..a9f30ffd6da 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -68,18 +68,21 @@ hir_analysis_closure_implicit_hrtb = implicit types in closure signatures are fo
 hir_analysis_cmse_call_generic =
     function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type
 
-hir_analysis_cmse_call_inputs_stack_spill =
-    arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
+hir_analysis_cmse_entry_generic =
+    functions with the `"C-cmse-nonsecure-entry"` ABI cannot contain generics in their type
+
+hir_analysis_cmse_inputs_stack_spill =
+    arguments for `"{$abi_name}"` function too large to pass via registers
     .label = {$plural ->
         [false] this argument doesn't
         *[true] these arguments don't
     } fit in the available registers
-    .note = functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
+    .note = functions with the `"{$abi_name}"` ABI must pass all their arguments via the 4 32-bit available argument registers
 
-hir_analysis_cmse_call_output_stack_spill =
-    return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
+hir_analysis_cmse_output_stack_spill =
+    return value of `"{$abi_name}"` function too large to pass via registers
     .label = this type doesn't fit in the available registers
-    .note1 = functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+    .note1 = functions with the `"{$abi_name}"` ABI must pass their result via the available return registers
     .note2 = the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
 hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures
@@ -248,8 +251,6 @@ hir_analysis_invalid_union_field =
 hir_analysis_invalid_union_field_sugg =
     wrap the field type in `ManuallyDrop<...>`
 
-hir_analysis_invalid_unnamed_field_ty = unnamed fields can only have struct or union types
-
 hir_analysis_late_bound_const_in_apit = `impl Trait` can only mention const parameters from an fn or impl
     .label = const parameter declared here
 
@@ -259,6 +260,9 @@ hir_analysis_late_bound_lifetime_in_apit = `impl Trait` can only mention lifetim
 hir_analysis_late_bound_type_in_apit = `impl Trait` can only mention type parameters from an fn or impl
     .label = type parameter declared here
 
+hir_analysis_lifetime_implicitly_captured = `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
+    .param_label = all lifetime parameters originating from a trait are captured implicitly
+
 hir_analysis_lifetime_must_be_first = lifetime parameter `{$name}` must be listed before non-lifetime parameters
     .label = move the lifetime before this parameter
 
@@ -535,19 +539,6 @@ hir_analysis_unconstrained_generic_parameter = the {$param_def_kind} `{$param_na
 hir_analysis_unconstrained_opaque_type = unconstrained opaque type
     .note = `{$name}` must be used in combination with a concrete type within the same {$what}
 
-hir_analysis_unnamed_fields_repr_field_defined = unnamed field defined here
-
-hir_analysis_unnamed_fields_repr_field_missing_repr_c =
-    named type of unnamed field must have `#[repr(C)]` representation
-    .label = unnamed field defined here
-    .field_ty_label = `{$field_ty}` defined here
-    .suggestion = add `#[repr(C)]` to this {$field_adt_kind}
-
-hir_analysis_unnamed_fields_repr_missing_repr_c =
-    {$adt_kind} with unnamed fields must have `#[repr(C)]` representation
-    .label = {$adt_kind} `{$adt_name}` defined here
-    .suggestion = add `#[repr(C)]` to this {$adt_kind}
-
 hir_analysis_unrecognized_atomic_operation =
     unrecognized atomic operation function: `{$op}`
     .label = unrecognized atomic operation
diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs
index 8d11328743c..5a66c31a0cc 100644
--- a/compiler/rustc_hir_analysis/src/autoderef.rs
+++ b/compiler/rustc_hir_analysis/src/autoderef.rs
@@ -1,4 +1,5 @@
 use rustc_infer::infer::InferCtxt;
+use rustc_infer::traits::PredicateObligations;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
 use rustc_session::Limit;
 use rustc_span::Span;
@@ -23,7 +24,7 @@ struct AutoderefSnapshot<'tcx> {
     reached_recursion_limit: bool,
     steps: Vec<(Ty<'tcx>, AutoderefKind)>,
     cur_ty: Ty<'tcx>,
-    obligations: Vec<traits::PredicateObligation<'tcx>>,
+    obligations: PredicateObligations<'tcx>,
 }
 
 pub struct Autoderef<'a, 'tcx> {
@@ -119,7 +120,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
             state: AutoderefSnapshot {
                 steps: vec![],
                 cur_ty: infcx.resolve_vars_if_possible(base_ty),
-                obligations: vec![],
+                obligations: PredicateObligations::new(),
                 at_start: true,
                 reached_recursion_limit: false,
             },
@@ -165,7 +166,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
     pub fn structurally_normalize(
         &self,
         ty: Ty<'tcx>,
-    ) -> Option<(Ty<'tcx>, Vec<traits::PredicateObligation<'tcx>>)> {
+    ) -> Option<(Ty<'tcx>, PredicateObligations<'tcx>)> {
         let ocx = ObligationCtxt::new(self.infcx);
         let Ok(normalized_ty) = ocx.structurally_normalize(
             &traits::ObligationCause::misc(self.span, self.body_id),
@@ -204,11 +205,11 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
         self.state.steps.len()
     }
 
-    pub fn into_obligations(self) -> Vec<traits::PredicateObligation<'tcx>> {
+    pub fn into_obligations(self) -> PredicateObligations<'tcx> {
         self.state.obligations
     }
 
-    pub fn current_obligations(&self) -> Vec<traits::PredicateObligation<'tcx>> {
+    pub fn current_obligations(&self) -> PredicateObligations<'tcx> {
         self.state.obligations.clone()
     }
 
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index eb62ff86c71..94da3d4ea84 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -8,7 +8,9 @@ use rustc_hir::Node;
 use rustc_hir::def::{CtorKind, DefKind};
 use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
 use rustc_infer::traits::Obligation;
-use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
+use rustc_lint_defs::builtin::{
+    REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS,
+};
 use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
 use rustc_middle::middle::stability::EvalResult;
 use rustc_middle::span_bug;
@@ -52,16 +54,18 @@ pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) {
             });
         }
     }
+}
 
-    // This ABI is only allowed on function pointers
-    if abi == Abi::CCmseNonSecureCall {
-        struct_span_code_err!(
-            tcx.dcx(),
-            span,
-            E0781,
-            "the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers"
-        )
-        .emit();
+pub fn check_abi_fn_ptr(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) {
+    match tcx.sess.target.is_abi_supported(abi) {
+        Some(true) => (),
+        Some(false) | None => {
+            tcx.node_span_lint(UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS, hir_id, span, |lint| {
+                lint.primary_message(format!(
+                    "the calling convention {abi} is not supported on this target"
+                ));
+            });
+        }
     }
 }
 
@@ -76,7 +80,6 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) {
 
     check_transparent(tcx, def);
     check_packed(tcx, span, def);
-    check_unnamed_fields(tcx, def);
 }
 
 fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
@@ -86,61 +89,6 @@ fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     check_transparent(tcx, def);
     check_union_fields(tcx, span, def_id);
     check_packed(tcx, span, def);
-    check_unnamed_fields(tcx, def);
-}
-
-/// Check the representation of adts with unnamed fields.
-fn check_unnamed_fields(tcx: TyCtxt<'_>, def: ty::AdtDef<'_>) {
-    if def.is_enum() {
-        return;
-    }
-    let variant = def.non_enum_variant();
-    if !variant.has_unnamed_fields() {
-        return;
-    }
-    if !def.is_anonymous() {
-        let adt_kind = def.descr();
-        let span = tcx.def_span(def.did());
-        let unnamed_fields = variant
-            .fields
-            .iter()
-            .filter(|f| f.is_unnamed())
-            .map(|f| {
-                let span = tcx.def_span(f.did);
-                errors::UnnamedFieldsReprFieldDefined { span }
-            })
-            .collect::<Vec<_>>();
-        debug_assert_ne!(unnamed_fields.len(), 0, "expect unnamed fields in this adt");
-        let adt_name = tcx.item_name(def.did());
-        if !def.repr().c() {
-            tcx.dcx().emit_err(errors::UnnamedFieldsRepr::MissingReprC {
-                span,
-                adt_kind,
-                adt_name,
-                unnamed_fields,
-                sugg_span: span.shrink_to_lo(),
-            });
-        }
-    }
-    for field in variant.fields.iter().filter(|f| f.is_unnamed()) {
-        let field_ty = tcx.type_of(field.did).instantiate_identity();
-        if let Some(adt) = field_ty.ty_adt_def()
-            && !adt.is_enum()
-        {
-            if !adt.is_anonymous() && !adt.repr().c() {
-                let field_ty_span = tcx.def_span(adt.did());
-                tcx.dcx().emit_err(errors::UnnamedFieldsRepr::FieldMissingReprC {
-                    span: tcx.def_span(field.did),
-                    field_ty_span,
-                    field_ty,
-                    field_adt_kind: adt.descr(),
-                    sugg_span: field_ty_span.shrink_to_lo(),
-                });
-            }
-        } else {
-            tcx.dcx().emit_err(errors::InvalidUnnamedFieldTy { span: tcx.def_span(field.did) });
-        }
-    }
 }
 
 /// Check that the fields of the `union` do not need dropping.
@@ -589,15 +537,22 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe
                                 param_span: tcx.def_span(def_id),
                             });
                         } else {
-                            // If the `use_span` is actually just the param itself, then we must
-                            // have not duplicated the lifetime but captured the original.
-                            // The "effective" `use_span` will be the span of the opaque itself,
-                            // and the param span will be the def span of the param.
-                            tcx.dcx().emit_err(errors::LifetimeNotCaptured {
-                                opaque_span,
-                                use_span: opaque_span,
-                                param_span: use_span,
-                            });
+                            if tcx.def_kind(tcx.parent(param.def_id)) == DefKind::Trait {
+                                tcx.dcx().emit_err(errors::LifetimeImplicitlyCaptured {
+                                    opaque_span,
+                                    param_span: tcx.def_span(param.def_id),
+                                });
+                            } else {
+                                // If the `use_span` is actually just the param itself, then we must
+                                // have not duplicated the lifetime but captured the original.
+                                // The "effective" `use_span` will be the span of the opaque itself,
+                                // and the param span will be the def span of the param.
+                                tcx.dcx().emit_err(errors::LifetimeNotCaptured {
+                                    opaque_span,
+                                    use_span: opaque_span,
+                                    param_span: use_span,
+                                });
+                            }
                         }
                         continue;
                     }
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 9ca5f25447b..75956165e87 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -592,14 +592,13 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
                 &cause,
                 hir.get_if_local(impl_m.def_id)
                     .and_then(|node| node.fn_decl())
-                    .map(|decl| (decl.output.span(), Cow::from("return type in trait"))),
+                    .map(|decl| (decl.output.span(), Cow::from("return type in trait"), false)),
                 Some(infer::ValuePairs::Terms(ExpectedFound {
                     expected: trait_return_ty.into(),
                     found: impl_return_ty.into(),
                 })),
                 terr,
                 false,
-                false,
             );
             return Err(diag.emit());
         }
@@ -1018,14 +1017,13 @@ fn report_trait_method_mismatch<'tcx>(
     infcx.err_ctxt().note_type_err(
         &mut diag,
         &cause,
-        trait_err_span.map(|sp| (sp, Cow::from("type in trait"))),
+        trait_err_span.map(|sp| (sp, Cow::from("type in trait"), false)),
         Some(infer::ValuePairs::PolySigs(ExpectedFound {
             expected: ty::Binder::dummy(trait_sig),
             found: ty::Binder::dummy(impl_sig),
         })),
         terr,
         false,
-        false,
     );
 
     diag.emit()
@@ -1825,14 +1823,13 @@ fn compare_const_predicate_entailment<'tcx>(
         infcx.err_ctxt().note_type_err(
             &mut diag,
             &cause,
-            trait_c_span.map(|span| (span, Cow::from("type in trait"))),
+            trait_c_span.map(|span| (span, Cow::from("type in trait"), false)),
             Some(infer::ValuePairs::Terms(ExpectedFound {
                 expected: trait_ty.into(),
                 found: impl_ty.into(),
             })),
             terr,
             false,
-            false,
         );
         return Err(diag.emit());
     };
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
index 80334c6efe7..2d6b9813271 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
@@ -64,6 +64,10 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
         return;
     };
 
+    if hidden_tys.items().any(|(_, &ty)| ty.skip_binder().references_error()) {
+        return;
+    }
+
     let mut collector = ImplTraitInTraitCollector { tcx, types: FxIndexSet::default() };
     trait_m_sig.visit_with(&mut collector);
 
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index 25e219ef3f2..06317a3b304 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -357,6 +357,19 @@ pub fn check_intrinsic_type(
                 (0, 0, vec![tcx.types.f128, tcx.types.f128, tcx.types.f128], tcx.types.f128)
             }
 
+            sym::fmuladdf16 => {
+                (0, 0, vec![tcx.types.f16, tcx.types.f16, tcx.types.f16], tcx.types.f16)
+            }
+            sym::fmuladdf32 => {
+                (0, 0, vec![tcx.types.f32, tcx.types.f32, tcx.types.f32], tcx.types.f32)
+            }
+            sym::fmuladdf64 => {
+                (0, 0, vec![tcx.types.f64, tcx.types.f64, tcx.types.f64], tcx.types.f64)
+            }
+            sym::fmuladdf128 => {
+                (0, 0, vec![tcx.types.f128, tcx.types.f128, tcx.types.f128], tcx.types.f128)
+            }
+
             sym::fabsf16 => (0, 0, vec![tcx.types.f16], tcx.types.f16),
             sym::fabsf32 => (0, 0, vec![tcx.types.f32], tcx.types.f32),
             sym::fabsf64 => (0, 0, vec![tcx.types.f64], tcx.types.f64),
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index d3d88919d87..959b17b2d40 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -73,7 +73,7 @@ pub mod wfcheck;
 
 use std::num::NonZero;
 
-pub use check::check_abi;
+pub use check::{check_abi, check_abi_fn_ptr};
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_errors::{Diag, ErrorGuaranteed, pluralize, struct_span_code_err};
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -652,7 +652,6 @@ pub fn check_function_signature<'tcx>(
                 })),
                 err,
                 false,
-                false,
             );
             return Err(diag.emit());
         }
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 3a9d2640eee..f788456d4e9 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -531,7 +531,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) {
         debug!(?required_bounds);
         let param_env = tcx.param_env(gat_def_id);
 
-        let mut unsatisfied_bounds: Vec<_> = required_bounds
+        let unsatisfied_bounds: Vec<_> = required_bounds
             .into_iter()
             .filter(|clause| match clause.kind().skip_binder() {
                 ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => {
@@ -552,9 +552,6 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) {
             .map(|clause| clause.to_string())
             .collect();
 
-        // We sort so that order is predictable
-        unsatisfied_bounds.sort();
-
         if !unsatisfied_bounds.is_empty() {
             let plural = pluralize!(unsatisfied_bounds.len());
             let suggestion = format!(
@@ -832,7 +829,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GATArgsCollector<'tcx> {
 
 fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool {
     match ty.kind {
-        hir::TyKind::TraitObject([(trait_ref, _)], ..) => match trait_ref.trait_ref.path.segments {
+        hir::TyKind::TraitObject([trait_ref], ..) => match trait_ref.trait_ref.path.segments {
             [s] => s.res.opt_def_id() == Some(trait_def_id.to_def_id()),
             _ => false,
         },
@@ -1878,24 +1875,15 @@ fn check_variances_for_type_defn<'tcx>(
     item: &'tcx hir::Item<'tcx>,
     hir_generics: &hir::Generics<'tcx>,
 ) {
-    let identity_args = ty::GenericArgs::identity_for_item(tcx, item.owner_id);
-
     match item.kind {
         ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) => {
-            for field in tcx.adt_def(item.owner_id).all_fields() {
-                if field.ty(tcx, identity_args).references_error() {
-                    return;
-                }
-            }
+            // Ok
         }
         ItemKind::TyAlias(..) => {
             assert!(
                 tcx.type_alias_is_lazy(item.owner_id),
                 "should not be computing variance of non-weak type alias"
             );
-            if tcx.type_of(item.owner_id).skip_binder().references_error() {
-                return;
-            }
         }
         kind => span_bug!(item.span, "cannot compute the variances of {kind:?}"),
     }
@@ -1958,6 +1946,15 @@ fn check_variances_for_type_defn<'tcx>(
             continue;
         }
 
+        // Look for `ErrorGuaranteed` deeply within this type.
+        if let ControlFlow::Break(ErrorGuaranteed { .. }) = tcx
+            .type_of(item.owner_id)
+            .instantiate_identity()
+            .visit_with(&mut HasErrorDeep { tcx, seen: Default::default() })
+        {
+            continue;
+        }
+
         match hir_param.name {
             hir::ParamName::Error => {}
             _ => {
@@ -1968,6 +1965,46 @@ fn check_variances_for_type_defn<'tcx>(
     }
 }
 
+/// Look for `ErrorGuaranteed` deeply within structs' (unsubstituted) fields.
+struct HasErrorDeep<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    seen: FxHashSet<DefId>,
+}
+impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasErrorDeep<'tcx> {
+    type Result = ControlFlow<ErrorGuaranteed>;
+
+    fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
+        match *ty.kind() {
+            ty::Adt(def, _) => {
+                if self.seen.insert(def.did()) {
+                    for field in def.all_fields() {
+                        self.tcx.type_of(field.did).instantiate_identity().visit_with(self)?;
+                    }
+                }
+            }
+            ty::Error(guar) => return ControlFlow::Break(guar),
+            _ => {}
+        }
+        ty.super_visit_with(self)
+    }
+
+    fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
+        if let Err(guar) = r.error_reported() {
+            ControlFlow::Break(guar)
+        } else {
+            ControlFlow::Continue(())
+        }
+    }
+
+    fn visit_const(&mut self, c: ty::Const<'tcx>) -> Self::Result {
+        if let Err(guar) = c.error_reported() {
+            ControlFlow::Break(guar)
+        } else {
+            ControlFlow::Continue(())
+        }
+    }
+}
+
 fn report_bivariance<'tcx>(
     tcx: TyCtxt<'tcx>,
     param: &'tcx hir::GenericParam<'tcx>,
diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs
index d1c888a185e..5127e73d978 100644
--- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs
@@ -319,9 +319,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
             // List of connected regions is built. Now, run the overlap check
             // for each pair of impl blocks in the same connected region.
             for region in connected_regions.into_iter().flatten() {
-                let mut impl_blocks =
-                    region.impl_blocks.into_iter().collect::<SmallVec<[usize; 8]>>();
-                impl_blocks.sort_unstable();
+                let impl_blocks = region.impl_blocks.into_iter().collect::<SmallVec<[usize; 8]>>();
                 for (i, &impl1_items_idx) in impl_blocks.iter().enumerate() {
                     let &(&impl1_def_id, impl_items1) = &impls_items[impl1_items_idx];
                     res = res.and(self.check_for_duplicate_items_in_impl(impl1_def_id));
diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs
index 69d36426447..eea5a16ac6f 100644
--- a/compiler/rustc_hir_analysis/src/coherence/mod.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs
@@ -199,7 +199,7 @@ fn check_object_overlap<'tcx>(
         for component_def_id in component_def_ids {
             if !tcx.is_dyn_compatible(component_def_id) {
                 // FIXME(dyn_compat_renaming): Rename test and update comment.
-                // Without the 'object_safe_for_dispatch' feature this is an error
+                // Without the 'dyn_compatible_for_dispatch' feature this is an error
                 // which will be reported by wfcheck. Ignore it here.
                 // This is tested by `coherence-impl-trait-for-trait-object-safe.rs`.
                 // With the feature enabled, the trait is not implemented automatically,
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index 640907c3e4a..f63e2d40e39 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -1007,79 +1007,6 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> {
             }
         }
     }
-
-    /// Check the uniqueness of fields across adt where there are
-    /// nested fields imported from an unnamed field.
-    fn check_field_in_nested_adt(&mut self, adt_def: ty::AdtDef<'_>, unnamed_field_span: Span) {
-        for field in adt_def.all_fields() {
-            if field.is_unnamed() {
-                // Here we don't care about the generic parameters, so `instantiate_identity` is enough.
-                match self.tcx.type_of(field.did).instantiate_identity().kind() {
-                    ty::Adt(adt_def, _) => {
-                        self.check_field_in_nested_adt(*adt_def, unnamed_field_span);
-                    }
-                    ty_kind => span_bug!(
-                        self.tcx.def_span(field.did),
-                        "Unexpected TyKind in FieldUniquenessCheckContext::check_field_in_nested_adt(): {ty_kind:?}"
-                    ),
-                }
-            } else {
-                self.check_field_decl(
-                    field.ident(self.tcx),
-                    NestedSpan {
-                        span: unnamed_field_span,
-                        nested_field_span: self.tcx.def_span(field.did),
-                    }
-                    .into(),
-                );
-            }
-        }
-    }
-
-    /// Check the uniqueness of fields in a struct variant, and recursively
-    /// check the nested fields if it is an unnamed field with type of an
-    /// anonymous adt.
-    fn check_field(&mut self, field: &hir::FieldDef<'_>) {
-        if field.ident.name != kw::Underscore {
-            self.check_field_decl(field.ident, field.span.into());
-            return;
-        }
-        match &field.ty.kind {
-            hir::TyKind::AnonAdt(item_id) => {
-                match &self.tcx.hir_node(item_id.hir_id()).expect_item().kind {
-                    hir::ItemKind::Struct(variant_data, ..)
-                    | hir::ItemKind::Union(variant_data, ..) => {
-                        variant_data.fields().iter().for_each(|f| self.check_field(f));
-                    }
-                    item_kind => span_bug!(
-                        field.ty.span,
-                        "Unexpected ItemKind in FieldUniquenessCheckContext::check_field(): {item_kind:?}"
-                    ),
-                }
-            }
-            hir::TyKind::Path(hir::QPath::Resolved(_, hir::Path { res, .. })) => {
-                // If this is a direct path to an ADT, we can check it
-                // If this is a type alias or non-ADT, `check_unnamed_fields` should verify it
-                if let Some(def_id) = res.opt_def_id()
-                    && let Some(local) = def_id.as_local()
-                    && let Node::Item(item) = self.tcx.hir_node_by_def_id(local)
-                    && item.is_adt()
-                {
-                    self.check_field_in_nested_adt(self.tcx.adt_def(def_id), field.span);
-                }
-            }
-            // Abort due to errors (there must be an error if an unnamed field
-            //  has any type kind other than an anonymous adt or a named adt)
-            ty_kind => {
-                self.tcx.dcx().span_delayed_bug(
-                    field.ty.span,
-                    format!("Unexpected TyKind in FieldUniquenessCheckContext::check_field(): {ty_kind:?}"),
-                );
-                // FIXME: errors during AST validation should abort the compilation before reaching here.
-                self.tcx.dcx().abort_if_errors();
-            }
-        }
-    }
 }
 
 fn lower_variant(
@@ -1090,20 +1017,13 @@ fn lower_variant(
     def: &hir::VariantData<'_>,
     adt_kind: ty::AdtKind,
     parent_did: LocalDefId,
-    is_anonymous: bool,
 ) -> ty::VariantDef {
-    let mut has_unnamed_fields = false;
     let mut field_uniqueness_check_ctx = FieldUniquenessCheckContext::new(tcx);
     let fields = def
         .fields()
         .iter()
-        .inspect(|f| {
-            has_unnamed_fields |= f.ident.name == kw::Underscore;
-            // We only check named ADT here because anonymous ADTs are checked inside
-            // the named ADT in which they are defined.
-            if !is_anonymous {
-                field_uniqueness_check_ctx.check_field(f);
-            }
+        .inspect(|field| {
+            field_uniqueness_check_ctx.check_field_decl(field.ident, field.span.into());
         })
         .map(|f| ty::FieldDef {
             did: f.def_id.to_def_id(),
@@ -1127,7 +1047,6 @@ fn lower_variant(
         adt_kind == AdtKind::Struct && tcx.has_attr(parent_did, sym::non_exhaustive)
             || variant_did
                 .is_some_and(|variant_did| tcx.has_attr(variant_did, sym::non_exhaustive)),
-        has_unnamed_fields,
     )
 }
 
@@ -1138,20 +1057,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
         bug!("expected ADT to be an item");
     };
 
-    let is_anonymous = item.ident.name == kw::Empty;
-    let repr = if is_anonymous {
-        let parent = tcx.local_parent(def_id);
-        if let Node::Item(item) = tcx.hir_node_by_def_id(parent)
-            && item.is_struct_or_union()
-        {
-            tcx.adt_def(parent).repr()
-        } else {
-            tcx.dcx().span_delayed_bug(item.span, "anonymous field inside non struct/union");
-            ty::ReprOptions::default()
-        }
-    } else {
-        tcx.repr_options_of_def(def_id)
-    };
+    let repr = tcx.repr_options_of_def(def_id);
     let (kind, variants) = match &item.kind {
         ItemKind::Enum(def, _) => {
             let mut distance_from_explicit = 0;
@@ -1175,7 +1081,6 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
                         &v.data,
                         AdtKind::Enum,
                         def_id,
-                        is_anonymous,
                     )
                 })
                 .collect();
@@ -1195,7 +1100,6 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
                 def,
                 adt_kind,
                 def_id,
-                is_anonymous,
             ))
             .collect();
 
@@ -1203,7 +1107,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
         }
         _ => bug!("{:?} is not an ADT", item.owner_id.def_id),
     };
-    tcx.mk_adt_def(def_id.to_def_id(), kind, variants, repr, is_anonymous)
+    tcx.mk_adt_def(def_id.to_def_id(), kind, variants, repr)
 }
 
 fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index 6d30f7c7b9d..a87b29b3093 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -866,7 +866,7 @@ impl<'tcx> ItemCtxt<'tcx> {
     #[instrument(level = "trace", skip(self))]
     fn bound_defines_assoc_item(&self, b: &hir::GenericBound<'_>, assoc_name: Ident) -> bool {
         match b {
-            hir::GenericBound::Trait(poly_trait_ref, _) => {
+            hir::GenericBound::Trait(poly_trait_ref) => {
                 let trait_ref = &poly_trait_ref.trait_ref;
                 if let Some(trait_did) = trait_ref.trait_def_id() {
                     self.tcx.trait_may_define_assoc_item(trait_did, assoc_name)
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 c8852a3a369..cb7f0901c7e 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -644,7 +644,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                 debug!(?bounds, ?lifetime, "TraitObject");
                 let scope = Scope::TraitRefBoundary { s: self.scope };
                 self.with(scope, |this| {
-                    for (bound, _) in bounds {
+                    for bound in bounds {
                         this.visit_poly_trait_ref_inner(
                             bound,
                             NonLifetimeBinderAllowed::Deny("trait object types"),
@@ -1918,7 +1918,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                                 return None;
                             }
                             predicate.bounds.iter().find_map(|bound| {
-                                let hir::GenericBound::Trait(trait_, _) = bound else {
+                                let hir::GenericBound::Trait(trait_) = bound else {
                                     return None;
                                 };
                                 BoundVarContext::supertrait_hrtb_vars(
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 4edb68e6199..af4445a7fd4 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -735,13 +735,6 @@ pub(crate) struct InvalidUnionField {
 }
 
 #[derive(Diagnostic)]
-#[diag(hir_analysis_invalid_unnamed_field_ty)]
-pub(crate) struct InvalidUnnamedFieldTy {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(hir_analysis_return_type_notation_on_non_rpitit)]
 pub(crate) struct ReturnTypeNotationOnNonRpitit<'tcx> {
     #[primary_span]
@@ -1599,41 +1592,6 @@ pub(crate) struct UnconstrainedGenericParameter {
 }
 
 #[derive(Diagnostic)]
-pub(crate) enum UnnamedFieldsRepr<'a> {
-    #[diag(hir_analysis_unnamed_fields_repr_missing_repr_c)]
-    MissingReprC {
-        #[primary_span]
-        #[label]
-        span: Span,
-        adt_kind: &'static str,
-        adt_name: Symbol,
-        #[subdiagnostic]
-        unnamed_fields: Vec<UnnamedFieldsReprFieldDefined>,
-        #[suggestion(code = "#[repr(C)]\n")]
-        sugg_span: Span,
-    },
-    #[diag(hir_analysis_unnamed_fields_repr_field_missing_repr_c)]
-    FieldMissingReprC {
-        #[primary_span]
-        #[label]
-        span: Span,
-        #[label(hir_analysis_field_ty_label)]
-        field_ty_span: Span,
-        field_ty: Ty<'a>,
-        field_adt_kind: &'static str,
-        #[suggestion(code = "#[repr(C)]\n")]
-        sugg_span: Span,
-    },
-}
-
-#[derive(Subdiagnostic)]
-#[note(hir_analysis_unnamed_fields_repr_field_defined)]
-pub(crate) struct UnnamedFieldsReprFieldDefined {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(hir_analysis_opaque_captures_higher_ranked_lifetime, code = E0657)]
 pub(crate) struct OpaqueCapturesHigherRankedLifetime {
     #[primary_span]
@@ -1669,23 +1627,25 @@ pub(crate) struct InvalidReceiverTy<'tcx> {
 pub(crate) struct EffectsWithoutNextSolver;
 
 #[derive(Diagnostic)]
-#[diag(hir_analysis_cmse_call_inputs_stack_spill, code = E0798)]
+#[diag(hir_analysis_cmse_inputs_stack_spill, code = E0798)]
 #[note]
-pub(crate) struct CmseCallInputsStackSpill {
+pub(crate) struct CmseInputsStackSpill {
     #[primary_span]
     #[label]
     pub span: Span,
     pub plural: bool,
+    pub abi_name: &'static str,
 }
 
 #[derive(Diagnostic)]
-#[diag(hir_analysis_cmse_call_output_stack_spill, code = E0798)]
+#[diag(hir_analysis_cmse_output_stack_spill, code = E0798)]
 #[note(hir_analysis_note1)]
 #[note(hir_analysis_note2)]
-pub(crate) struct CmseCallOutputStackSpill {
+pub(crate) struct CmseOutputStackSpill {
     #[primary_span]
     #[label]
     pub span: Span,
+    pub abi_name: &'static str,
 }
 
 #[derive(Diagnostic)]
@@ -1701,3 +1661,10 @@ pub(crate) struct BadReturnTypeNotation {
     #[primary_span]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_cmse_entry_generic, code = E0798)]
+pub(crate) struct CmseEntryGeneric {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs
index b6cffb90805..8a83866b7fa 100644
--- a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs
+++ b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs
@@ -35,6 +35,15 @@ pub(crate) struct LifetimeNotCaptured {
 }
 
 #[derive(Diagnostic)]
+#[diag(hir_analysis_lifetime_implicitly_captured)]
+pub(crate) struct LifetimeImplicitlyCaptured {
+    #[primary_span]
+    pub opaque_span: Span,
+    #[label(hir_analysis_param_label)]
+    pub param_span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(hir_analysis_bad_precise_capture)]
 pub(crate) struct BadPreciseCapture {
     #[primary_span]
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 45cd46e3df2..8f7ca089c91 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -44,10 +44,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let mut unbounds: SmallVec<[_; 1]> = SmallVec::new();
         let mut search_bounds = |hir_bounds: &'tcx [hir::GenericBound<'tcx>]| {
             for hir_bound in hir_bounds {
-                let hir::GenericBound::Trait(ptr, modifier) = hir_bound else {
+                let hir::GenericBound::Trait(ptr) = hir_bound else {
                     continue;
                 };
-                match modifier {
+                match ptr.modifiers {
                     hir::TraitBoundModifier::Maybe => unbounds.push(ptr),
                     hir::TraitBoundModifier::Negative => {
                         if let Some(sized_def_id) = sized_def_id
@@ -156,8 +156,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     {
         for hir_bound in hir_bounds {
             match hir_bound {
-                hir::GenericBound::Trait(poly_trait_ref, modifier) => {
-                    let (constness, polarity) = match modifier {
+                hir::GenericBound::Trait(poly_trait_ref) => {
+                    let (constness, polarity) = match poly_trait_ref.modifiers {
                         hir::TraitBoundModifier::Const => {
                             (ty::BoundConstness::Const, ty::PredicatePolarity::Positive)
                         }
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 5150db7f51b..672dc8ddeda 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
@@ -1,9 +1,8 @@
-use rustc_errors::DiagCtxtHandle;
-use rustc_hir as hir;
-use rustc_hir::HirId;
+use rustc_errors::{DiagCtxtHandle, E0781, struct_span_code_err};
+use rustc_hir::{self as hir, HirId};
+use rustc_middle::bug;
 use rustc_middle::ty::layout::LayoutError;
 use rustc_middle::ty::{self, ParamEnv, TyCtxt};
-use rustc_span::Span;
 use rustc_target::spec::abi;
 
 use crate::errors;
@@ -18,49 +17,104 @@ pub(crate) fn validate_cmse_abi<'tcx>(
     abi: abi::Abi,
     fn_sig: ty::PolyFnSig<'tcx>,
 ) {
-    if let abi::Abi::CCmseNonSecureCall = abi {
-        let hir_node = tcx.hir_node(hir_id);
-        let hir::Node::Ty(hir::Ty {
-            span: bare_fn_span,
-            kind: hir::TyKind::BareFn(bare_fn_ty),
-            ..
-        }) = hir_node
-        else {
-            // might happen when this ABI is used incorrectly. That will be handled elsewhere
-            return;
-        };
-
-        match is_valid_cmse_inputs(tcx, fn_sig) {
-            Ok(Ok(())) => {}
-            Ok(Err(index)) => {
-                // fn(x: u32, u32, u32, u16, y: u16) -> u32,
-                //                           ^^^^^^
-                let span = bare_fn_ty.param_names[index]
-                    .span
-                    .to(bare_fn_ty.decl.inputs[index].span)
-                    .to(bare_fn_ty.decl.inputs.last().unwrap().span);
-                let plural = bare_fn_ty.param_names.len() - index != 1;
-                dcx.emit_err(errors::CmseCallInputsStackSpill { span, plural });
-            }
-            Err(layout_err) => {
-                if let Some(err) = cmse_layout_err(layout_err, *bare_fn_span) {
-                    dcx.emit_err(err);
+    let abi_name = abi.name();
+
+    match abi {
+        abi::Abi::CCmseNonSecureCall => {
+            let hir_node = tcx.hir_node(hir_id);
+            let hir::Node::Ty(hir::Ty {
+                span: bare_fn_span,
+                kind: hir::TyKind::BareFn(bare_fn_ty),
+                ..
+            }) = hir_node
+            else {
+                let span = match tcx.parent_hir_node(hir_id) {
+                    hir::Node::Item(hir::Item {
+                        kind: hir::ItemKind::ForeignMod { .. },
+                        span,
+                        ..
+                    }) => *span,
+                    _ => tcx.hir().span(hir_id),
+                };
+                struct_span_code_err!(
+                    tcx.dcx(),
+                    span,
+                    E0781,
+                    "the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers"
+                )
+                .emit();
+                return;
+            };
+
+            match is_valid_cmse_inputs(tcx, fn_sig) {
+                Ok(Ok(())) => {}
+                Ok(Err(index)) => {
+                    // fn(x: u32, u32, u32, u16, y: u16) -> u32,
+                    //                           ^^^^^^
+                    let span = bare_fn_ty.param_names[index]
+                        .span
+                        .to(bare_fn_ty.decl.inputs[index].span)
+                        .to(bare_fn_ty.decl.inputs.last().unwrap().span);
+                    let plural = bare_fn_ty.param_names.len() - index != 1;
+                    dcx.emit_err(errors::CmseInputsStackSpill { span, plural, abi_name });
+                }
+                Err(layout_err) => {
+                    if should_emit_generic_error(abi, layout_err) {
+                        dcx.emit_err(errors::CmseCallGeneric { span: *bare_fn_span });
+                    }
                 }
             }
-        }
 
-        match is_valid_cmse_output(tcx, fn_sig) {
-            Ok(true) => {}
-            Ok(false) => {
-                let span = bare_fn_ty.decl.output.span();
-                dcx.emit_err(errors::CmseCallOutputStackSpill { span });
-            }
-            Err(layout_err) => {
-                if let Some(err) = cmse_layout_err(layout_err, *bare_fn_span) {
-                    dcx.emit_err(err);
+            match is_valid_cmse_output(tcx, fn_sig) {
+                Ok(true) => {}
+                Ok(false) => {
+                    let span = bare_fn_ty.decl.output.span();
+                    dcx.emit_err(errors::CmseOutputStackSpill { span, abi_name });
+                }
+                Err(layout_err) => {
+                    if should_emit_generic_error(abi, layout_err) {
+                        dcx.emit_err(errors::CmseCallGeneric { span: *bare_fn_span });
+                    }
+                }
+            };
+        }
+        abi::Abi::CCmseNonSecureEntry => {
+            let hir_node = tcx.hir_node(hir_id);
+            let Some(hir::FnSig { decl, span: fn_sig_span, .. }) = hir_node.fn_sig() else {
+                // might happen when this ABI is used incorrectly. That will be handled elsewhere
+                return;
+            };
+
+            match is_valid_cmse_inputs(tcx, fn_sig) {
+                Ok(Ok(())) => {}
+                Ok(Err(index)) => {
+                    // fn f(x: u32, y: u32, z: u32, w: u16, q: u16) -> u32,
+                    //                                      ^^^^^^
+                    let span = decl.inputs[index].span.to(decl.inputs.last().unwrap().span);
+                    let plural = decl.inputs.len() - index != 1;
+                    dcx.emit_err(errors::CmseInputsStackSpill { span, plural, abi_name });
+                }
+                Err(layout_err) => {
+                    if should_emit_generic_error(abi, layout_err) {
+                        dcx.emit_err(errors::CmseEntryGeneric { span: *fn_sig_span });
+                    }
                 }
             }
-        };
+
+            match is_valid_cmse_output(tcx, fn_sig) {
+                Ok(true) => {}
+                Ok(false) => {
+                    let span = decl.output.span();
+                    dcx.emit_err(errors::CmseOutputStackSpill { span, abi_name });
+                }
+                Err(layout_err) => {
+                    if should_emit_generic_error(abi, layout_err) {
+                        dcx.emit_err(errors::CmseEntryGeneric { span: *fn_sig_span });
+                    }
+                }
+            };
+        }
+        _ => (),
     }
 }
 
@@ -141,22 +195,22 @@ fn is_valid_cmse_output<'tcx>(
     Ok(ret_ty == tcx.types.i64 || ret_ty == tcx.types.u64 || ret_ty == tcx.types.f64)
 }
 
-fn cmse_layout_err<'tcx>(
-    layout_err: &'tcx LayoutError<'tcx>,
-    span: Span,
-) -> Option<crate::errors::CmseCallGeneric> {
+fn should_emit_generic_error<'tcx>(abi: abi::Abi, layout_err: &'tcx LayoutError<'tcx>) -> bool {
     use LayoutError::*;
 
     match layout_err {
         Unknown(ty) => {
-            if ty.is_impl_trait() {
-                None // prevent double reporting of this error
-            } else {
-                Some(errors::CmseCallGeneric { span })
+            match abi {
+                abi::Abi::CCmseNonSecureCall => {
+                    // prevent double reporting of this error
+                    !ty.is_impl_trait()
+                }
+                abi::Abi::CCmseNonSecureEntry => true,
+                _ => bug!("invalid ABI: {abi}"),
             }
         }
         SizeOverflow(..) | NormalizationFailure(..) | ReferencesError(..) | Cycle(..) => {
-            None // not our job to report these
+            false // not our job to report these
         }
     }
 }
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 394a263fbb5..98822eec2ac 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
@@ -30,7 +30,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         &self,
         span: Span,
         hir_id: hir::HirId,
-        hir_trait_bounds: &[(hir::PolyTraitRef<'tcx>, hir::TraitBoundModifier)],
+        hir_trait_bounds: &[hir::PolyTraitRef<'tcx>],
         lifetime: &hir::Lifetime,
         representation: DynKind,
     ) -> Ty<'tcx> {
@@ -39,8 +39,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let mut bounds = Bounds::default();
         let mut potential_assoc_types = Vec::new();
         let dummy_self = self.tcx().types.trait_object_dummy_self;
-        for (trait_bound, modifier) in hir_trait_bounds.iter().rev() {
-            if *modifier == hir::TraitBoundModifier::Maybe {
+        for trait_bound in hir_trait_bounds.iter().rev() {
+            // FIXME: This doesn't handle `? const`.
+            if trait_bound.modifiers == hir::TraitBoundModifier::Maybe {
                 continue;
             }
             if let GenericArgCountResult {
@@ -263,7 +264,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 let args = tcx.mk_args(&args);
 
                 let span = i.bottom().1;
-                let empty_generic_args = hir_trait_bounds.iter().any(|(hir_bound, _)| {
+                let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| {
                     hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id)
                         && hir_bound.span.contains(span)
                 });
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 a735b8cc2a4..01768c89cca 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -718,7 +718,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         &self,
         associated_types: FxIndexMap<Span, FxIndexSet<DefId>>,
         potential_assoc_types: Vec<usize>,
-        trait_bounds: &[(hir::PolyTraitRef<'_>, hir::TraitBoundModifier)],
+        trait_bounds: &[hir::PolyTraitRef<'_>],
     ) {
         if associated_types.values().all(|v| v.is_empty()) {
             return;
@@ -764,12 +764,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         // related to issue #91997, turbofishes added only when in an expr or pat
         let mut in_expr_or_pat = false;
         if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) {
-            let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.0.trait_ref.hir_ref_id));
+            let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.trait_ref.hir_ref_id));
             in_expr_or_pat = match grandparent {
                 hir::Node::Expr(_) | hir::Node::Pat(_) => true,
                 _ => false,
             };
-            match bound.0.trait_ref.path.segments {
+            match bound.trait_ref.path.segments {
                 // FIXME: `trait_ref.path.span` can point to a full path with multiple
                 // segments, even though `trait_ref.path.segments` is of length `1`. Work
                 // around that bug here, even though it should be fixed elsewhere.
@@ -810,7 +810,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         // and we can then use their span to indicate this to the user.
         let bound_names = trait_bounds
             .iter()
-            .filter_map(|(poly_trait_ref, _)| {
+            .filter_map(|poly_trait_ref| {
                 let path = poly_trait_ref.trait_ref.path.segments.last()?;
                 let args = path.args?;
 
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 5607fe873f6..fd49e7e4439 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
@@ -1,6 +1,6 @@
 use rustc_ast::TraitObjectSyntax;
 use rustc_errors::codes::*;
-use rustc_errors::{Diag, EmissionGuarantee, StashKey};
+use rustc_errors::{Diag, EmissionGuarantee, ErrorGuaranteed, StashKey, Suggestions};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_lint_defs::Applicability;
@@ -15,13 +15,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     ///
     /// *Bare* trait object types are ones that aren't preceded by the keyword `dyn`.
     /// In edition 2021 and onward we emit a hard error for them.
-    pub(super) fn prohibit_or_lint_bare_trait_object_ty(&self, self_ty: &hir::Ty<'_>) {
+    pub(super) fn prohibit_or_lint_bare_trait_object_ty(
+        &self,
+        self_ty: &hir::Ty<'_>,
+    ) -> Option<ErrorGuaranteed> {
         let tcx = self.tcx();
 
         let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) =
             self_ty.kind
         else {
-            return;
+            return None;
         };
 
         let in_path = match tcx.parent_hir_node(self_ty.hir_id) {
@@ -47,7 +50,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 .ok()
                 .is_some_and(|s| s.trim_end().ends_with('<'));
 
-        let is_global = poly_trait_ref.0.trait_ref.path.is_global();
+        let is_global = poly_trait_ref.trait_ref.path.is_global();
 
         let mut sugg = vec![(
             self_ty.span.shrink_to_lo(),
@@ -70,8 +73,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         }
 
         if self_ty.span.edition().at_least_rust_2021() {
-            let msg = "trait objects must include the `dyn` keyword";
-            let label = "add `dyn` keyword before this trait";
+            let msg = "expected a type, found a trait";
+            let label = "you can add the `dyn` keyword if you want a trait object";
             let mut diag =
                 rustc_errors::struct_span_code_err!(self.dcx(), self_ty.span, E0782, "{}", msg);
             if self_ty.span.can_be_used_for_suggestions()
@@ -83,7 +86,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             // Check if the impl trait that we are considering is an impl of a local trait.
             self.maybe_suggest_blanket_trait_impl(self_ty, &mut diag);
             self.maybe_suggest_assoc_ty_bound(self_ty, &mut diag);
-            diag.stash(self_ty.span, StashKey::TraitMissingMethod);
+            // In case there is an associate type with the same name
+            // Add the suggestion to this error
+            if let Some(mut sugg) =
+                tcx.dcx().steal_non_err(self_ty.span, StashKey::AssociatedTypeSuggestion)
+                && let Suggestions::Enabled(ref mut s1) = diag.suggestions
+                && let Suggestions::Enabled(ref mut s2) = sugg.suggestions
+            {
+                s1.append(s2);
+                sugg.cancel();
+            }
+            diag.stash(self_ty.span, StashKey::TraitMissingMethod)
         } else {
             tcx.node_span_lint(BARE_TRAIT_OBJECTS, self_ty.hir_id, self_ty.span, |lint| {
                 lint.primary_message("trait objects without an explicit `dyn` are deprecated");
@@ -96,6 +109,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 }
                 self.maybe_suggest_blanket_trait_impl(self_ty, lint);
             });
+            None
         }
     }
 
@@ -174,41 +188,31 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         // 1. Independent functions
         // 2. Functions inside trait blocks
         // 3. Functions inside impl blocks
-        let (sig, generics, owner) = match tcx.hir_node_by_def_id(parent_id) {
+        let (sig, generics) = match tcx.hir_node_by_def_id(parent_id) {
             hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, generics, _), .. }) => {
-                (sig, generics, None)
+                (sig, generics)
             }
             hir::Node::TraitItem(hir::TraitItem {
                 kind: hir::TraitItemKind::Fn(sig, _),
                 generics,
-                owner_id,
                 ..
-            }) => (sig, generics, Some(tcx.parent(owner_id.to_def_id()))),
+            }) => (sig, generics),
             hir::Node::ImplItem(hir::ImplItem {
                 kind: hir::ImplItemKind::Fn(sig, _),
                 generics,
-                owner_id,
                 ..
-            }) => (sig, generics, Some(tcx.parent(owner_id.to_def_id()))),
+            }) => (sig, generics),
             _ => return false,
         };
         let Ok(trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else {
             return false;
         };
         let impl_sugg = vec![(self_ty.span.shrink_to_lo(), "impl ".to_string())];
-        let mut is_downgradable = true;
-
         // Check if trait object is safe for suggesting dynamic dispatch.
         let is_dyn_compatible = match self_ty.kind {
             hir::TyKind::TraitObject(objects, ..) => {
-                objects.iter().all(|(o, _)| match o.trait_ref.path.res {
-                    Res::Def(DefKind::Trait, id) => {
-                        if Some(id) == owner {
-                            // For recursive traits, don't downgrade the error. (#119652)
-                            is_downgradable = false;
-                        }
-                        tcx.is_dyn_compatible(id)
-                    }
+                objects.iter().all(|o| match o.trait_ref.path.res {
+                    Res::Def(DefKind::Trait, id) => tcx.is_dyn_compatible(id),
                     _ => false,
                 })
             }
@@ -255,9 +259,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     suggestion,
                     Applicability::MachineApplicable,
                 );
-            } else if is_downgradable {
-                // We'll emit the dyn-compatibility error already, with a structured suggestion.
-                diag.downgrade_to_delayed_bug();
             }
             return true;
         }
@@ -281,10 +282,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             );
             if !is_dyn_compatible {
                 diag.note(format!("`{trait_name}` it is dyn-incompatible, so it can't be `dyn`"));
-                if is_downgradable {
-                    // We'll emit the dyn-compatibility error already, with a structured suggestion.
-                    diag.downgrade_to_delayed_bug();
-                }
             } else {
                 // No ampersand in suggestion if it's borrowed already
                 let (dyn_str, paren_dyn_str) =
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 28a1fc88741..d760acf53bd 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -53,6 +53,7 @@ use rustc_trait_selection::traits::{self, ObligationCtxt};
 use tracing::{debug, debug_span, instrument};
 
 use crate::bounds::Bounds;
+use crate::check::check_abi_fn_ptr;
 use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, WildPatTy};
 use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint};
 use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
@@ -2063,13 +2064,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 )
             }
             hir::TyKind::TraitObject(bounds, lifetime, repr) => {
-                self.prohibit_or_lint_bare_trait_object_ty(hir_ty);
-
-                let repr = match repr {
-                    TraitObjectSyntax::Dyn | TraitObjectSyntax::None => ty::Dyn,
-                    TraitObjectSyntax::DynStar => ty::DynStar,
-                };
-                self.lower_trait_object_ty(hir_ty.span, hir_ty.hir_id, bounds, lifetime, repr)
+                if let Some(guar) = self.prohibit_or_lint_bare_trait_object_ty(hir_ty) {
+                    // Don't continue with type analysis if the `dyn` keyword is missing
+                    // It generates confusing errors, especially if the user meant to use another
+                    // keyword like `impl`
+                    Ty::new_error(tcx, guar)
+                } else {
+                    let repr = match repr {
+                        TraitObjectSyntax::Dyn | TraitObjectSyntax::None => ty::Dyn,
+                        TraitObjectSyntax::DynStar => ty::DynStar,
+                    };
+                    self.lower_trait_object_ty(hir_ty.span, hir_ty.hir_id, bounds, lifetime, repr)
+                }
             }
             // If we encounter a fully qualified path with RTN generics, then it must have
             // *not* gone through `lower_ty_maybe_return_type_notation`, and therefore
@@ -2337,6 +2343,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, safety, abi);
         let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars);
 
+        if let hir::Node::Ty(hir::Ty { kind: hir::TyKind::BareFn(bare_fn_ty), span, .. }) =
+            tcx.hir_node(hir_id)
+        {
+            check_abi_fn_ptr(tcx, hir_id, *span, bare_fn_ty.abi);
+        }
+
         // reject function types that violate cmse ABI requirements
         cmse::validate_cmse_abi(self.tcx(), self.dcx(), hir_id, abi, bare_fn_ty);
 
diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs
index 12bb9a3f9e0..02cfb57b836 100644
--- a/compiler/rustc_hir_analysis/src/variance/mod.rs
+++ b/compiler/rustc_hir_analysis/src/variance/mod.rs
@@ -5,6 +5,7 @@
 
 use itertools::Itertools;
 use rustc_arena::DroplessArena;
+use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::query::Providers;
@@ -63,8 +64,29 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] {
             let crate_map = tcx.crate_variances(());
             return crate_map.variances.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]);
         }
+        DefKind::AssocTy => match tcx.opt_rpitit_info(item_def_id.to_def_id()) {
+            Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) => {
+                return variance_of_opaque(
+                    tcx,
+                    opaque_def_id.expect_local(),
+                    ForceCaptureTraitArgs::Yes,
+                );
+            }
+            None | Some(ty::ImplTraitInTraitData::Impl { .. }) => {}
+        },
         DefKind::OpaqueTy => {
-            return variance_of_opaque(tcx, item_def_id);
+            let force_capture_trait_args = if let hir::OpaqueTyOrigin::FnReturn {
+                parent: _,
+                in_trait_or_impl: Some(hir::RpitContext::Trait),
+            } =
+                tcx.hir_node_by_def_id(item_def_id).expect_opaque_ty().origin
+            {
+                ForceCaptureTraitArgs::Yes
+            } else {
+                ForceCaptureTraitArgs::No
+            };
+
+            return variance_of_opaque(tcx, item_def_id, force_capture_trait_args);
         }
         _ => {}
     }
@@ -73,8 +95,18 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] {
     span_bug!(tcx.def_span(item_def_id), "asked to compute variance for wrong kind of item");
 }
 
+#[derive(Debug, Copy, Clone)]
+enum ForceCaptureTraitArgs {
+    Yes,
+    No,
+}
+
 #[instrument(level = "trace", skip(tcx), ret)]
-fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] {
+fn variance_of_opaque(
+    tcx: TyCtxt<'_>,
+    item_def_id: LocalDefId,
+    force_capture_trait_args: ForceCaptureTraitArgs,
+) -> &[ty::Variance] {
     let generics = tcx.generics_of(item_def_id);
 
     // Opaque types may only use regions that are bound. So for
@@ -115,9 +147,7 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
         #[instrument(level = "trace", skip(self), ret)]
         fn visit_ty(&mut self, t: Ty<'tcx>) {
             match t.kind() {
-                ty::Alias(_, ty::AliasTy { def_id, args, .. })
-                    if matches!(self.tcx.def_kind(*def_id), DefKind::OpaqueTy) =>
-                {
+                ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
                     self.visit_opaque(*def_id, args);
                 }
                 _ => t.super_visit_with(self),
@@ -135,6 +165,15 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
         let mut generics = generics;
         while let Some(def_id) = generics.parent {
             generics = tcx.generics_of(def_id);
+
+            // Don't mark trait params generic if we're in an RPITIT.
+            if matches!(force_capture_trait_args, ForceCaptureTraitArgs::Yes)
+                && generics.parent.is_none()
+            {
+                debug_assert_eq!(tcx.def_kind(def_id), DefKind::Trait);
+                break;
+            }
+
             for param in &generics.own_params {
                 match param.kind {
                     ty::GenericParamDefKind::Lifetime => {
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 9fe6a8ee342..9ebfd4f15ab 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -301,16 +301,13 @@ impl<'a> State<'a> {
                     self.word_space("dyn");
                 }
                 let mut first = true;
-                for (bound, modifier) in bounds {
+                for bound in bounds {
                     if first {
                         first = false;
                     } else {
                         self.nbsp();
                         self.word_space("+");
                     }
-                    if *modifier == TraitBoundModifier::Maybe {
-                        self.word("?");
-                    }
                     self.print_poly_trait_ref(bound);
                 }
                 if !lifetime.is_elided() {
@@ -679,6 +676,10 @@ impl<'a> State<'a> {
     }
 
     fn print_poly_trait_ref(&mut self, t: &hir::PolyTraitRef<'_>) {
+        // FIXME: This isn't correct!
+        if t.modifiers == TraitBoundModifier::Maybe {
+            self.word("?");
+        }
         self.print_formal_generic_params(t.bound_generic_params);
         self.print_trait_ref(&t.trait_ref);
     }
@@ -2077,10 +2078,7 @@ impl<'a> State<'a> {
             }
 
             match bound {
-                GenericBound::Trait(tref, modifier) => {
-                    if modifier == &TraitBoundModifier::Maybe {
-                        self.word("?");
-                    }
+                GenericBound::Trait(tref) => {
                     self.print_poly_trait_ref(tref);
                 }
                 GenericBound::Outlives(lt) => {
diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml
index 73a775690d6..894402a8c2e 100644
--- a/compiler/rustc_hir_typeck/Cargo.toml
+++ b/compiler/rustc_hir_typeck/Cargo.toml
@@ -6,6 +6,7 @@ edition = "2021"
 [dependencies]
 # tidy-alphabetical-start
 itertools = "0.12"
+rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_ir = { path = "../rustc_ast_ir" }
 rustc_attr = { path = "../rustc_attr" }
diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs
index 0d9d1910ae0..e68caa3e2e3 100644
--- a/compiler/rustc_hir_typeck/src/_match.rs
+++ b/compiler/rustc_hir_typeck/src/_match.rs
@@ -235,8 +235,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Some(ret_coercion) => {
                 let ret_ty = ret_coercion.borrow().expected_ty();
                 let ret_ty = self.infcx.shallow_resolve(ret_ty);
-                self.can_coerce(arm_ty, ret_ty)
-                    && prior_arm.is_none_or(|(_, ty, _)| self.can_coerce(ty, ret_ty))
+                self.may_coerce(arm_ty, ret_ty)
+                    && prior_arm.is_none_or(|(_, ty, _)| self.may_coerce(ty, ret_ty))
                     // The match arms need to unify for the case of `impl Trait`.
                     && !matches!(ret_ty.kind(), ty::Alias(ty::Opaque, ..))
             }
diff --git a/compiler/rustc_hir_typeck/src/autoderef.rs b/compiler/rustc_hir_typeck/src/autoderef.rs
index d2d6da8f32b..e4ca1cee757 100644
--- a/compiler/rustc_hir_typeck/src/autoderef.rs
+++ b/compiler/rustc_hir_typeck/src/autoderef.rs
@@ -5,6 +5,7 @@ use std::iter;
 use itertools::Itertools;
 use rustc_hir_analysis::autoderef::{Autoderef, AutoderefKind};
 use rustc_infer::infer::InferOk;
+use rustc_infer::traits::PredicateObligations;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
 use rustc_middle::ty::{self, Ty};
 use rustc_span::Span;
@@ -36,10 +37,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) -> InferOk<'tcx, Vec<Adjustment<'tcx>>> {
         let steps = autoderef.steps();
         if steps.is_empty() {
-            return InferOk { obligations: vec![], value: vec![] };
+            return InferOk { obligations: PredicateObligations::new(), value: vec![] };
         }
 
-        let mut obligations = vec![];
+        let mut obligations = PredicateObligations::new();
         let targets =
             steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(autoderef.final_ty(false)));
         let steps: Vec<_> = steps
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 407191661a4..8fa6ab8503d 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -409,7 +409,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                 let mut sugg_mutref = false;
                 if let ty::Ref(reg, cast_ty, mutbl) = *self.cast_ty.kind() {
                     if let ty::RawPtr(expr_ty, _) = *self.expr_ty.kind()
-                        && fcx.can_coerce(
+                        && fcx.may_coerce(
                             Ty::new_ref(fcx.tcx, fcx.tcx.lifetimes.re_erased, expr_ty, mutbl),
                             self.cast_ty,
                         )
@@ -418,14 +418,14 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                     } else if let ty::Ref(expr_reg, expr_ty, expr_mutbl) = *self.expr_ty.kind()
                         && expr_mutbl == Mutability::Not
                         && mutbl == Mutability::Mut
-                        && fcx.can_coerce(Ty::new_mut_ref(fcx.tcx, expr_reg, expr_ty), self.cast_ty)
+                        && fcx.may_coerce(Ty::new_mut_ref(fcx.tcx, expr_reg, expr_ty), self.cast_ty)
                     {
                         sugg_mutref = true;
                     }
 
                     if !sugg_mutref
                         && sugg == None
-                        && fcx.can_coerce(
+                        && fcx.may_coerce(
                             Ty::new_ref(fcx.tcx, reg, self.expr_ty, mutbl),
                             self.cast_ty,
                         )
@@ -433,7 +433,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                         sugg = Some((format!("&{}", mutbl.prefix_str()), false));
                     }
                 } else if let ty::RawPtr(_, mutbl) = *self.cast_ty.kind()
-                    && fcx.can_coerce(
+                    && fcx.may_coerce(
                         Ty::new_ref(fcx.tcx, fcx.tcx.lifetimes.re_erased, self.expr_ty, mutbl),
                         self.cast_ty,
                     )
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index fcaa5751152..fd6ac7de14a 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -8,7 +8,7 @@ use rustc_hir as hir;
 use rustc_hir::lang_items::LangItem;
 use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
 use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, InferResult};
-use rustc_infer::traits::ObligationCauseCode;
+use rustc_infer::traits::{ObligationCauseCode, PredicateObligations};
 use rustc_macros::{TypeFoldable, TypeVisitable};
 use rustc_middle::span_bug;
 use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
@@ -805,7 +805,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // [c1]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341089706
         // [c2]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341096796
         self.commit_if_ok(|_| {
-            let mut all_obligations = vec![];
+            let mut all_obligations = PredicateObligations::new();
             let supplied_sig = self.instantiate_binder_with_fresh_vars(
                 self.tcx.def_span(expr_def_id),
                 BoundRegionConversionTime::FnCall,
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index bd0b9870298..406d668e7a5 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -46,6 +46,7 @@ use rustc_infer::infer::relate::RelateResult;
 use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
 use rustc_infer::traits::{
     IfExpressionCause, MatchExpressionArmCause, Obligation, PredicateObligation,
+    PredicateObligations,
 };
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::span_bug;
@@ -120,7 +121,7 @@ fn simple<'tcx>(kind: Adjust<'tcx>) -> impl FnOnce(Ty<'tcx>) -> Vec<Adjustment<'
 fn success<'tcx>(
     adj: Vec<Adjustment<'tcx>>,
     target: Ty<'tcx>,
-    obligations: Vec<traits::PredicateObligation<'tcx>>,
+    obligations: PredicateObligations<'tcx>,
 ) -> CoerceResult<'tcx> {
     Ok(InferOk { value: (adj, target), obligations })
 }
@@ -184,7 +185,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         // Coercing from `!` to any type is allowed:
         if a.is_never() {
             if self.coerce_never {
-                return success(simple(Adjust::NeverToAny)(b), b, vec![]);
+                return success(simple(Adjust::NeverToAny)(b), b, PredicateObligations::new());
             } else {
                 // Otherwise the only coercion we can do is unification.
                 return self.unify_and(a, b, identity);
@@ -278,7 +279,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             // Two unresolved type variables: create a `Coerce` predicate.
             let target_ty = if self.use_lub { self.next_ty_var(self.cause.span) } else { b };
 
-            let mut obligations = Vec::with_capacity(2);
+            let mut obligations = PredicateObligations::with_capacity(2);
             for &source_ty in &[a, b] {
                 if source_ty != target_ty {
                     obligations.push(Obligation::new(
@@ -744,7 +745,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
 
         // Check the obligations of the cast -- for example, when casting
         // `usize` to `dyn* Clone + 'static`:
-        let mut obligations: Vec<_> = predicates
+        let obligations = predicates
             .iter()
             .map(|predicate| {
                 // For each existential predicate (e.g., `?Self: Clone`) instantiate
@@ -764,21 +765,21 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                         ty::OutlivesPredicate(a, b_region),
                     ))),
                 ),
+                // Enforce that the type is `usize`/pointer-sized.
+                Obligation::new(
+                    self.tcx,
+                    self.cause.clone(),
+                    self.param_env,
+                    ty::TraitRef::new(
+                        self.tcx,
+                        self.tcx
+                            .require_lang_item(hir::LangItem::PointerLike, Some(self.cause.span)),
+                        [a],
+                    ),
+                ),
             ])
             .collect();
 
-        // Enforce that the type is `usize`/pointer-sized.
-        obligations.push(Obligation::new(
-            self.tcx,
-            self.cause.clone(),
-            self.param_env,
-            ty::TraitRef::new(
-                self.tcx,
-                self.tcx.require_lang_item(hir::LangItem::PointerLike, Some(self.cause.span)),
-                [a],
-            ),
-        ));
-
         Ok(InferOk {
             value: (
                 vec![Adjustment { kind: Adjust::Pointer(PointerCoercion::DynStar), target: b }],
@@ -1083,24 +1084,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         })
     }
 
-    /// Same as `coerce()`, but without side-effects.
+    /// Probe whether `expr_ty` can be coerced to `target_ty`. This has no side-effects,
+    /// and may return false positives if types are not yet fully constrained by inference.
     ///
-    /// Returns false if the coercion creates any obligations that result in
-    /// errors.
-    pub(crate) fn can_coerce(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> bool {
-        // FIXME(-Znext-solver): We need to structurally resolve both types here.
-        let source = self.resolve_vars_with_obligations(expr_ty);
-        debug!("coercion::can_with_predicates({:?} -> {:?})", source, target);
-
+    /// Returns false if the coercion is not possible, or if the coercion creates any
+    /// sub-obligations that result in errors.
+    ///
+    /// This should only be used for diagnostics.
+    pub(crate) fn may_coerce(&self, expr_ty: Ty<'tcx>, target_ty: Ty<'tcx>) -> bool {
         let cause = self.cause(DUMMY_SP, ObligationCauseCode::ExprAssignable);
         // We don't ever need two-phase here since we throw out the result of the coercion.
         // We also just always set `coerce_never` to true, since this is a heuristic.
-        let coerce = Coerce::new(self, cause, AllowTwoPhase::No, true);
+        let coerce = Coerce::new(self, cause.clone(), AllowTwoPhase::No, true);
         self.probe(|_| {
-            let Ok(ok) = coerce.coerce(source, target) else {
+            // Make sure to structurally resolve the types, since we use
+            // the `TyKind`s heavily in coercion.
+            let ocx = ObligationCtxt::new(self);
+            let structurally_resolve = |ty| {
+                let ty = self.shallow_resolve(ty);
+                if self.next_trait_solver()
+                    && let ty::Alias(..) = ty.kind()
+                {
+                    ocx.structurally_normalize(&cause, self.param_env, ty)
+                } else {
+                    Ok(ty)
+                }
+            };
+            let Ok(expr_ty) = structurally_resolve(expr_ty) else {
+                return false;
+            };
+            let Ok(target_ty) = structurally_resolve(target_ty) else {
+                return false;
+            };
+
+            let Ok(ok) = coerce.coerce(expr_ty, target_ty) else {
                 return false;
             };
-            let ocx = ObligationCtxt::new(self);
             ocx.register_obligations(ok.obligations);
             ocx.select_where_possible().is_empty()
         })
@@ -1369,7 +1388,7 @@ pub fn can_coerce<'tcx>(
 ) -> bool {
     let root_ctxt = crate::typeck_root_ctxt::TypeckRootCtxt::new(tcx, body_id);
     let fn_ctxt = FnCtxt::new(&root_ctxt, param_env, body_id);
-    fn_ctxt.can_coerce(ty, output_ty)
+    fn_ctxt.may_coerce(ty, output_ty)
 }
 
 /// CoerceMany encapsulates the pattern you should use when you have
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 95ca3e66472..7bdd3c95ad1 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -36,7 +36,6 @@ use rustc_span::symbol::{Ident, Symbol, kw, sym};
 use rustc_target::abi::{FIRST_VARIANT, FieldIdx};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt};
-use smallvec::SmallVec;
 use tracing::{debug, instrument, trace};
 use {rustc_ast as ast, rustc_hir as hir};
 
@@ -1331,9 +1330,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let refs_can_coerce = |lhs: Ty<'tcx>, rhs: Ty<'tcx>| {
                 let lhs = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, lhs.peel_refs());
                 let rhs = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, rhs.peel_refs());
-                self.can_coerce(rhs, lhs)
+                self.may_coerce(rhs, lhs)
             };
-            let (applicability, eq) = if self.can_coerce(rhs_ty, lhs_ty) {
+            let (applicability, eq) = if self.may_coerce(rhs_ty, lhs_ty) {
                 (Applicability::MachineApplicable, true)
             } else if refs_can_coerce(rhs_ty, lhs_ty) {
                 // The lhs and rhs are likely missing some references in either side. Subsequent
@@ -1350,7 +1349,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let actual_lhs_ty = self.check_expr(rhs_expr);
                 (
                     Applicability::MaybeIncorrect,
-                    self.can_coerce(rhs_ty, actual_lhs_ty)
+                    self.may_coerce(rhs_ty, actual_lhs_ty)
                         || refs_can_coerce(rhs_ty, actual_lhs_ty),
                 )
             } else if let ExprKind::Binary(
@@ -1364,7 +1363,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let actual_rhs_ty = self.check_expr(lhs_expr);
                 (
                     Applicability::MaybeIncorrect,
-                    self.can_coerce(actual_rhs_ty, lhs_ty)
+                    self.may_coerce(actual_rhs_ty, lhs_ty)
                         || refs_can_coerce(actual_rhs_ty, lhs_ty),
                 )
             } else {
@@ -1415,7 +1414,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         self.param_env,
                     )
                     .may_apply();
-                if lhs_deref_ty_is_sized && self.can_coerce(rhs_ty, lhs_deref_ty) {
+                if lhs_deref_ty_is_sized && self.may_coerce(rhs_ty, lhs_deref_ty) {
                     err.span_suggestion_verbose(
                         lhs.span.shrink_to_lo(),
                         "consider dereferencing here to assign to the mutably borrowed value",
@@ -1783,7 +1782,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expr: &'tcx hir::Expr<'tcx>,
     ) -> Ty<'tcx> {
         let flds = expected.only_has_type(self).and_then(|ty| {
-            let ty = self.resolve_vars_with_obligations(ty);
+            let ty = self.try_structurally_resolve_type(expr.span, ty);
             match ty.kind() {
                 ty::Tuple(flds) => Some(&flds[..]),
                 _ => None,
@@ -1861,7 +1860,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) {
         let tcx = self.tcx;
 
-        let adt_ty = self.resolve_vars_with_obligations(adt_ty);
+        let adt_ty = self.try_structurally_resolve_type(span, adt_ty);
         let adt_ty_hint = expected.only_has_type(self).and_then(|expected| {
             self.fudge_inference_if_ok(|| {
                 let ocx = ObligationCtxt::new(self);
@@ -1907,8 +1906,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let ident = tcx.adjust_ident(field.ident, variant.def_id);
             let field_type = if let Some((i, v_field)) = remaining_fields.remove(&ident) {
                 seen_fields.insert(ident, field.span);
-                // FIXME: handle nested fields
-                self.write_field_index(field.hir_id, i, Vec::new());
+                self.write_field_index(field.hir_id, i);
 
                 // We don't look at stability attributes on
                 // struct-like enums (yet...), but it's definitely not
@@ -2552,35 +2550,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         base_def: ty::AdtDef<'tcx>,
         ident: Ident,
-        nested_fields: &mut SmallVec<[(FieldIdx, &'tcx ty::FieldDef); 1]>,
-    ) -> bool {
+    ) -> Option<(FieldIdx, &'tcx ty::FieldDef)> {
         // No way to find a field in an enum.
         if base_def.is_enum() {
-            return false;
+            return None;
         }
 
         for (field_idx, field) in base_def.non_enum_variant().fields.iter_enumerated() {
-            if field.is_unnamed() {
-                // We have an unnamed field, recurse into the nested ADT to find `ident`.
-                // If we find it there, return immediately, and `nested_fields` will contain the
-                // correct path.
-                nested_fields.push((field_idx, field));
-
-                let field_ty = self.tcx.type_of(field.did).instantiate_identity();
-                let adt_def = field_ty.ty_adt_def().expect("expect Adt for unnamed field");
-                if self.find_adt_field(adt_def, ident, &mut *nested_fields) {
-                    return true;
-                }
-
-                nested_fields.pop();
-            } else if field.ident(self.tcx).normalize_to_macros_2_0() == ident {
+            if field.ident(self.tcx).normalize_to_macros_2_0() == ident {
                 // We found the field we wanted.
-                nested_fields.push((field_idx, field));
-                return true;
+                return Some((field_idx, field));
             }
         }
 
-        false
+        None
     }
 
     // Check field access expressions
@@ -2610,34 +2593,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         return Ty::new_error(self.tcx(), guar);
                     }
 
-                    let mut field_path = SmallVec::new();
-                    if self.find_adt_field(*base_def, ident, &mut field_path) {
-                        let (first_idx, _) = field_path[0];
-                        let (_, last_field) = field_path.last().unwrap();
-
-                        // Save the index of all fields regardless of their visibility in case
-                        // of error recovery.
-                        let nested_fields = field_path[..]
-                            .array_windows()
-                            .map(|[(_, outer), (inner_idx, _)]| {
-                                let outer_ty = self.field_ty(expr.span, outer, args);
-                                (outer_ty, *inner_idx)
-                            })
-                            .collect();
-                        self.write_field_index(expr.hir_id, first_idx, nested_fields);
+                    if let Some((idx, field)) = self.find_adt_field(*base_def, ident) {
+                        self.write_field_index(expr.hir_id, idx);
 
                         let adjustments = self.adjust_steps(&autoderef);
-                        if last_field.vis.is_accessible_from(def_scope, self.tcx) {
+                        if field.vis.is_accessible_from(def_scope, self.tcx) {
                             self.apply_adjustments(base, adjustments);
                             self.register_predicates(autoderef.into_obligations());
 
-                            self.tcx.check_stability(
-                                last_field.did,
-                                Some(expr.hir_id),
-                                expr.span,
-                                None,
-                            );
-                            return self.field_ty(expr.span, last_field, args);
+                            self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span, None);
+                            return self.field_ty(expr.span, field, args);
                         }
 
                         // The field is not accessible, fall through to error reporting.
@@ -2652,11 +2617,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 self.apply_adjustments(base, adjustments);
                                 self.register_predicates(autoderef.into_obligations());
 
-                                self.write_field_index(
-                                    expr.hir_id,
-                                    FieldIdx::from_usize(index),
-                                    Vec::new(),
-                                );
+                                self.write_field_index(expr.hir_id, FieldIdx::from_usize(index));
                                 return field_ty;
                             }
                         }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 380d9126964..eccb18ad6c4 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -165,16 +165,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    pub(crate) fn write_field_index(
-        &self,
-        hir_id: HirId,
-        index: FieldIdx,
-        nested_fields: Vec<(Ty<'tcx>, FieldIdx)>,
-    ) {
+    pub(crate) fn write_field_index(&self, hir_id: HirId, index: FieldIdx) {
         self.typeck_results.borrow_mut().field_indices_mut().insert(hir_id, index);
-        if !nested_fields.is_empty() {
-            self.typeck_results.borrow_mut().nested_fields_mut().insert(hir_id, nested_fields);
-        }
     }
 
     #[instrument(level = "debug", skip(self))]
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index fa471647d02..bf8cc462189 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -658,7 +658,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 && fn_sig.inputs()[1..]
                     .iter()
                     .zip(input_types.iter())
-                    .all(|(expected, found)| self.can_coerce(*expected, *found))
+                    .all(|(expected, found)| self.may_coerce(*expected, *found))
                 && fn_sig.inputs()[1..].len() == input_types.len()
             {
                 err.span_suggestion_verbose(
@@ -722,7 +722,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             let expectation = Expectation::rvalue_hint(self, expected_input_ty);
             let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty);
-            let can_coerce = self.can_coerce(arg_ty, coerced_ty);
+            let can_coerce = self.may_coerce(arg_ty, coerced_ty);
             if !can_coerce {
                 return Compatibility::Incompatible(Some(ty::error::TypeError::Sorts(
                     ty::error::ExpectedFound::new(true, coerced_ty, arg_ty),
@@ -802,7 +802,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx + tys.len()),
                     ),
                 ) {
-                    if !self.can_coerce(provided_ty, *expected_ty) {
+                    if !self.may_coerce(provided_ty, *expected_ty) {
                         satisfied = false;
                         break;
                     }
@@ -1023,7 +1023,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 std::iter::zip(formal_and_expected_inputs.iter(), removed_arg_tys.iter()).all(
                     |((expected_ty, _), (provided_ty, _))| {
                         !provided_ty.references_error()
-                            && self.can_coerce(*provided_ty, *expected_ty)
+                            && self.may_coerce(*provided_ty, *expected_ty)
                     },
                 )
             };
@@ -1114,7 +1114,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 None,
                                 Some(trace.values),
                                 e,
-                                false,
                                 true,
                             );
                         }
@@ -2124,7 +2123,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                     let expr_ty = self.typeck_results.borrow().expr_ty(expr);
                                     let return_ty = fn_sig.output();
                                     if !matches!(expr.kind, hir::ExprKind::Ret(..))
-                                        && self.can_coerce(expr_ty, return_ty)
+                                        && self.may_coerce(expr_ty, return_ty)
                                     {
                                         found_semi = true;
                                     }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs
index 2aad9a043f9..693cb4465cc 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs
@@ -1,6 +1,6 @@
 //! A utility module to inspect currently ambiguous obligations in the current context.
 
-use rustc_infer::traits::{self, ObligationCause};
+use rustc_infer::traits::{self, ObligationCause, PredicateObligations};
 use rustc_middle::traits::solve::Goal;
 use rustc_middle::ty::{self, Ty, TypeVisitableExt};
 use rustc_span::Span;
@@ -15,10 +15,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Returns a list of all obligations whose self type has been unified
     /// with the unconstrained type `self_ty`.
     #[instrument(skip(self), level = "debug")]
-    pub(crate) fn obligations_for_self_ty(
-        &self,
-        self_ty: ty::TyVid,
-    ) -> Vec<traits::PredicateObligation<'tcx>> {
+    pub(crate) fn obligations_for_self_ty(&self, self_ty: ty::TyVid) -> PredicateObligations<'tcx> {
         if self.next_trait_solver() {
             self.obligations_for_self_ty_next(self_ty)
         } else {
@@ -75,10 +72,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub(crate) fn obligations_for_self_ty_next(
         &self,
         self_ty: ty::TyVid,
-    ) -> Vec<traits::PredicateObligation<'tcx>> {
+    ) -> PredicateObligations<'tcx> {
         let obligations = self.fulfillment_cx.borrow().pending_obligations();
         debug!(?obligations);
-        let mut obligations_for_self_ty = vec![];
+        let mut obligations_for_self_ty = PredicateObligations::new();
         for obligation in obligations {
             let mut visitor = NestedObligationsForSelfTy {
                 fcx: self,
@@ -103,7 +100,7 @@ struct NestedObligationsForSelfTy<'a, 'tcx> {
     fcx: &'a FnCtxt<'a, 'tcx>,
     self_ty: ty::TyVid,
     root_cause: &'a ObligationCause<'tcx>,
-    obligations_for_self_ty: &'a mut Vec<traits::PredicateObligation<'tcx>>,
+    obligations_for_self_ty: &'a mut PredicateObligations<'tcx>,
 }
 
 impl<'a, 'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'a, 'tcx> {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 1df4d32f3cb..3f703c2fcf6 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -261,7 +261,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if let hir::ExprKind::MethodCall(hir::PathSegment { ident: method, .. }, recv_expr, &[], _) =
             expr.kind
             && let Some(recv_ty) = self.typeck_results.borrow().expr_ty_opt(recv_expr)
-            && self.can_coerce(recv_ty, expected)
+            && self.may_coerce(recv_ty, expected)
             && let name = method.name.as_str()
             && (name.starts_with("to_") || name.starts_with("as_") || name == "into")
         {
@@ -349,7 +349,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return true;
         }
 
-        if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
+        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)
         {
@@ -568,7 +568,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if self.tcx.hir().is_inside_const_context(hir_id) || !expected.is_box() || found.is_box() {
             return false;
         }
-        if self.can_coerce(Ty::new_box(self.tcx, found), expected) {
+        if self.may_coerce(Ty::new_box(self.tcx, found), expected) {
             let suggest_boxing = match found.kind() {
                 ty::Tuple(tuple) if tuple.is_empty() => {
                     errors::SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span }
@@ -663,7 +663,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
         match expected.kind() {
             ty::Adt(def, _) if Some(def.did()) == pin_did => {
-                if self.can_coerce(pin_box_found, expected) {
+                if self.may_coerce(pin_box_found, expected) {
                     debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected);
                     match found.kind() {
                         ty::Adt(def, _) if def.is_box() => {
@@ -689,7 +689,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         }
                     }
                     true
-                } else if self.can_coerce(pin_found, expected) {
+                } else if self.may_coerce(pin_found, expected) {
                     match found.kind() {
                         ty::Adt(def, _) if def.is_box() => {
                             err.help("use `Box::pin`");
@@ -701,7 +701,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     false
                 }
             }
-            ty::Adt(def, _) if def.is_box() && self.can_coerce(box_found, expected) => {
+            ty::Adt(def, _) if def.is_box() && self.may_coerce(box_found, expected) => {
                 // Check if the parent expression is a call to Pin::new. If it
                 // is and we were expecting a Box, ergo Pin<Box<expected>>, we
                 // can suggest Box::pin.
@@ -849,7 +849,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             hir::FnRetTy::Return(hir_ty) => {
                 if let hir::TyKind::OpaqueDef(op_ty, ..) = hir_ty.kind
                     // FIXME: account for RPITIT.
-                    && let [hir::GenericBound::Trait(trait_ref, _)] = op_ty.bounds
+                    && let [hir::GenericBound::Trait(trait_ref)] = op_ty.bounds
                     && let Some(hir::PathSegment { args: Some(generic_args), .. }) =
                         trait_ref.trait_ref.path.segments.last()
                     && let [constraint] = generic_args.constraints
@@ -884,7 +884,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     let ty = Binder::bind_with_vars(ty, bound_vars);
                     let ty = self.normalize(hir_ty.span, ty);
                     let ty = self.tcx.instantiate_bound_regions_with_erased(ty);
-                    if self.can_coerce(expected, ty) {
+                    if self.may_coerce(expected, ty) {
                         err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other {
                             span: hir_ty.span,
                             expected,
@@ -1035,7 +1035,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // extract all bounds from the source code using their spans
         let all_matching_bounds_strs = predicates_from_where
             .filter_map(|bound| match bound {
-                GenericBound::Trait(_, _) => {
+                GenericBound::Trait(_) => {
                     self.tcx.sess.source_map().span_to_snippet(bound.span()).ok()
                 }
                 _ => None,
@@ -1141,12 +1141,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     ty::Asyncness::No => ty,
                 };
                 let ty = self.normalize(expr.span, ty);
-                self.can_coerce(found, ty)
+                self.may_coerce(found, ty)
             }
             hir::FnRetTy::DefaultReturn(_) if in_closure => {
                 self.ret_coercion.as_ref().map_or(false, |ret| {
                     let ret_ty = ret.borrow().expected_ty();
-                    self.can_coerce(found, ret_ty)
+                    self.may_coerce(found, ret_ty)
                 })
             }
             _ => false,
@@ -1510,7 +1510,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             provided_ty
         };
 
-        if !self.can_coerce(expected_ty, dummy_ty) {
+        if !self.may_coerce(expected_ty, dummy_ty) {
             return;
         }
         let msg = format!("use `{adt_name}::map_or` to deref inner value of `{adt_name}`");
@@ -1534,7 +1534,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected_ty: Ty<'tcx>,
     ) {
         if let ty::Slice(elem_ty) | ty::Array(elem_ty, _) = expected_ty.kind() {
-            if self.can_coerce(blk_ty, *elem_ty)
+            if self.may_coerce(blk_ty, *elem_ty)
                 && blk.stmts.is_empty()
                 && blk.rules == hir::BlockCheckMode::DefaultBlock
             {
@@ -1744,7 +1744,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if item_ty.has_param() {
             return false;
         }
-        if self.can_coerce(item_ty, expected_ty) {
+        if self.may_coerce(item_ty, expected_ty) {
             err.span_suggestion_verbose(
                 segment.ident.span,
                 format!("try referring to the associated const `{capitalized_name}` instead",),
@@ -1804,7 +1804,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // diagnostic in cases where we have `(&&T).clone()` and we expect `T`).
             && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..)))
             // Check that we're in fact trying to clone into the expected type
-            && self.can_coerce(*pointee_ty, expected_ty)
+            && self.may_coerce(*pointee_ty, expected_ty)
             && let trait_ref = ty::TraitRef::new(self.tcx, clone_trait_did, [expected_ty])
             // And the expected type doesn't implement `Clone`
             && !self.predicate_must_hold_considering_regions(&traits::Obligation::new(
@@ -2022,7 +2022,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             } else {
                 return false;
             };
-        if is_ctor || !self.can_coerce(args.type_at(0), expected) {
+        if is_ctor || !self.may_coerce(args.type_at(0), expected) {
             return false;
         }
 
@@ -2293,7 +2293,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         .then(|| " (its field is private, but it's local to this crate and its privacy can be changed)".to_string());
 
                     let sole_field_ty = sole_field.ty(self.tcx, args);
-                    if self.can_coerce(expr_ty, sole_field_ty) {
+                    if self.may_coerce(expr_ty, sole_field_ty) {
                         let variant_path =
                             with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
                         // FIXME #56861: DRYer prelude filtering
@@ -2401,7 +2401,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         let int_type = args.type_at(0);
-        if !self.can_coerce(expr_ty, int_type) {
+        if !self.may_coerce(expr_ty, int_type) {
             return false;
         }
 
@@ -2585,7 +2585,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, checked_ty)
                     }
                 };
-                if self.can_coerce(ref_ty, expected) {
+                if self.may_coerce(ref_ty, expected) {
                     let mut sugg_sp = sp;
                     if let hir::ExprKind::MethodCall(segment, receiver, args, _) = expr.kind {
                         let clone_trait =
diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs
index a4121adf628..8a7005ac328 100644
--- a/compiler/rustc_hir_typeck/src/intrinsicck.rs
+++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs
@@ -1,4 +1,5 @@
 use hir::HirId;
+use rustc_abi::Primitive::Pointer;
 use rustc_errors::codes::*;
 use rustc_errors::struct_span_code_err;
 use rustc_hir as hir;
@@ -6,7 +7,7 @@ use rustc_index::Idx;
 use rustc_middle::bug;
 use rustc_middle::ty::layout::{LayoutError, SizeSkeleton};
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
-use rustc_target::abi::{Pointer, VariantIdx};
+use rustc_target::abi::VariantIdx;
 use tracing::trace;
 
 use super::FnCtxt;
diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs
index cb8b1df2c6e..69b9be00276 100644
--- a/compiler/rustc_hir_typeck/src/method/mod.rs
+++ b/compiler/rustc_hir_typeck/src/method/mod.rs
@@ -12,6 +12,7 @@ use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Namespace};
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::{self, InferOk};
+use rustc_infer::traits::PredicateObligations;
 use rustc_middle::query::Providers;
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::{
@@ -199,6 +200,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             self_ty, segment, span, call_expr, self_expr, &pick, args,
         );
 
+        // NOTE: on the failure path, we also record the possibly-used trait methods
+        // since an unused import warning is kinda distracting from the method error.
         for &import_id in &pick.import_ids {
             debug!("used_trait_import: {:?}", import_id);
             self.typeck_results.borrow_mut().used_trait_imports.insert(import_id);
@@ -412,7 +415,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         debug!("lookup_in_trait_adjusted: method_item={:?}", method_item);
-        let mut obligations = vec![];
+        let mut obligations = PredicateObligations::new();
 
         // FIXME(effects): revisit when binops get `#[const_trait]`
 
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 4f726f3ed38..9dd59541148 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -191,6 +191,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Expectation<'tcx>,
         trait_missing_method: bool,
     ) -> ErrorGuaranteed {
+        // NOTE: Reporting a method error should also suppress any unused trait errors,
+        // since the method error is very possibly the reason why the trait wasn't used.
+        for &import_id in
+            self.tcx.in_scope_traits(call_id).into_iter().flatten().flat_map(|c| &c.import_ids)
+        {
+            self.typeck_results.borrow_mut().used_trait_imports.insert(import_id);
+        }
+
         let (span, sugg_span, source, item_name, args) = match self.tcx.hir_node(call_id) {
             hir::Node::Expr(&hir::Expr {
                 kind: hir::ExprKind::MethodCall(segment, rcvr, args, _),
@@ -952,7 +960,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         continue;
                     }
                     unimplemented_traits.entry(p.trait_ref.def_id).or_insert((
-                        predicate.kind().rebind(p.trait_ref),
+                        predicate.kind().rebind(p),
                         Obligation {
                             cause: cause.clone(),
                             param_env: self.param_env,
@@ -1926,7 +1934,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             && fn_sig.inputs()[1..]
                                 .iter()
                                 .zip(args.into_iter())
-                                .all(|(expected, found)| self.can_coerce(*expected, *found))
+                                .all(|(expected, found)| self.may_coerce(*expected, *found))
                             && fn_sig.inputs()[1..].len() == args.len()
                         {
                             err.span_suggestion_verbose(
@@ -4140,7 +4148,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return false;
         };
 
-        if !self.can_coerce(output, expected) {
+        if !self.may_coerce(output, expected) {
             return false;
         }
 
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 49c5a7d8a65..e961752b24c 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -690,16 +690,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                     BindingMode(def_br, Mutability::Mut)
                 } else {
-                    // `mut` resets binding mode on edition <= 2021
-                    self.typeck_results
+                    // `mut` resets the binding mode on edition <= 2021
+                    *self
+                        .typeck_results
                         .borrow_mut()
                         .rust_2024_migration_desugared_pats_mut()
-                        .insert(pat_info.top_info.hir_id);
+                        .entry(pat_info.top_info.hir_id)
+                        .or_default() |= pat.span.at_least_rust_2024();
                     BindingMode(ByRef::No, Mutability::Mut)
                 }
             }
             BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl),
-            BindingMode(ByRef::Yes(_), _) => user_bind_annot,
+            BindingMode(ByRef::Yes(_), _) => {
+                if matches!(def_br, ByRef::Yes(_)) {
+                    // `ref`/`ref mut` overrides the binding mode on edition <= 2021
+                    *self
+                        .typeck_results
+                        .borrow_mut()
+                        .rust_2024_migration_desugared_pats_mut()
+                        .entry(pat_info.top_info.hir_id)
+                        .or_default() |= pat.span.at_least_rust_2024();
+                }
+                user_bind_annot
+            }
         };
 
         if bm.0 == ByRef::Yes(Mutability::Mut)
@@ -1513,8 +1526,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     field_map
                         .get(&ident)
                         .map(|(i, f)| {
-                            // FIXME: handle nested fields
-                            self.write_field_index(field.hir_id, *i, Vec::new());
+                            self.write_field_index(field.hir_id, *i);
                             self.tcx.check_stability(f.did, Some(pat.hir_id), span, None);
                             self.field_ty(span, f, args)
                         })
@@ -1768,7 +1780,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 } else if inexistent_fields.len() == 1 {
                     match pat_field.pat.kind {
                         PatKind::Lit(expr)
-                            if !self.can_coerce(
+                            if !self.may_coerce(
                                 self.typeck_results.borrow().expr_ty(expr),
                                 self.field_ty(field.span, field_def, args),
                             ) => {}
@@ -2205,14 +2217,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         } else {
             // Reset binding mode on old editions
-
             if pat_info.binding_mode != ByRef::No {
                 pat_info.binding_mode = ByRef::No;
-
-                self.typeck_results
+                *self
+                    .typeck_results
                     .borrow_mut()
                     .rust_2024_migration_desugared_pats_mut()
-                    .insert(pat_info.top_info.hir_id);
+                    .entry(pat_info.top_info.hir_id)
+                    .or_default() |= pat.span.at_least_rust_2024();
             }
         }
 
@@ -2263,6 +2275,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 (err, err)
             }
         };
+
         self.check_pat(inner, inner_ty, pat_info);
         ref_ty
     }
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index 3254dddaee9..d3d092be222 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -588,11 +588,6 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
         {
             self.typeck_results.field_indices_mut().insert(hir_id, index);
         }
-        if let Some(nested_fields) =
-            self.fcx.typeck_results.borrow_mut().nested_fields_mut().remove(hir_id)
-        {
-            self.typeck_results.nested_fields_mut().insert(hir_id, nested_fields);
-        }
     }
 
     #[instrument(skip(self, span), level = "debug")]
@@ -640,7 +635,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
 
     #[instrument(skip(self), level = "debug")]
     fn visit_rust_2024_migration_desugared_pats(&mut self, hir_id: hir::HirId) {
-        if self
+        if let Some(is_hard_error) = self
             .fcx
             .typeck_results
             .borrow_mut()
@@ -650,7 +645,9 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
             debug!(
                 "node is a pat whose match ergonomics are desugared by the Rust 2024 migration lint"
             );
-            self.typeck_results.rust_2024_migration_desugared_pats_mut().insert(hir_id);
+            self.typeck_results
+                .rust_2024_migration_desugared_pats_mut()
+                .insert(hir_id, is_hard_error);
         }
     }
 
diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs
index 52f354b8eca..cae55230b06 100644
--- a/compiler/rustc_index/src/lib.rs
+++ b/compiler/rustc_index/src/lib.rs
@@ -17,6 +17,7 @@ mod vec;
 pub use idx::Idx;
 pub use rustc_index_macros::newtype_index;
 pub use slice::IndexSlice;
+#[doc(no_inline)]
 pub use vec::IndexVec;
 
 /// Type size assertion. The first argument is a type and the second argument is its expected size.
diff --git a/compiler/rustc_index_macros/Cargo.toml b/compiler/rustc_index_macros/Cargo.toml
index 07ee81788ce..a7c2a1804dd 100644
--- a/compiler/rustc_index_macros/Cargo.toml
+++ b/compiler/rustc_index_macros/Cargo.toml
@@ -7,7 +7,7 @@ edition = "2021"
 proc-macro = true
 
 [dependencies]
-syn = { version = "2.0.9", features = ["full"] }
+syn = { version = "2.0.9", features = ["full", "extra-traits"] }
 proc-macro2 = "1"
 quote = "1"
 
diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml
index 1f616710200..ef5a1468c87 100644
--- a/compiler/rustc_infer/Cargo.toml
+++ b/compiler/rustc_infer/Cargo.toml
@@ -21,5 +21,6 @@ rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 rustc_type_ir = { path = "../rustc_type_ir" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
+thin-vec = "0.2.12"
 tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs
index 5422761a6cf..8c943a961e7 100644
--- a/compiler/rustc_infer/src/infer/at.rs
+++ b/compiler/rustc_infer/src/infer/at.rs
@@ -27,11 +27,14 @@
 
 use relate::lattice::{LatticeOp, LatticeOpKind};
 use rustc_middle::bug;
+use rustc_middle::ty::relate::solver_relating::RelateExt as NextSolverRelate;
 use rustc_middle::ty::{Const, ImplSubject};
 
 use super::*;
 use crate::infer::relate::type_relating::TypeRelating;
-use crate::infer::relate::{Relate, StructurallyRelateAliases, TypeRelation};
+use crate::infer::relate::{Relate, TypeRelation};
+use crate::traits::Obligation;
+use crate::traits::solve::Goal;
 
 /// Whether we should define opaque types or just treat them opaquely.
 ///
@@ -109,16 +112,26 @@ impl<'a, 'tcx> At<'a, 'tcx> {
     where
         T: ToTrace<'tcx>,
     {
-        let mut op = TypeRelating::new(
-            self.infcx,
-            ToTrace::to_trace(self.cause, expected, actual),
-            self.param_env,
-            define_opaque_types,
-            StructurallyRelateAliases::No,
-            ty::Contravariant,
-        );
-        op.relate(expected, actual)?;
-        Ok(InferOk { value: (), obligations: op.into_obligations() })
+        if self.infcx.next_trait_solver {
+            NextSolverRelate::relate(
+                self.infcx,
+                self.param_env,
+                expected,
+                ty::Contravariant,
+                actual,
+            )
+            .map(|goals| self.goals_to_obligations(goals))
+        } else {
+            let mut op = TypeRelating::new(
+                self.infcx,
+                ToTrace::to_trace(self.cause, expected, actual),
+                self.param_env,
+                define_opaque_types,
+                ty::Contravariant,
+            );
+            op.relate(expected, actual)?;
+            Ok(InferOk { value: (), obligations: op.into_obligations() })
+        }
     }
 
     /// Makes `expected <: actual`.
@@ -131,16 +144,20 @@ impl<'a, 'tcx> At<'a, 'tcx> {
     where
         T: ToTrace<'tcx>,
     {
-        let mut op = TypeRelating::new(
-            self.infcx,
-            ToTrace::to_trace(self.cause, expected, actual),
-            self.param_env,
-            define_opaque_types,
-            StructurallyRelateAliases::No,
-            ty::Covariant,
-        );
-        op.relate(expected, actual)?;
-        Ok(InferOk { value: (), obligations: op.into_obligations() })
+        if self.infcx.next_trait_solver {
+            NextSolverRelate::relate(self.infcx, self.param_env, expected, ty::Covariant, actual)
+                .map(|goals| self.goals_to_obligations(goals))
+        } else {
+            let mut op = TypeRelating::new(
+                self.infcx,
+                ToTrace::to_trace(self.cause, expected, actual),
+                self.param_env,
+                define_opaque_types,
+                ty::Covariant,
+            );
+            op.relate(expected, actual)?;
+            Ok(InferOk { value: (), obligations: op.into_obligations() })
+        }
     }
 
     /// Makes `expected == actual`.
@@ -172,16 +189,20 @@ impl<'a, 'tcx> At<'a, 'tcx> {
     where
         T: Relate<TyCtxt<'tcx>>,
     {
-        let mut op = TypeRelating::new(
-            self.infcx,
-            trace,
-            self.param_env,
-            define_opaque_types,
-            StructurallyRelateAliases::No,
-            ty::Invariant,
-        );
-        op.relate(expected, actual)?;
-        Ok(InferOk { value: (), obligations: op.into_obligations() })
+        if self.infcx.next_trait_solver {
+            NextSolverRelate::relate(self.infcx, self.param_env, expected, ty::Invariant, actual)
+                .map(|goals| self.goals_to_obligations(goals))
+        } else {
+            let mut op = TypeRelating::new(
+                self.infcx,
+                trace,
+                self.param_env,
+                define_opaque_types,
+                ty::Invariant,
+            );
+            op.relate(expected, actual)?;
+            Ok(InferOk { value: (), obligations: op.into_obligations() })
+        }
     }
 
     pub fn relate<T>(
@@ -208,49 +229,6 @@ impl<'a, 'tcx> At<'a, 'tcx> {
         }
     }
 
-    /// Used in the new solver since we don't care about tracking an `ObligationCause`.
-    pub fn relate_no_trace<T>(
-        self,
-        expected: T,
-        variance: ty::Variance,
-        actual: T,
-    ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution>
-    where
-        T: Relate<TyCtxt<'tcx>>,
-    {
-        let mut op = TypeRelating::new(
-            self.infcx,
-            TypeTrace::dummy(self.cause),
-            self.param_env,
-            DefineOpaqueTypes::Yes,
-            StructurallyRelateAliases::No,
-            variance,
-        );
-        op.relate(expected, actual)?;
-        Ok(op.into_obligations().into_iter().map(|o| o.into()).collect())
-    }
-
-    /// Used in the new solver since we don't care about tracking an `ObligationCause`.
-    pub fn eq_structurally_relating_aliases_no_trace<T>(
-        self,
-        expected: T,
-        actual: T,
-    ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution>
-    where
-        T: Relate<TyCtxt<'tcx>>,
-    {
-        let mut op = TypeRelating::new(
-            self.infcx,
-            TypeTrace::dummy(self.cause),
-            self.param_env,
-            DefineOpaqueTypes::Yes,
-            StructurallyRelateAliases::Yes,
-            ty::Invariant,
-        );
-        op.relate(expected, actual)?;
-        Ok(op.into_obligations().into_iter().map(|o| o.into()).collect())
-    }
-
     /// Computes the least-upper-bound, or mutual supertype, of two
     /// values. The order of the arguments doesn't matter, but since
     /// this can result in an error (e.g., if asked to compute LUB of
@@ -269,6 +247,26 @@ impl<'a, 'tcx> At<'a, 'tcx> {
         let value = op.relate(expected, actual)?;
         Ok(InferOk { value, obligations: op.into_obligations() })
     }
+
+    fn goals_to_obligations(
+        &self,
+        goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
+    ) -> InferOk<'tcx, ()> {
+        InferOk {
+            value: (),
+            obligations: goals
+                .into_iter()
+                .map(|goal| {
+                    Obligation::new(
+                        self.infcx.tcx,
+                        self.cause.clone(),
+                        goal.param_env,
+                        goal.predicate,
+                    )
+                })
+                .collect(),
+        }
+    }
 }
 
 impl<'tcx> ToTrace<'tcx> for ImplSubject<'tcx> {
diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs
index 7791bd73628..1d3d32ef749 100644
--- a/compiler/rustc_infer/src/infer/canonical/query_response.rs
+++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs
@@ -28,7 +28,8 @@ use crate::infer::region_constraints::{Constraint, RegionConstraintData};
 use crate::infer::{DefineOpaqueTypes, InferCtxt, InferOk, InferResult};
 use crate::traits::query::NoSolution;
 use crate::traits::{
-    Obligation, ObligationCause, PredicateObligation, ScrubbedTraitError, TraitEngine,
+    Obligation, ObligationCause, PredicateObligation, PredicateObligations, ScrubbedTraitError,
+    TraitEngine,
 };
 
 impl<'tcx> InferCtxt<'tcx> {
@@ -493,7 +494,7 @@ impl<'tcx> InferCtxt<'tcx> {
             ),
         };
 
-        let mut obligations = vec![];
+        let mut obligations = PredicateObligations::new();
 
         // Carry all newly resolved opaque types to the caller's scope
         for &(a, b) in &query_response.value.opaque_types {
@@ -598,7 +599,7 @@ impl<'tcx> InferCtxt<'tcx> {
         variables1: &OriginalQueryValues<'tcx>,
         variables2: impl Fn(BoundVar) -> GenericArg<'tcx>,
     ) -> InferResult<'tcx, ()> {
-        let mut obligations = vec![];
+        let mut obligations = PredicateObligations::new();
         for (index, value1) in variables1.var_values.iter().enumerate() {
             let value2 = variables2(BoundVar::new(index));
 
diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs
index c06836edcfb..57007752cad 100644
--- a/compiler/rustc_infer/src/infer/context.rs
+++ b/compiler/rustc_infer/src/infer/context.rs
@@ -1,22 +1,27 @@
 ///! Definition of `InferCtxtLike` from the librarified type layer.
 use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_middle::infer::unify_key::EffectVarValue;
 use rustc_middle::traits::ObligationCause;
-use rustc_middle::traits::solve::{Goal, NoSolution, SolverMode};
+use rustc_middle::traits::solve::SolverMode;
 use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::ty::relate::RelateResult;
+use rustc_middle::ty::relate::combine::PredicateEmittingRelation;
 use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_span::DUMMY_SP;
-use rustc_type_ir::InferCtxtLike;
-use rustc_type_ir::relate::Relate;
+use rustc_span::{DUMMY_SP, ErrorGuaranteed};
 
 use super::{BoundRegionConversionTime, InferCtxt, SubregionOrigin};
 
-impl<'tcx> InferCtxtLike for InferCtxt<'tcx> {
+impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
     type Interner = TyCtxt<'tcx>;
 
     fn cx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
 
+    fn next_trait_solver(&self) -> bool {
+        self.next_trait_solver
+    }
+
     fn solver_mode(&self) -> ty::solve::SolverMode {
         match self.intercrate {
             true => SolverMode::Coherence,
@@ -131,29 +136,86 @@ impl<'tcx> InferCtxtLike for InferCtxt<'tcx> {
         self.enter_forall(value, f)
     }
 
-    fn relate<T: Relate<TyCtxt<'tcx>>>(
+    fn equate_ty_vids_raw(&self, a: rustc_type_ir::TyVid, b: rustc_type_ir::TyVid) {
+        self.inner.borrow_mut().type_variables().equate(a, b);
+    }
+
+    fn equate_int_vids_raw(&self, a: rustc_type_ir::IntVid, b: rustc_type_ir::IntVid) {
+        self.inner.borrow_mut().int_unification_table().union(a, b);
+    }
+
+    fn equate_float_vids_raw(&self, a: rustc_type_ir::FloatVid, b: rustc_type_ir::FloatVid) {
+        self.inner.borrow_mut().float_unification_table().union(a, b);
+    }
+
+    fn equate_const_vids_raw(&self, a: rustc_type_ir::ConstVid, b: rustc_type_ir::ConstVid) {
+        self.inner.borrow_mut().const_unification_table().union(a, b);
+    }
+
+    fn equate_effect_vids_raw(&self, a: rustc_type_ir::EffectVid, b: rustc_type_ir::EffectVid) {
+        self.inner.borrow_mut().effect_unification_table().union(a, b);
+    }
+
+    fn instantiate_ty_var_raw<R: PredicateEmittingRelation<Self>>(
         &self,
-        param_env: ty::ParamEnv<'tcx>,
-        lhs: T,
-        variance: ty::Variance,
-        rhs: T,
-    ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> {
-        self.at(&ObligationCause::dummy(), param_env).relate_no_trace(lhs, variance, rhs)
+        relation: &mut R,
+        target_is_expected: bool,
+        target_vid: rustc_type_ir::TyVid,
+        instantiation_variance: rustc_type_ir::Variance,
+        source_ty: Ty<'tcx>,
+    ) -> RelateResult<'tcx, ()> {
+        self.instantiate_ty_var(
+            relation,
+            target_is_expected,
+            target_vid,
+            instantiation_variance,
+            source_ty,
+        )
+    }
+
+    fn instantiate_int_var_raw(
+        &self,
+        vid: rustc_type_ir::IntVid,
+        value: rustc_type_ir::IntVarValue,
+    ) {
+        self.inner.borrow_mut().int_unification_table().union_value(vid, value);
+    }
+
+    fn instantiate_float_var_raw(
+        &self,
+        vid: rustc_type_ir::FloatVid,
+        value: rustc_type_ir::FloatVarValue,
+    ) {
+        self.inner.borrow_mut().float_unification_table().union_value(vid, value);
+    }
+
+    fn instantiate_effect_var_raw(&self, vid: rustc_type_ir::EffectVid, value: ty::Const<'tcx>) {
+        self.inner
+            .borrow_mut()
+            .effect_unification_table()
+            .union_value(vid, EffectVarValue::Known(value));
     }
 
-    fn eq_structurally_relating_aliases<T: Relate<TyCtxt<'tcx>>>(
+    fn instantiate_const_var_raw<R: PredicateEmittingRelation<Self>>(
         &self,
-        param_env: ty::ParamEnv<'tcx>,
-        lhs: T,
-        rhs: T,
-    ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> {
-        self.at(&ObligationCause::dummy(), param_env)
-            .eq_structurally_relating_aliases_no_trace(lhs, rhs)
+        relation: &mut R,
+        target_is_expected: bool,
+        target_vid: rustc_type_ir::ConstVid,
+        source_ct: ty::Const<'tcx>,
+    ) -> RelateResult<'tcx, ()> {
+        self.instantiate_const_var(relation, target_is_expected, target_vid, source_ct)
+    }
+
+    fn set_tainted_by_errors(&self, e: ErrorGuaranteed) {
+        self.set_tainted_by_errors(e)
     }
 
     fn shallow_resolve(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
         self.shallow_resolve(ty)
     }
+    fn shallow_resolve_const(&self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        self.shallow_resolve_const(ct)
+    }
 
     fn resolve_vars_if_possible<T>(&self, value: T) -> T
     where
@@ -167,7 +229,19 @@ impl<'tcx> InferCtxtLike for InferCtxt<'tcx> {
     }
 
     fn sub_regions(&self, sub: ty::Region<'tcx>, sup: ty::Region<'tcx>) {
-        self.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP, None), sub, sup)
+        self.inner.borrow_mut().unwrap_region_constraints().make_subregion(
+            SubregionOrigin::RelateRegionParamBound(DUMMY_SP, None),
+            sub,
+            sup,
+        );
+    }
+
+    fn equate_regions(&self, a: ty::Region<'tcx>, b: ty::Region<'tcx>) {
+        self.inner.borrow_mut().unwrap_region_constraints().make_eqregion(
+            SubregionOrigin::RelateRegionParamBound(DUMMY_SP, None),
+            a,
+            b,
+        );
     }
 
     fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>) {
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index cb60d9f286b..7ef714475fc 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -18,7 +18,7 @@ pub use relate::combine::PredicateEmittingRelation;
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_data_structures::sync::Lrc;
-use rustc_data_structures::undo_log::Rollback;
+use rustc_data_structures::undo_log::{Rollback, UndoLogs};
 use rustc_data_structures::unify as ut;
 use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed};
 use rustc_hir as hir;
@@ -32,7 +32,6 @@ use rustc_middle::infer::unify_key::{
 use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult};
 use rustc_middle::traits::select;
-use rustc_middle::traits::solve::{Goal, NoSolution};
 pub use rustc_middle::ty::IntVarValue;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::fold::{
@@ -50,7 +49,10 @@ use snapshot::undo_log::InferCtxtUndoLogs;
 use tracing::{debug, instrument};
 use type_variable::TypeVariableOrigin;
 
-use crate::traits::{self, ObligationCause, ObligationInspector, PredicateObligation, TraitEngine};
+use crate::infer::region_constraints::UndoLog;
+use crate::traits::{
+    self, ObligationCause, ObligationInspector, PredicateObligations, TraitEngine,
+};
 
 pub mod at;
 pub mod canonical;
@@ -67,11 +69,18 @@ pub mod resolve;
 pub(crate) mod snapshot;
 mod type_variable;
 
+/// `InferOk<'tcx, ()>` is used a lot. It may seem like a useless wrapper
+/// around `PredicateObligations<'tcx>`, but it has one important property:
+/// because `InferOk` is marked with `#[must_use]`, if you have a method
+/// `InferCtxt::f` that returns `InferResult<'tcx, ()>` and you call it with
+/// `infcx.f()?;` you'll get a warning about the obligations being discarded
+/// without use, which is probably unintentional and has been a source of bugs
+/// in the past.
 #[must_use]
 #[derive(Debug)]
 pub struct InferOk<'tcx, T> {
     pub value: T,
-    pub obligations: Vec<PredicateObligation<'tcx>>,
+    pub obligations: PredicateObligations<'tcx>,
 }
 pub type InferResult<'tcx, T> = Result<InferOk<'tcx, T>, TypeError<'tcx>>;
 
@@ -163,12 +172,12 @@ impl<'tcx> InferCtxtInner<'tcx> {
             undo_log: InferCtxtUndoLogs::default(),
 
             projection_cache: Default::default(),
-            type_variable_storage: type_variable::TypeVariableStorage::new(),
-            const_unification_storage: ut::UnificationTableStorage::new(),
-            int_unification_storage: ut::UnificationTableStorage::new(),
-            float_unification_storage: ut::UnificationTableStorage::new(),
-            effect_unification_storage: ut::UnificationTableStorage::new(),
-            region_constraint_storage: Some(RegionConstraintStorage::new()),
+            type_variable_storage: Default::default(),
+            const_unification_storage: Default::default(),
+            int_unification_storage: Default::default(),
+            float_unification_storage: Default::default(),
+            effect_unification_storage: Default::default(),
+            region_constraint_storage: Some(Default::default()),
             region_obligations: vec![],
             opaque_type_storage: Default::default(),
         }
@@ -332,7 +341,6 @@ pub enum ValuePairs<'tcx> {
     PolySigs(ExpectedFound<ty::PolyFnSig<'tcx>>),
     ExistentialTraitRef(ExpectedFound<ty::PolyExistentialTraitRef<'tcx>>),
     ExistentialProjection(ExpectedFound<ty::PolyExistentialProjection<'tcx>>),
-    Dummy,
 }
 
 impl<'tcx> ValuePairs<'tcx> {
@@ -652,7 +660,7 @@ impl<'tcx, T> InferOk<'tcx, T> {
 }
 
 impl<'tcx> InferOk<'tcx, ()> {
-    pub fn into_obligations(self) -> Vec<PredicateObligation<'tcx>> {
+    pub fn into_obligations(self) -> PredicateObligations<'tcx> {
         self.obligations
     }
 }
@@ -1004,8 +1012,8 @@ impl<'tcx> InferCtxt<'tcx> {
         ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(effect_vid)).into()
     }
 
-    /// Given a set of generics defined on a type or impl, returns the generic parameters mapping each
-    /// type/region parameter to a fresh inference variable.
+    /// Given a set of generics defined on a type or impl, returns the generic parameters mapping
+    /// each type/region parameter to a fresh inference variable.
     pub fn fresh_args_for_item(&self, span: Span, def_id: DefId) -> GenericArgsRef<'tcx> {
         GenericArgs::for_item(self.tcx, def_id, |param, _| self.var_for_def(span, param))
     }
@@ -1036,18 +1044,14 @@ impl<'tcx> InferCtxt<'tcx> {
     /// Clone the list of variable regions. This is used only during NLL processing
     /// to put the set of region variables into the NLL region context.
     pub fn get_region_var_origins(&self) -> VarInfos {
-        let mut inner = self.inner.borrow_mut();
-        let (var_infos, data) = inner
-            .region_constraint_storage
-            // We clone instead of taking because borrowck still wants to use
-            // the inference context after calling this for diagnostics
-            // and the new trait solver.
-            .clone()
-            .expect("regions already resolved")
-            .with_log(&mut inner.undo_log)
-            .into_infos_and_data();
-        assert!(data.is_empty());
-        var_infos
+        let inner = self.inner.borrow();
+        assert!(!UndoLogs::<UndoLog<'_>>::in_snapshot(&inner.undo_log));
+        let storage = inner.region_constraint_storage.as_ref().expect("regions already resolved");
+        assert!(storage.data.is_empty());
+        // We clone instead of taking because borrowck still wants to use the
+        // inference context after calling this for diagnostics and the new
+        // trait solver.
+        storage.var_infos.clone()
     }
 
     #[instrument(level = "debug", skip(self), ret)]
@@ -1383,10 +1387,10 @@ impl<'tcx> InferCtxt<'tcx> {
     ///
     /// The constant can be located on a trait like `<A as B>::C`, in which case the given
     /// generic parameters and environment are used to resolve the constant. Alternatively if the
-    /// constant has generic parameters in scope the instantiations are used to evaluate the value of
-    /// the constant. For example in `fn foo<T>() { let _ = [0; bar::<T>()]; }` the repeat count
-    /// constant `bar::<T>()` requires a instantiation for `T`, if the instantiation for `T` is still
-    /// too generic for the constant to be evaluated then `Err(ErrorHandled::TooGeneric)` is
+    /// constant has generic parameters in scope the instantiations are used to evaluate the value
+    /// of the constant. For example in `fn foo<T>() { let _ = [0; bar::<T>()]; }` the repeat count
+    /// constant `bar::<T>()` requires a instantiation for `T`, if the instantiation for `T` is
+    /// still too generic for the constant to be evaluated then `Err(ErrorHandled::TooGeneric)` is
     /// returned.
     ///
     /// This handles inferences variables within both `param_env` and `args` by
@@ -1634,10 +1638,6 @@ impl<'tcx> TypeTrace<'tcx> {
             values: ValuePairs::Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())),
         }
     }
-
-    fn dummy(cause: &ObligationCause<'tcx>) -> TypeTrace<'tcx> {
-        TypeTrace { cause: cause.clone(), values: ValuePairs::Dummy }
-    }
 }
 
 impl<'tcx> SubregionOrigin<'tcx> {
diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs
index 5a2ffbf029e..853ae6d2641 100644
--- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs
@@ -16,7 +16,7 @@ use tracing::{debug, instrument};
 use super::DefineOpaqueTypes;
 use crate::errors::OpaqueHiddenTypeDiag;
 use crate::infer::{InferCtxt, InferOk};
-use crate::traits::{self, Obligation};
+use crate::traits::{self, Obligation, PredicateObligations};
 
 mod table;
 
@@ -46,14 +46,14 @@ impl<'tcx> InferCtxt<'tcx> {
     ) -> InferOk<'tcx, T> {
         // We handle opaque types differently in the new solver.
         if self.next_trait_solver() {
-            return InferOk { value, obligations: vec![] };
+            return InferOk { value, obligations: PredicateObligations::new() };
         }
 
         if !value.has_opaque_types() {
-            return InferOk { value, obligations: vec![] };
+            return InferOk { value, obligations: PredicateObligations::new() };
         }
 
-        let mut obligations = vec![];
+        let mut obligations = PredicateObligations::new();
         let value = value.fold_with(&mut BottomUpFolder {
             tcx: self.tcx,
             lt_op: |lt| lt,
@@ -148,11 +148,11 @@ impl<'tcx> InferCtxt<'tcx> {
                 }
 
                 if let ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }) = *b.kind() {
-                    // We could accept this, but there are various ways to handle this situation, and we don't
-                    // want to make a decision on it right now. Likely this case is so super rare anyway, that
-                    // no one encounters it in practice.
-                    // It does occur however in `fn fut() -> impl Future<Output = i32> { async { 42 } }`,
-                    // where it is of no concern, so we only check for TAITs.
+                    // We could accept this, but there are various ways to handle this situation,
+                    // and we don't want to make a decision on it right now. Likely this case is so
+                    // super rare anyway, that no one encounters it in practice. It does occur
+                    // however in `fn fut() -> impl Future<Output = i32> { async { 42 } }`, where
+                    // it is of no concern, so we only check for TAITs.
                     if self.can_define_opaque_ty(b_def_id)
                         && self.tcx.is_type_alias_impl_trait(b_def_id)
                     {
diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs
index a071b84a1a0..9300fc574dc 100644
--- a/compiler/rustc_infer/src/infer/outlives/env.rs
+++ b/compiler/rustc_infer/src/infer/outlives/env.rs
@@ -1,8 +1,7 @@
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_data_structures::transitive_relation::TransitiveRelationBuilder;
-use rustc_middle::bug;
-use rustc_middle::ty::{self, Region};
-use tracing::{debug, instrument};
+use rustc_middle::{bug, ty};
+use tracing::debug;
 
 use super::explicit_outlives_bounds;
 use crate::infer::GenericKind;
@@ -54,37 +53,16 @@ pub struct OutlivesEnvironment<'tcx> {
     region_bound_pairs: RegionBoundPairs<'tcx>,
 }
 
-/// Builder of OutlivesEnvironment.
-#[derive(Debug)]
-struct OutlivesEnvironmentBuilder<'tcx> {
-    param_env: ty::ParamEnv<'tcx>,
-    region_relation: TransitiveRelationBuilder<Region<'tcx>>,
-    region_bound_pairs: RegionBoundPairs<'tcx>,
-}
-
 /// "Region-bound pairs" tracks outlives relations that are known to
 /// be true, either because of explicit where-clauses like `T: 'a` or
 /// because of implied bounds.
 pub type RegionBoundPairs<'tcx> = FxIndexSet<ty::OutlivesPredicate<'tcx, GenericKind<'tcx>>>;
 
 impl<'tcx> OutlivesEnvironment<'tcx> {
-    /// Create a builder using `ParamEnv` and add explicit outlives bounds into it.
-    fn builder(param_env: ty::ParamEnv<'tcx>) -> OutlivesEnvironmentBuilder<'tcx> {
-        let mut builder = OutlivesEnvironmentBuilder {
-            param_env,
-            region_relation: Default::default(),
-            region_bound_pairs: Default::default(),
-        };
-
-        builder.add_outlives_bounds(explicit_outlives_bounds(param_env));
-
-        builder
-    }
-
-    #[inline]
     /// Create a new `OutlivesEnvironment` without extra outlives bounds.
+    #[inline]
     pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self {
-        Self::builder(param_env).build()
+        Self::with_bounds(param_env, vec![])
     }
 
     /// Create a new `OutlivesEnvironment` with extra outlives bounds.
@@ -92,56 +70,27 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
         param_env: ty::ParamEnv<'tcx>,
         extra_bounds: impl IntoIterator<Item = OutlivesBound<'tcx>>,
     ) -> Self {
-        let mut builder = Self::builder(param_env);
-        builder.add_outlives_bounds(extra_bounds);
-        builder.build()
-    }
+        let mut region_relation = TransitiveRelationBuilder::default();
+        let mut region_bound_pairs = RegionBoundPairs::default();
 
-    /// Borrows current value of the `free_region_map`.
-    pub fn free_region_map(&self) -> &FreeRegionMap<'tcx> {
-        &self.free_region_map
-    }
-
-    /// Borrows current `region_bound_pairs`.
-    pub fn region_bound_pairs(&self) -> &RegionBoundPairs<'tcx> {
-        &self.region_bound_pairs
-    }
-}
-
-impl<'tcx> OutlivesEnvironmentBuilder<'tcx> {
-    #[inline]
-    #[instrument(level = "debug")]
-    fn build(self) -> OutlivesEnvironment<'tcx> {
-        OutlivesEnvironment {
-            param_env: self.param_env,
-            free_region_map: FreeRegionMap { relation: self.region_relation.freeze() },
-            region_bound_pairs: self.region_bound_pairs,
-        }
-    }
-
-    /// Processes outlives bounds that are known to hold, whether from implied or other sources.
-    fn add_outlives_bounds<I>(&mut self, outlives_bounds: I)
-    where
-        I: IntoIterator<Item = OutlivesBound<'tcx>>,
-    {
         // Record relationships such as `T:'x` that don't go into the
         // free-region-map but which we use here.
-        for outlives_bound in outlives_bounds {
+        for outlives_bound in explicit_outlives_bounds(param_env).chain(extra_bounds) {
             debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound);
             match outlives_bound {
                 OutlivesBound::RegionSubParam(r_a, param_b) => {
-                    self.region_bound_pairs
+                    region_bound_pairs
                         .insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a));
                 }
                 OutlivesBound::RegionSubAlias(r_a, alias_b) => {
-                    self.region_bound_pairs
+                    region_bound_pairs
                         .insert(ty::OutlivesPredicate(GenericKind::Alias(alias_b), r_a));
                 }
                 OutlivesBound::RegionSubRegion(r_a, r_b) => match (*r_a, *r_b) {
                     (
                         ty::ReStatic | ty::ReEarlyParam(_) | ty::ReLateParam(_),
                         ty::ReStatic | ty::ReEarlyParam(_) | ty::ReLateParam(_),
-                    ) => self.region_relation.add(r_a, r_b),
+                    ) => region_relation.add(r_a, r_b),
                     (ty::ReError(_), _) | (_, ty::ReError(_)) => {}
                     // FIXME(#109628): We shouldn't have existential variables in implied bounds.
                     // Panic here once the linked issue is resolved!
@@ -150,5 +99,21 @@ impl<'tcx> OutlivesEnvironmentBuilder<'tcx> {
                 },
             }
         }
+
+        OutlivesEnvironment {
+            param_env,
+            free_region_map: FreeRegionMap { relation: region_relation.freeze() },
+            region_bound_pairs,
+        }
+    }
+
+    /// Borrows current value of the `free_region_map`.
+    pub fn free_region_map(&self) -> &FreeRegionMap<'tcx> {
+        &self.free_region_map
+    }
+
+    /// Borrows current `region_bound_pairs`.
+    pub fn region_bound_pairs(&self) -> &RegionBoundPairs<'tcx> {
+        &self.region_bound_pairs
     }
 }
diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs
index a270f9322f3..e23bb1aaa56 100644
--- a/compiler/rustc_infer/src/infer/outlives/mod.rs
+++ b/compiler/rustc_infer/src/infer/outlives/mod.rs
@@ -1,11 +1,12 @@
 //! Various code related to computing outlives relations.
 
+use rustc_data_structures::undo_log::UndoLogs;
 use rustc_middle::traits::query::{NoSolution, OutlivesBound};
 use rustc_middle::ty;
 use tracing::instrument;
 
 use self::env::OutlivesEnvironment;
-use super::region_constraints::RegionConstraintData;
+use super::region_constraints::{RegionConstraintData, UndoLog};
 use super::{InferCtxt, RegionResolutionError, SubregionOrigin};
 use crate::infer::free_regions::RegionRelations;
 use crate::infer::lexical_region_resolve;
@@ -63,7 +64,7 @@ impl<'tcx> InferCtxt<'tcx> {
             }
         };
 
-        let (var_infos, data) = {
+        let storage = {
             let mut inner = self.inner.borrow_mut();
             let inner = &mut *inner;
             assert!(
@@ -71,18 +72,14 @@ impl<'tcx> InferCtxt<'tcx> {
                 "region_obligations not empty: {:#?}",
                 inner.region_obligations
             );
-            inner
-                .region_constraint_storage
-                .take()
-                .expect("regions already resolved")
-                .with_log(&mut inner.undo_log)
-                .into_infos_and_data()
+            assert!(!UndoLogs::<UndoLog<'_>>::in_snapshot(&inner.undo_log));
+            inner.region_constraint_storage.take().expect("regions already resolved")
         };
 
         let region_rels = &RegionRelations::new(self.tcx, outlives_env.free_region_map());
 
         let (lexical_region_resolutions, errors) =
-            lexical_region_resolve::resolve(region_rels, var_infos, data);
+            lexical_region_resolve::resolve(region_rels, storage.var_infos, storage.data);
 
         let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions));
         assert!(old_value.is_none());
diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs
index 634cda86bc3..e0e03a29220 100644
--- a/compiler/rustc_infer/src/infer/outlives/obligations.rs
+++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs
@@ -396,11 +396,12 @@ where
         // 'a` in the environment but `trait Foo<'b> { type Item: 'b
         // }` in the trait definition.
         approx_env_bounds.retain(|bound_outlives| {
-            // OK to skip binder because we only manipulate and compare against other
-            // values from the same binder. e.g. if we have (e.g.) `for<'a> <T as Trait<'a>>::Item: 'a`
-            // in `bound`, the `'a` will be a `^1` (bound, debruijn index == innermost) region.
-            // If the declaration is `trait Trait<'b> { type Item: 'b; }`, then `projection_declared_bounds_from_trait`
-            // will be invoked with `['b => ^1]` and so we will get `^1` returned.
+            // OK to skip binder because we only manipulate and compare against other values from
+            // the same binder. e.g. if we have (e.g.) `for<'a> <T as Trait<'a>>::Item: 'a` in
+            // `bound`, the `'a` will be a `^1` (bound, debruijn index == innermost) region. If the
+            // declaration is `trait Trait<'b> { type Item: 'b; }`, then
+            // `projection_declared_bounds_from_trait` will be invoked with `['b => ^1]` and so we
+            // will get `^1` returned.
             let bound = bound_outlives.skip_binder();
             let ty::Alias(_, alias_ty) = bound.0.kind() else { bug!("expected AliasTy") };
             self.verify_bound.declared_bounds_from_definition(*alias_ty).all(|r| r != bound.1)
diff --git a/compiler/rustc_infer/src/infer/projection.rs b/compiler/rustc_infer/src/infer/projection.rs
index b78f9d49268..1bee9632110 100644
--- a/compiler/rustc_infer/src/infer/projection.rs
+++ b/compiler/rustc_infer/src/infer/projection.rs
@@ -2,7 +2,7 @@ use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::{self, Ty};
 
 use super::InferCtxt;
-use crate::traits::{Obligation, PredicateObligation};
+use crate::traits::{Obligation, PredicateObligations};
 
 impl<'tcx> InferCtxt<'tcx> {
     /// Instead of normalizing an associated type projection,
@@ -17,7 +17,7 @@ impl<'tcx> InferCtxt<'tcx> {
         projection_ty: ty::AliasTy<'tcx>,
         cause: ObligationCause<'tcx>,
         recursion_depth: usize,
-        obligations: &mut Vec<PredicateObligation<'tcx>>,
+        obligations: &mut PredicateObligations<'tcx>,
     ) -> Ty<'tcx> {
         debug_assert!(!self.next_trait_solver());
         let ty_var = self.next_ty_var(self.tcx.def_span(projection_ty.def_id));
diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs
index 7913f0e340e..3cfc58dea05 100644
--- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs
+++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs
@@ -55,8 +55,8 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
     /// * what placeholder they must outlive transitively
     ///   * if they must also be equal to a placeholder, report an error because `P1: P2`
     /// * minimum universe U of all SCCs they must outlive
-    ///   * if they must also be equal to a placeholder P, and U cannot name P, report an error, as that
-    ///     indicates `P: R` and `R` is in an incompatible universe
+    ///   * if they must also be equal to a placeholder P, and U cannot name P, report an error, as
+    ///     that indicates `P: R` and `R` is in an incompatible universe
     ///
     /// To improve performance and for the old trait solver caching to be sound, this takes
     /// an optional snapshot in which case we only look at region constraints added in that
@@ -73,7 +73,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
     /// * R: P1, R: P2, as above
     #[instrument(level = "debug", skip(self, tcx, only_consider_snapshot), ret)]
     pub fn leak_check(
-        &mut self,
+        self,
         tcx: TyCtxt<'tcx>,
         outer_universe: ty::UniverseIndex,
         max_universe: ty::UniverseIndex,
@@ -83,7 +83,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
             return Ok(());
         }
 
-        let mini_graph = &MiniGraph::new(tcx, self, only_consider_snapshot);
+        let mini_graph = MiniGraph::new(tcx, &self, only_consider_snapshot);
 
         let mut leak_check = LeakCheck::new(tcx, outer_universe, max_universe, mini_graph, self);
         leak_check.assign_placeholder_values()?;
@@ -92,11 +92,11 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
     }
 }
 
-struct LeakCheck<'a, 'b, 'tcx> {
+struct LeakCheck<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
     outer_universe: ty::UniverseIndex,
-    mini_graph: &'a MiniGraph<'tcx>,
-    rcc: &'a mut RegionConstraintCollector<'b, 'tcx>,
+    mini_graph: MiniGraph<'tcx>,
+    rcc: RegionConstraintCollector<'a, 'tcx>,
 
     // Initially, for each SCC S, stores a placeholder `P` such that `S = P`
     // must hold.
@@ -115,26 +115,27 @@ struct LeakCheck<'a, 'b, 'tcx> {
     // either the placeholder `P1` or the empty region in that same universe.
     //
     // To detect errors, we look for an SCC S where the values in
-    // `scc_values[S]` (if any) cannot be stored into `scc_universes[S]`.
+    // `scc_placeholders[S]` (if any) cannot be stored into `scc_universes[S]`.
     scc_universes: IndexVec<LeakCheckScc, SccUniverse<'tcx>>,
 }
 
-impl<'a, 'b, 'tcx> LeakCheck<'a, 'b, 'tcx> {
+impl<'a, 'tcx> LeakCheck<'a, 'tcx> {
     fn new(
         tcx: TyCtxt<'tcx>,
         outer_universe: ty::UniverseIndex,
         max_universe: ty::UniverseIndex,
-        mini_graph: &'a MiniGraph<'tcx>,
-        rcc: &'a mut RegionConstraintCollector<'b, 'tcx>,
+        mini_graph: MiniGraph<'tcx>,
+        rcc: RegionConstraintCollector<'a, 'tcx>,
     ) -> Self {
         let dummy_scc_universe = SccUniverse { universe: max_universe, region: None };
+        let num_sccs = mini_graph.sccs.num_sccs();
         Self {
             tcx,
             outer_universe,
             mini_graph,
             rcc,
-            scc_placeholders: IndexVec::from_elem_n(None, mini_graph.sccs.num_sccs()),
-            scc_universes: IndexVec::from_elem_n(dummy_scc_universe, mini_graph.sccs.num_sccs()),
+            scc_placeholders: IndexVec::from_elem_n(None, num_sccs),
+            scc_universes: IndexVec::from_elem_n(dummy_scc_universe, num_sccs),
         }
     }
 
@@ -156,7 +157,16 @@ impl<'a, 'b, 'tcx> LeakCheck<'a, 'b, 'tcx> {
             // Detect those SCCs that directly contain a placeholder
             if let ty::RePlaceholder(placeholder) = **region {
                 if self.outer_universe.cannot_name(placeholder.universe) {
-                    self.assign_scc_value(scc, placeholder)?;
+                    // Update `scc_placeholders` to account for the fact that `P: S` must hold.
+                    match self.scc_placeholders[scc] {
+                        Some(p) => {
+                            assert_ne!(p, placeholder);
+                            return Err(self.placeholder_error(p, placeholder));
+                        }
+                        None => {
+                            self.scc_placeholders[scc] = Some(placeholder);
+                        }
+                    }
                 }
             }
         }
@@ -164,26 +174,6 @@ impl<'a, 'b, 'tcx> LeakCheck<'a, 'b, 'tcx> {
         Ok(())
     }
 
-    // assign_scc_value(S, P): Update `scc_values` to account for the fact that `P: S` must hold.
-    // This may create an error.
-    fn assign_scc_value(
-        &mut self,
-        scc: LeakCheckScc,
-        placeholder: ty::PlaceholderRegion,
-    ) -> RelateResult<'tcx, ()> {
-        match self.scc_placeholders[scc] {
-            Some(p) => {
-                assert_ne!(p, placeholder);
-                return Err(self.placeholder_error(p, placeholder));
-            }
-            None => {
-                self.scc_placeholders[scc] = Some(placeholder);
-            }
-        };
-
-        Ok(())
-    }
-
     /// For each SCC S, iterate over each successor S1 where `S: S1`:
     ///
     /// * Compute
@@ -216,8 +206,8 @@ impl<'a, 'b, 'tcx> LeakCheck<'a, 'b, 'tcx> {
             // Walk over each `scc2` such that `scc1: scc2` and compute:
             //
             // * `scc1_universe`: the minimum universe of `scc2` and the constituents of `scc1`
-            // * `succ_bound`: placeholder `P` that the successors must outlive, if any (if there are multiple,
-            //   we pick one arbitrarily)
+            // * `succ_bound`: placeholder `P` that the successors must outlive, if any (if there
+            //   are multiple, we pick one arbitrarily)
             let mut scc1_universe = self.scc_universes[scc1];
             let mut succ_bound = None;
             for &scc2 in self.mini_graph.sccs.successors(scc1) {
@@ -260,7 +250,8 @@ impl<'a, 'b, 'tcx> LeakCheck<'a, 'b, 'tcx> {
                 self.scc_placeholders[scc1] = succ_bound;
             }
 
-            // At this point, `scc_placeholder[scc1]` stores some placeholder that `scc1` must outlive (if any).
+            // At this point, `scc_placeholder[scc1]` stores some placeholder that `scc1` must
+            // outlive (if any).
         }
         Ok(())
     }
diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs
index 4411f8db72a..270217e26b7 100644
--- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs
+++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs
@@ -27,9 +27,9 @@ pub use rustc_middle::infer::MemberConstraint;
 #[derive(Clone, Default)]
 pub struct RegionConstraintStorage<'tcx> {
     /// For each `RegionVid`, the corresponding `RegionVariableOrigin`.
-    var_infos: IndexVec<RegionVid, RegionVariableInfo>,
+    pub(super) var_infos: IndexVec<RegionVid, RegionVariableInfo>,
 
-    data: RegionConstraintData<'tcx>,
+    pub(super) data: RegionConstraintData<'tcx>,
 
     /// For a given pair of regions (R1, R2), maps to a region R3 that
     /// is designated as their LUB (edges R1 <= R3 and R2 <= R3
@@ -61,21 +61,6 @@ pub struct RegionConstraintCollector<'a, 'tcx> {
     undo_log: &'a mut InferCtxtUndoLogs<'tcx>,
 }
 
-impl<'tcx> std::ops::Deref for RegionConstraintCollector<'_, 'tcx> {
-    type Target = RegionConstraintStorage<'tcx>;
-    #[inline]
-    fn deref(&self) -> &RegionConstraintStorage<'tcx> {
-        self.storage
-    }
-}
-
-impl<'tcx> std::ops::DerefMut for RegionConstraintCollector<'_, 'tcx> {
-    #[inline]
-    fn deref_mut(&mut self) -> &mut RegionConstraintStorage<'tcx> {
-        self.storage
-    }
-}
-
 pub type VarInfos = IndexVec<RegionVid, RegionVariableInfo>;
 
 /// The full set of region constraints gathered up by the collector.
@@ -309,10 +294,6 @@ pub(crate) struct RegionSnapshot {
 }
 
 impl<'tcx> RegionConstraintStorage<'tcx> {
-    pub fn new() -> Self {
-        Self::default()
-    }
-
     #[inline]
     pub(crate) fn with_log<'a>(
         &'a mut self,
@@ -320,46 +301,15 @@ impl<'tcx> RegionConstraintStorage<'tcx> {
     ) -> RegionConstraintCollector<'a, 'tcx> {
         RegionConstraintCollector { storage: self, undo_log }
     }
-
-    fn rollback_undo_entry(&mut self, undo_entry: UndoLog<'tcx>) {
-        match undo_entry {
-            AddVar(vid) => {
-                self.var_infos.pop().unwrap();
-                assert_eq!(self.var_infos.len(), vid.index());
-            }
-            AddConstraint(index) => {
-                self.data.constraints.pop().unwrap();
-                assert_eq!(self.data.constraints.len(), index);
-            }
-            AddVerify(index) => {
-                self.data.verifys.pop();
-                assert_eq!(self.data.verifys.len(), index);
-            }
-            AddCombination(Glb, ref regions) => {
-                self.glbs.remove(regions);
-            }
-            AddCombination(Lub, ref regions) => {
-                self.lubs.remove(regions);
-            }
-        }
-    }
 }
 
 impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
     pub fn num_region_vars(&self) -> usize {
-        self.var_infos.len()
+        self.storage.var_infos.len()
     }
 
     pub fn region_constraint_data(&self) -> &RegionConstraintData<'tcx> {
-        &self.data
-    }
-
-    /// Once all the constraints have been gathered, extract out the final data.
-    ///
-    /// Not legal during a snapshot.
-    pub fn into_infos_and_data(self) -> (VarInfos, RegionConstraintData<'tcx>) {
-        assert!(!UndoLogs::<UndoLog<'_>>::in_snapshot(&self.undo_log));
-        (mem::take(&mut self.storage.var_infos), mem::take(&mut self.storage.data))
+        &self.storage.data
     }
 
     /// Takes (and clears) the current set of constraints. Note that
@@ -415,17 +365,17 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
     }
 
     pub fn data(&self) -> &RegionConstraintData<'tcx> {
-        &self.data
+        &self.storage.data
     }
 
-    pub(super) fn start_snapshot(&mut self) -> RegionSnapshot {
+    pub(super) fn start_snapshot(&self) -> RegionSnapshot {
         debug!("RegionConstraintCollector: start_snapshot");
-        RegionSnapshot { any_unifications: self.any_unifications }
+        RegionSnapshot { any_unifications: self.storage.any_unifications }
     }
 
     pub(super) fn rollback_to(&mut self, snapshot: RegionSnapshot) {
         debug!("RegionConstraintCollector: rollback_to({:?})", snapshot);
-        self.any_unifications = snapshot.any_unifications;
+        self.storage.any_unifications = snapshot.any_unifications;
     }
 
     pub(super) fn new_region_var(
@@ -433,7 +383,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
         universe: ty::UniverseIndex,
         origin: RegionVariableOrigin,
     ) -> RegionVid {
-        let vid = self.var_infos.push(RegionVariableInfo { origin, universe });
+        let vid = self.storage.var_infos.push(RegionVariableInfo { origin, universe });
 
         let u_vid = self.unification_table_mut().new_key(RegionVariableValue::Unknown { universe });
         assert_eq!(vid, u_vid.vid);
@@ -444,7 +394,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
 
     /// Returns the origin for the given variable.
     pub(super) fn var_origin(&self, vid: RegionVid) -> RegionVariableOrigin {
-        self.var_infos[vid].origin
+        self.storage.var_infos[vid].origin
     }
 
     fn add_constraint(&mut self, constraint: Constraint<'tcx>, origin: SubregionOrigin<'tcx>) {
@@ -467,8 +417,8 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
             return;
         }
 
-        let index = self.data.verifys.len();
-        self.data.verifys.push(verify);
+        let index = self.storage.data.verifys.len();
+        self.storage.data.verifys.push(verify);
         self.undo_log.push(AddVerify(index));
     }
 
@@ -488,7 +438,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
                 (ty::ReVar(a), ty::ReVar(b)) => {
                     debug!("make_eqregion: unifying {:?} with {:?}", a, b);
                     if self.unification_table_mut().unify_var_var(a, b).is_ok() {
-                        self.any_unifications = true;
+                        self.storage.any_unifications = true;
                     }
                 }
                 (ty::ReVar(vid), _) => {
@@ -498,7 +448,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
                         .unify_var_value(vid, RegionVariableValue::Known { value: b })
                         .is_ok()
                     {
-                        self.any_unifications = true;
+                        self.storage.any_unifications = true;
                     };
                 }
                 (_, ty::ReVar(vid)) => {
@@ -508,7 +458,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
                         .unify_var_value(vid, RegionVariableValue::Known { value: a })
                         .is_ok()
                     {
-                        self.any_unifications = true;
+                        self.storage.any_unifications = true;
                     };
                 }
                 (_, _) => {}
@@ -530,7 +480,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
             return;
         }
 
-        self.data.member_constraints.push(MemberConstraint {
+        self.storage.data.member_constraints.push(MemberConstraint {
             key,
             definition_span,
             hidden_ty,
@@ -646,8 +596,8 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
 
     fn combine_map(&mut self, t: CombineMapType) -> &mut CombineMap<'tcx> {
         match t {
-            Glb => &mut self.glbs,
-            Lub => &mut self.lubs,
+            Glb => &mut self.storage.glbs,
+            Lub => &mut self.storage.lubs,
         }
     }
 
@@ -700,11 +650,12 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
         &self,
         value_count: usize,
     ) -> (Range<RegionVid>, Vec<RegionVariableOrigin>) {
-        let range = RegionVid::from(value_count)..RegionVid::from(self.unification_table.len());
+        let range =
+            RegionVid::from(value_count)..RegionVid::from(self.storage.unification_table.len());
         (
             range.clone(),
             (range.start.index()..range.end.index())
-                .map(|index| self.var_infos[ty::RegionVid::from(index)].origin)
+                .map(|index| self.storage.var_infos[ty::RegionVid::from(index)].origin)
                 .collect(),
         )
     }
@@ -801,6 +752,25 @@ impl<'tcx> RegionConstraintData<'tcx> {
 
 impl<'tcx> Rollback<UndoLog<'tcx>> for RegionConstraintStorage<'tcx> {
     fn reverse(&mut self, undo: UndoLog<'tcx>) {
-        self.rollback_undo_entry(undo)
+        match undo {
+            AddVar(vid) => {
+                self.var_infos.pop().unwrap();
+                assert_eq!(self.var_infos.len(), vid.index());
+            }
+            AddConstraint(index) => {
+                self.data.constraints.pop().unwrap();
+                assert_eq!(self.data.constraints.len(), index);
+            }
+            AddVerify(index) => {
+                self.data.verifys.pop();
+                assert_eq!(self.data.verifys.len(), index);
+            }
+            AddCombination(Glb, ref regions) => {
+                self.glbs.remove(regions);
+            }
+            AddCombination(Lub, ref regions) => {
+                self.lubs.remove(regions);
+            }
+        }
     }
 }
diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs
deleted file mode 100644
index c4f5a85a8be..00000000000
--- a/compiler/rustc_infer/src/infer/relate/combine.rs
+++ /dev/null
@@ -1,240 +0,0 @@
-//! There are four type combiners: `TypeRelating`, `Lub`, and `Glb`,
-//! and `NllTypeRelating` in rustc_borrowck, which is only used for NLL.
-//!
-//! Each implements the trait [TypeRelation] and contains methods for
-//! combining two instances of various things and yielding a new instance.
-//! These combiner methods always yield a `Result<T>`. To relate two
-//! types, you can use `infcx.at(cause, param_env)` which then allows
-//! you to use the relevant methods of [At](crate::infer::at::At).
-//!
-//! Combiners mostly do their specific behavior and then hand off the
-//! bulk of the work to [InferCtxt::super_combine_tys] and
-//! [InferCtxt::super_combine_consts].
-//!
-//! Combining two types may have side-effects on the inference contexts
-//! which can be undone by using snapshots. You probably want to use
-//! either [InferCtxt::commit_if_ok] or [InferCtxt::probe].
-//!
-//! On success, the  LUB/GLB operations return the appropriate bound. The
-//! return value of `Equate` or `Sub` shouldn't really be used.
-
-use rustc_middle::bug;
-use rustc_middle::infer::unify_key::EffectVarValue;
-use rustc_middle::ty::error::{ExpectedFound, TypeError};
-use rustc_middle::ty::{self, InferConst, IntType, Ty, TypeVisitableExt, UintType};
-pub use rustc_next_trait_solver::relate::combine::*;
-use tracing::debug;
-
-use super::{RelateResult, StructurallyRelateAliases};
-use crate::infer::{InferCtxt, relate};
-
-impl<'tcx> InferCtxt<'tcx> {
-    pub fn super_combine_tys<R>(
-        &self,
-        relation: &mut R,
-        a: Ty<'tcx>,
-        b: Ty<'tcx>,
-    ) -> RelateResult<'tcx, Ty<'tcx>>
-    where
-        R: PredicateEmittingRelation<InferCtxt<'tcx>>,
-    {
-        debug!("super_combine_tys::<{}>({:?}, {:?})", std::any::type_name::<R>(), a, b);
-        debug_assert!(!a.has_escaping_bound_vars());
-        debug_assert!(!b.has_escaping_bound_vars());
-
-        match (a.kind(), b.kind()) {
-            // Relate integral variables to other types
-            (&ty::Infer(ty::IntVar(a_id)), &ty::Infer(ty::IntVar(b_id))) => {
-                self.inner.borrow_mut().int_unification_table().union(a_id, b_id);
-                Ok(a)
-            }
-            (&ty::Infer(ty::IntVar(v_id)), &ty::Int(v)) => {
-                self.unify_integral_variable(v_id, IntType(v));
-                Ok(b)
-            }
-            (&ty::Int(v), &ty::Infer(ty::IntVar(v_id))) => {
-                self.unify_integral_variable(v_id, IntType(v));
-                Ok(a)
-            }
-            (&ty::Infer(ty::IntVar(v_id)), &ty::Uint(v)) => {
-                self.unify_integral_variable(v_id, UintType(v));
-                Ok(b)
-            }
-            (&ty::Uint(v), &ty::Infer(ty::IntVar(v_id))) => {
-                self.unify_integral_variable(v_id, UintType(v));
-                Ok(a)
-            }
-
-            // Relate floating-point variables to other types
-            (&ty::Infer(ty::FloatVar(a_id)), &ty::Infer(ty::FloatVar(b_id))) => {
-                self.inner.borrow_mut().float_unification_table().union(a_id, b_id);
-                Ok(a)
-            }
-            (&ty::Infer(ty::FloatVar(v_id)), &ty::Float(v)) => {
-                self.unify_float_variable(v_id, ty::FloatVarValue::Known(v));
-                Ok(b)
-            }
-            (&ty::Float(v), &ty::Infer(ty::FloatVar(v_id))) => {
-                self.unify_float_variable(v_id, ty::FloatVarValue::Known(v));
-                Ok(a)
-            }
-
-            // We don't expect `TyVar` or `Fresh*` vars at this point with lazy norm.
-            (ty::Alias(..), ty::Infer(ty::TyVar(_))) | (ty::Infer(ty::TyVar(_)), ty::Alias(..))
-                if self.next_trait_solver() =>
-            {
-                bug!(
-                    "We do not expect to encounter `TyVar` this late in combine \
-                    -- they should have been handled earlier"
-                )
-            }
-            (_, ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)))
-            | (ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)), _)
-                if self.next_trait_solver() =>
-            {
-                bug!("We do not expect to encounter `Fresh` variables in the new solver")
-            }
-
-            (_, ty::Alias(..)) | (ty::Alias(..), _) if self.next_trait_solver() => {
-                match relation.structurally_relate_aliases() {
-                    StructurallyRelateAliases::Yes => {
-                        relate::structurally_relate_tys(relation, a, b)
-                    }
-                    StructurallyRelateAliases::No => {
-                        relation.register_alias_relate_predicate(a, b);
-                        Ok(a)
-                    }
-                }
-            }
-
-            // All other cases of inference are errors
-            (&ty::Infer(_), _) | (_, &ty::Infer(_)) => {
-                Err(TypeError::Sorts(ExpectedFound::new(true, a, b)))
-            }
-
-            // During coherence, opaque types should be treated as *possibly*
-            // equal to any other type (except for possibly itself). This is an
-            // extremely heavy hammer, but can be relaxed in a forwards-compatible
-            // way later.
-            (&ty::Alias(ty::Opaque, _), _) | (_, &ty::Alias(ty::Opaque, _)) if self.intercrate => {
-                relation.register_predicates([ty::Binder::dummy(ty::PredicateKind::Ambiguous)]);
-                Ok(a)
-            }
-
-            _ => relate::structurally_relate_tys(relation, a, b),
-        }
-    }
-
-    pub fn super_combine_consts<R>(
-        &self,
-        relation: &mut R,
-        a: ty::Const<'tcx>,
-        b: ty::Const<'tcx>,
-    ) -> RelateResult<'tcx, ty::Const<'tcx>>
-    where
-        R: PredicateEmittingRelation<InferCtxt<'tcx>>,
-    {
-        debug!("super_combine_consts::<{}>({:?}, {:?})", std::any::type_name::<R>(), a, b);
-        debug_assert!(!a.has_escaping_bound_vars());
-        debug_assert!(!b.has_escaping_bound_vars());
-
-        if a == b {
-            return Ok(a);
-        }
-
-        let a = self.shallow_resolve_const(a);
-        let b = self.shallow_resolve_const(b);
-
-        match (a.kind(), b.kind()) {
-            (
-                ty::ConstKind::Infer(InferConst::Var(a_vid)),
-                ty::ConstKind::Infer(InferConst::Var(b_vid)),
-            ) => {
-                self.inner.borrow_mut().const_unification_table().union(a_vid, b_vid);
-                Ok(a)
-            }
-
-            (
-                ty::ConstKind::Infer(InferConst::EffectVar(a_vid)),
-                ty::ConstKind::Infer(InferConst::EffectVar(b_vid)),
-            ) => {
-                self.inner.borrow_mut().effect_unification_table().union(a_vid, b_vid);
-                Ok(a)
-            }
-
-            // All other cases of inference with other variables are errors.
-            (
-                ty::ConstKind::Infer(InferConst::Var(_) | InferConst::EffectVar(_)),
-                ty::ConstKind::Infer(_),
-            )
-            | (
-                ty::ConstKind::Infer(_),
-                ty::ConstKind::Infer(InferConst::Var(_) | InferConst::EffectVar(_)),
-            ) => {
-                bug!(
-                    "tried to combine ConstKind::Infer/ConstKind::Infer(InferConst::Var): {a:?} and {b:?}"
-                )
-            }
-
-            (ty::ConstKind::Infer(InferConst::Var(vid)), _) => {
-                self.instantiate_const_var(relation, true, vid, b)?;
-                Ok(b)
-            }
-
-            (_, ty::ConstKind::Infer(InferConst::Var(vid))) => {
-                self.instantiate_const_var(relation, false, vid, a)?;
-                Ok(a)
-            }
-
-            (ty::ConstKind::Infer(InferConst::EffectVar(vid)), _) => {
-                Ok(self.unify_effect_variable(vid, b))
-            }
-
-            (_, ty::ConstKind::Infer(InferConst::EffectVar(vid))) => {
-                Ok(self.unify_effect_variable(vid, a))
-            }
-
-            (ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..))
-                if self.tcx.features().generic_const_exprs || self.next_trait_solver() =>
-            {
-                match relation.structurally_relate_aliases() {
-                    StructurallyRelateAliases::No => {
-                        relation.register_predicates([if self.next_trait_solver() {
-                            ty::PredicateKind::AliasRelate(
-                                a.into(),
-                                b.into(),
-                                ty::AliasRelationDirection::Equate,
-                            )
-                        } else {
-                            ty::PredicateKind::ConstEquate(a, b)
-                        }]);
-
-                        Ok(b)
-                    }
-                    StructurallyRelateAliases::Yes => {
-                        relate::structurally_relate_consts(relation, a, b)
-                    }
-                }
-            }
-            _ => relate::structurally_relate_consts(relation, a, b),
-        }
-    }
-
-    #[inline(always)]
-    fn unify_integral_variable(&self, vid: ty::IntVid, val: ty::IntVarValue) {
-        self.inner.borrow_mut().int_unification_table().union_value(vid, val);
-    }
-
-    #[inline(always)]
-    fn unify_float_variable(&self, vid: ty::FloatVid, val: ty::FloatVarValue) {
-        self.inner.borrow_mut().float_unification_table().union_value(vid, val);
-    }
-
-    fn unify_effect_variable(&self, vid: ty::EffectVid, val: ty::Const<'tcx>) -> ty::Const<'tcx> {
-        self.inner
-            .borrow_mut()
-            .effect_unification_table()
-            .union_value(vid, EffectVarValue::Known(val));
-        val
-    }
-}
diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs
index a6d10aa5968..7049444db9b 100644
--- a/compiler/rustc_infer/src/infer/relate/generalize.rs
+++ b/compiler/rustc_infer/src/infer/relate/generalize.rs
@@ -50,7 +50,8 @@ impl<'tcx> InferCtxt<'tcx> {
         // Then the `generalized_ty` would be `&'?2 ?3`, where `'?2` and `?3` are fresh
         // region/type inference variables.
         //
-        // We then relate `generalized_ty <: source_ty`,adding constraints like `'x: '?2` and `?1 <: ?3`.
+        // We then relate `generalized_ty <: source_ty`, adding constraints like `'x: '?2` and
+        // `?1 <: ?3`.
         let Generalization { value_may_be_infer: generalized_ty, has_unconstrained_ty_var } = self
             .generalize(
                 relation.span(),
@@ -104,7 +105,8 @@ impl<'tcx> InferCtxt<'tcx> {
                     &ty::Alias(ty::Projection, data) => {
                         // FIXME: This does not handle subtyping correctly, we could
                         // instead create a new inference variable `?normalized_source`, emitting
-                        // `Projection(normalized_source, ?ty_normalized)` and `?normalized_source <: generalized_ty`.
+                        // `Projection(normalized_source, ?ty_normalized)` and
+                        // `?normalized_source <: generalized_ty`.
                         relation.register_predicates([ty::ProjectionPredicate {
                             projection_term: data.into(),
                             term: generalized_ty.into(),
@@ -181,7 +183,7 @@ impl<'tcx> InferCtxt<'tcx> {
     ///
     /// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant.
     #[instrument(level = "debug", skip(self, relation))]
-    pub(super) fn instantiate_const_var<R: PredicateEmittingRelation<InferCtxt<'tcx>>>(
+    pub(crate) fn instantiate_const_var<R: PredicateEmittingRelation<InferCtxt<'tcx>>>(
         &self,
         relation: &mut R,
         target_is_expected: bool,
diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs
index 8cec312abf7..4eb77a99be7 100644
--- a/compiler/rustc_infer/src/infer/relate/lattice.rs
+++ b/compiler/rustc_infer/src/infer/relate/lattice.rs
@@ -18,6 +18,7 @@
 //! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order)
 
 use rustc_middle::traits::solve::Goal;
+use rustc_middle::ty::relate::combine::{super_combine_consts, super_combine_tys};
 use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::{self, Ty, TyCtxt, TyVar, TypeVisitableExt};
 use rustc_span::Span;
@@ -26,7 +27,7 @@ use tracing::{debug, instrument};
 use super::StructurallyRelateAliases;
 use super::combine::PredicateEmittingRelation;
 use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin, TypeTrace};
-use crate::traits::{Obligation, PredicateObligation};
+use crate::traits::{Obligation, PredicateObligations};
 
 #[derive(Clone, Copy)]
 pub(crate) enum LatticeOpKind {
@@ -51,7 +52,7 @@ pub(crate) struct LatticeOp<'infcx, 'tcx> {
     param_env: ty::ParamEnv<'tcx>,
     // Mutable fields
     kind: LatticeOpKind,
-    obligations: Vec<PredicateObligation<'tcx>>,
+    obligations: PredicateObligations<'tcx>,
 }
 
 impl<'infcx, 'tcx> LatticeOp<'infcx, 'tcx> {
@@ -61,10 +62,10 @@ impl<'infcx, 'tcx> LatticeOp<'infcx, 'tcx> {
         param_env: ty::ParamEnv<'tcx>,
         kind: LatticeOpKind,
     ) -> LatticeOp<'infcx, 'tcx> {
-        LatticeOp { infcx, trace, param_env, kind, obligations: vec![] }
+        LatticeOp { infcx, trace, param_env, kind, obligations: PredicateObligations::new() }
     }
 
-    pub(crate) fn into_obligations(self) -> Vec<PredicateObligation<'tcx>> {
+    pub(crate) fn into_obligations(self) -> PredicateObligations<'tcx> {
         self.obligations
     }
 }
@@ -148,7 +149,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for LatticeOp<'_, 'tcx> {
             (
                 &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
                 &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
-            ) if a_def_id == b_def_id => infcx.super_combine_tys(self, a, b),
+            ) if a_def_id == b_def_id => super_combine_tys(infcx, self, a, b),
 
             (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
             | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
@@ -163,7 +164,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for LatticeOp<'_, 'tcx> {
                 Ok(a)
             }
 
-            _ => infcx.super_combine_tys(self, a, b),
+            _ => super_combine_tys(infcx, self, a, b),
         }
     }
 
@@ -191,7 +192,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for LatticeOp<'_, 'tcx> {
         a: ty::Const<'tcx>,
         b: ty::Const<'tcx>,
     ) -> RelateResult<'tcx, ty::Const<'tcx>> {
-        self.infcx.super_combine_consts(self, a, b)
+        super_combine_consts(self.infcx, self, a, b)
     }
 
     fn binders<T>(
diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs
index baab5bb274a..e6d1003cab6 100644
--- a/compiler/rustc_infer/src/infer/relate/mod.rs
+++ b/compiler/rustc_infer/src/infer/relate/mod.rs
@@ -3,12 +3,9 @@
 //! As well as the implementation of `Relate` for interned things (`Ty`/`Const`/etc).
 
 pub use rustc_middle::ty::relate::RelateResult;
-pub use rustc_next_trait_solver::relate::*;
+pub use rustc_type_ir::relate::combine::PredicateEmittingRelation;
+pub use rustc_type_ir::relate::*;
 
-pub use self::combine::PredicateEmittingRelation;
-
-#[allow(hidden_glob_reexports)]
-pub(super) mod combine;
 mod generalize;
 mod higher_ranked;
 pub(super) mod lattice;
diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs
index 4b4b13e919c..009271a8378 100644
--- a/compiler/rustc_infer/src/infer/relate/type_relating.rs
+++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs
@@ -1,4 +1,5 @@
 use rustc_middle::traits::solve::Goal;
+use rustc_middle::ty::relate::combine::{super_combine_consts, super_combine_tys};
 use rustc_middle::ty::relate::{
     Relate, RelateResult, TypeRelation, relate_args_invariantly, relate_args_with_variances,
 };
@@ -10,7 +11,7 @@ use tracing::{debug, instrument};
 use crate::infer::BoundRegionConversionTime::HigherRankedType;
 use crate::infer::relate::{PredicateEmittingRelation, StructurallyRelateAliases};
 use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin, TypeTrace};
-use crate::traits::{Obligation, PredicateObligation};
+use crate::traits::{Obligation, PredicateObligations};
 
 /// Enforce that `a` is equal to or a subtype of `b`.
 pub(crate) struct TypeRelating<'infcx, 'tcx> {
@@ -20,11 +21,10 @@ pub(crate) struct TypeRelating<'infcx, 'tcx> {
     trace: TypeTrace<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     define_opaque_types: DefineOpaqueTypes,
-    structurally_relate_aliases: StructurallyRelateAliases,
 
     // Mutable fields.
     ambient_variance: ty::Variance,
-    obligations: Vec<PredicateObligation<'tcx>>,
+    obligations: PredicateObligations<'tcx>,
     /// The cache only tracks the `ambient_variance` as it's the
     /// only field which is mutable and which meaningfully changes
     /// the result when relating types.
@@ -56,22 +56,21 @@ impl<'infcx, 'tcx> TypeRelating<'infcx, 'tcx> {
         trace: TypeTrace<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
         define_opaque_types: DefineOpaqueTypes,
-        structurally_relate_aliases: StructurallyRelateAliases,
         ambient_variance: ty::Variance,
     ) -> TypeRelating<'infcx, 'tcx> {
+        assert!(!infcx.next_trait_solver);
         TypeRelating {
             infcx,
             trace,
             param_env,
             define_opaque_types,
-            structurally_relate_aliases,
             ambient_variance,
-            obligations: vec![],
+            obligations: PredicateObligations::new(),
             cache: Default::default(),
         }
     }
 
-    pub(crate) fn into_obligations(self) -> Vec<PredicateObligation<'tcx>> {
+    pub(crate) fn into_obligations(self) -> PredicateObligations<'tcx> {
         self.obligations
     }
 }
@@ -183,23 +182,16 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, 'tcx> {
                 )?;
             }
 
-            (&ty::Error(e), _) | (_, &ty::Error(e)) => {
-                infcx.set_tainted_by_errors(e);
-                return Ok(Ty::new_error(self.cx(), e));
-            }
-
             (
                 &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
                 &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
             ) if a_def_id == b_def_id => {
-                infcx.super_combine_tys(self, a, b)?;
+                super_combine_tys(infcx, self, a, b)?;
             }
 
             (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
             | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
-                if self.define_opaque_types == DefineOpaqueTypes::Yes
-                    && def_id.is_local()
-                    && !infcx.next_trait_solver() =>
+                if self.define_opaque_types == DefineOpaqueTypes::Yes && def_id.is_local() =>
             {
                 self.register_goals(infcx.handle_opaque_type(
                     a,
@@ -210,7 +202,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, 'tcx> {
             }
 
             _ => {
-                infcx.super_combine_tys(self, a, b)?;
+                super_combine_tys(infcx, self, a, b)?;
             }
         }
 
@@ -265,7 +257,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, 'tcx> {
         a: ty::Const<'tcx>,
         b: ty::Const<'tcx>,
     ) -> RelateResult<'tcx, ty::Const<'tcx>> {
-        self.infcx.super_combine_consts(self, a, b)
+        super_combine_consts(self.infcx, self, a, b)
     }
 
     fn binders<T>(
@@ -357,7 +349,7 @@ impl<'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> for TypeRelating<'_, 'tcx>
     }
 
     fn structurally_relate_aliases(&self) -> StructurallyRelateAliases {
-        self.structurally_relate_aliases
+        StructurallyRelateAliases::No
     }
 
     fn register_predicates(
diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs
index 025c3a629fa..64cc76f827e 100644
--- a/compiler/rustc_infer/src/infer/resolve.rs
+++ b/compiler/rustc_infer/src/infer/resolve.rs
@@ -38,7 +38,7 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for OpportunisticVarResolver<'a, 'tcx> {
         if !t.has_non_region_infer() {
             t // micro-optimize -- if there is nothing in this type that this fold affects...
         } else if let Some(&ty) = self.cache.get(&t) {
-            return ty;
+            ty
         } else {
             let shallow = self.infcx.shallow_resolve(t);
             let res = shallow.super_fold_with(self);
diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs
index c50477b2922..779ce976bec 100644
--- a/compiler/rustc_infer/src/infer/type_variable.rs
+++ b/compiler/rustc_infer/src/infer/type_variable.rs
@@ -19,7 +19,7 @@ impl<'tcx> Rollback<sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>> for TypeVariabl
     }
 }
 
-#[derive(Clone)]
+#[derive(Clone, Default)]
 pub(crate) struct TypeVariableStorage<'tcx> {
     /// The origins of each type variable.
     values: IndexVec<TyVid, TypeVariableData>,
@@ -74,13 +74,6 @@ impl<'tcx> TypeVariableValue<'tcx> {
 }
 
 impl<'tcx> TypeVariableStorage<'tcx> {
-    pub(crate) fn new() -> TypeVariableStorage<'tcx> {
-        TypeVariableStorage {
-            values: Default::default(),
-            eq_relations: ut::UnificationTableStorage::new(),
-        }
-    }
-
     #[inline]
     pub(crate) fn with_log<'a>(
         &'a mut self,
diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs
index a076cdad672..ba1516655b0 100644
--- a/compiler/rustc_infer/src/traits/engine.rs
+++ b/compiler/rustc_infer/src/traits/engine.rs
@@ -3,7 +3,7 @@ use std::fmt::Debug;
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::{self, Ty, Upcast};
 
-use super::{ObligationCause, PredicateObligation};
+use super::{ObligationCause, PredicateObligation, PredicateObligations};
 use crate::infer::InferCtxt;
 use crate::traits::Obligation;
 
@@ -20,7 +20,7 @@ pub enum ScrubbedTraitError<'tcx> {
     /// An ambiguity. This goal may hold if further inference is done.
     Ambiguity,
     /// An old-solver-style cycle error, which will fatal.
-    Cycle(Vec<PredicateObligation<'tcx>>),
+    Cycle(PredicateObligations<'tcx>),
 }
 
 impl<'tcx> ScrubbedTraitError<'tcx> {
@@ -62,7 +62,7 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx {
     fn register_predicate_obligations(
         &mut self,
         infcx: &InferCtxt<'tcx>,
-        obligations: Vec<PredicateObligation<'tcx>>,
+        obligations: PredicateObligations<'tcx>,
     ) {
         for obligation in obligations {
             self.register_predicate_obligation(infcx, obligation);
@@ -84,7 +84,7 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx {
         self.collect_remaining_errors(infcx)
     }
 
-    fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>>;
+    fn pending_obligations(&self) -> PredicateObligations<'tcx>;
 
     /// Among all pending obligations, collect those are stalled on a inference variable which has
     /// changed since the last call to `select_where_possible`. Those obligations are marked as
@@ -92,7 +92,7 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx {
     fn drain_unstalled_obligations(
         &mut self,
         infcx: &InferCtxt<'tcx>,
-    ) -> Vec<PredicateObligation<'tcx>>;
+    ) -> PredicateObligations<'tcx>;
 }
 
 pub trait FromSolverError<'tcx, E>: Debug + 'tcx {
diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs
index 34b0fe3e967..ac641ef5652 100644
--- a/compiler/rustc_infer/src/traits/mod.rs
+++ b/compiler/rustc_infer/src/traits/mod.rs
@@ -17,6 +17,7 @@ use rustc_middle::traits::solve::Certainty;
 pub use rustc_middle::traits::*;
 use rustc_middle::ty::{self, Ty, TyCtxt, Upcast};
 use rustc_span::Span;
+use thin_vec::ThinVec;
 
 pub use self::ImplSource::*;
 pub use self::SelectionError::*;
@@ -84,6 +85,8 @@ pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>;
 pub type TraitObligation<'tcx> = Obligation<'tcx, ty::TraitPredicate<'tcx>>;
 pub type PolyTraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>;
 
+pub type PredicateObligations<'tcx> = ThinVec<PredicateObligation<'tcx>>;
+
 impl<'tcx> PredicateObligation<'tcx> {
     /// Flips the polarity of the inner predicate.
     ///
diff --git a/compiler/rustc_infer/src/traits/project.rs b/compiler/rustc_infer/src/traits/project.rs
index fa813d0f90c..cab2ce9f5a0 100644
--- a/compiler/rustc_infer/src/traits/project.rs
+++ b/compiler/rustc_infer/src/traits/project.rs
@@ -6,7 +6,7 @@ use rustc_middle::traits::EvaluationResult;
 use rustc_middle::ty;
 use tracing::{debug, info};
 
-use super::PredicateObligation;
+use super::PredicateObligations;
 use crate::infer::snapshot::undo_log::InferCtxtUndoLogs;
 
 pub(crate) type UndoLog<'tcx> =
@@ -20,7 +20,7 @@ pub struct MismatchedProjectionTypes<'tcx> {
 #[derive(Clone)]
 pub struct Normalized<'tcx, T> {
     pub value: T,
-    pub obligations: Vec<PredicateObligation<'tcx>>,
+    pub obligations: PredicateObligations<'tcx>,
 }
 
 pub type NormalizedTerm<'tcx> = Normalized<'tcx, ty::Term<'tcx>>;
@@ -92,38 +92,31 @@ pub enum ProjectionCacheEntry<'tcx> {
     Error,
     NormalizedTerm {
         ty: NormalizedTerm<'tcx>,
-        /// If we were able to successfully evaluate the
-        /// corresponding cache entry key during predicate
-        /// evaluation, then this field stores the final
-        /// result obtained from evaluating all of the projection
-        /// sub-obligations. During evaluation, we will skip
-        /// evaluating the cached sub-obligations in `ty`
-        /// if this field is set. Evaluation only
-        /// cares about the final result, so we don't
-        /// care about any region constraint side-effects
-        /// produced by evaluating the sub-obligations.
+        /// If we were able to successfully evaluate the corresponding cache
+        /// entry key during predicate evaluation, then this field stores the
+        /// final result obtained from evaluating all of the projection
+        /// sub-obligations. During evaluation, we will skip evaluating the
+        /// cached sub-obligations in `ty` if this field is set. Evaluation
+        /// only cares about the final result, so we don't care about any
+        /// region constraint side-effects produced by evaluating the
+        /// sub-obligations.
         ///
-        /// Additionally, we will clear out the sub-obligations
-        /// entirely if we ever evaluate the cache entry (along
-        /// with all its sub obligations) to `EvaluatedToOk`.
-        /// This affects all users of the cache, not just evaluation.
-        /// Since a result of `EvaluatedToOk` means that there were
-        /// no region obligations that need to be tracked, it's
-        /// fine to forget about the sub-obligations - they
-        /// don't provide any additional information. However,
-        /// we do *not* discard any obligations when we see
-        /// `EvaluatedToOkModuloRegions` - we don't know
-        /// which sub-obligations may introduce region constraints,
-        /// so we keep them all to be safe.
+        /// Additionally, we will clear out the sub-obligations entirely if we
+        /// ever evaluate the cache entry (along with all its sub obligations)
+        /// to `EvaluatedToOk`. This affects all users of the cache, not just
+        /// evaluation. Since a result of `EvaluatedToOk` means that there were
+        /// no region obligations that need to be tracked, it's fine to forget
+        /// about the sub-obligations - they don't provide any additional
+        /// information. However, we do *not* discard any obligations when we
+        /// see `EvaluatedToOkModuloRegions` - we don't know which
+        /// sub-obligations may introduce region constraints, so we keep them
+        /// all to be safe.
         ///
-        /// When we are not performing evaluation
-        /// (e.g. in `FulfillmentContext`), we ignore this field,
-        /// and always re-process the cached sub-obligations
-        /// (which may have been cleared out - see the above
-        /// paragraph).
-        /// This ensures that we do not lose any regions
-        /// constraints that arise from processing the
-        /// sub-obligations.
+        /// When we are not performing evaluation (e.g. in
+        /// `FulfillmentContext`), we ignore this field, and always re-process
+        /// the cached sub-obligations (which may have been cleared out - see
+        /// the above paragraph). This ensures that we do not lose any regions
+        /// constraints that arise from processing the sub-obligations.
         complete: Option<EvaluationResult>,
     },
 }
@@ -198,7 +191,7 @@ impl<'tcx> ProjectionCache<'_, 'tcx> {
                 info!("ProjectionCacheEntry::complete({:?}) - completing {:?}", key, ty);
                 let mut ty = ty.clone();
                 if result.must_apply_considering_regions() {
-                    ty.obligations = vec![];
+                    ty.obligations = PredicateObligations::new();
                 }
                 map.insert(key, ProjectionCacheEntry::NormalizedTerm {
                     ty,
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 536ce154cd0..3189620e969 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -810,7 +810,7 @@ fn test_unstable_options_tracking_hash() {
     tracked!(mir_opt_level, Some(4));
     tracked!(move_size_limit, Some(4096));
     tracked!(mutable_noalias, false);
-    tracked!(next_solver, Some(NextSolverConfig { coherence: true, globally: false }));
+    tracked!(next_solver, NextSolverConfig { coherence: true, globally: true });
     tracked!(no_generate_arange_section, true);
     tracked!(no_jump_tables, true);
     tracked!(no_link, true);
diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs
index 60aab668cba..b0ab50dd773 100644
--- a/compiler/rustc_lexer/src/lib.rs
+++ b/compiler/rustc_lexer/src/lib.rs
@@ -104,6 +104,12 @@ pub enum TokenKind {
     /// for emoji identifier recovery, as those are not meant to be ever accepted.
     InvalidPrefix,
 
+    /// Guarded string literal prefix: `#"` or `##`.
+    ///
+    /// Used for reserving "guarded strings" (RFC 3598) in edition 2024.
+    /// Split into the component tokens on older editions.
+    GuardedStrPrefix,
+
     /// Examples: `12u8`, `1.0e-40`, `b"123"`. Note that `_` is an invalid
     /// suffix, but may be present here on string and float literals. Users of
     /// this type will need to check for and reject that case.
@@ -191,30 +197,41 @@ pub enum DocStyle {
 /// `rustc_ast::ast::LitKind`).
 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
 pub enum LiteralKind {
-    /// "12_u8", "0o100", "0b120i99", "1f32".
+    /// `12_u8`, `0o100`, `0b120i99`, `1f32`.
     Int { base: Base, empty_int: bool },
-    /// "12.34f32", "1e3", but not "1f32".
+    /// `12.34f32`, `1e3`, but not `1f32`.
     Float { base: Base, empty_exponent: bool },
-    /// "'a'", "'\\'", "'''", "';"
+    /// `'a'`, `'\\'`, `'''`, `';`
     Char { terminated: bool },
-    /// "b'a'", "b'\\'", "b'''", "b';"
+    /// `b'a'`, `b'\\'`, `b'''`, `b';`
     Byte { terminated: bool },
-    /// ""abc"", ""abc"
+    /// `"abc"`, `"abc`
     Str { terminated: bool },
-    /// "b"abc"", "b"abc"
+    /// `b"abc"`, `b"abc`
     ByteStr { terminated: bool },
     /// `c"abc"`, `c"abc`
     CStr { terminated: bool },
-    /// "r"abc"", "r#"abc"#", "r####"ab"###"c"####", "r#"a". `None` indicates
+    /// `r"abc"`, `r#"abc"#`, `r####"ab"###"c"####`, `r#"a`. `None` indicates
     /// an invalid literal.
     RawStr { n_hashes: Option<u8> },
-    /// "br"abc"", "br#"abc"#", "br####"ab"###"c"####", "br#"a". `None`
+    /// `br"abc"`, `br#"abc"#`, `br####"ab"###"c"####`, `br#"a`. `None`
     /// indicates an invalid literal.
     RawByteStr { n_hashes: Option<u8> },
     /// `cr"abc"`, "cr#"abc"#", `cr#"a`. `None` indicates an invalid literal.
     RawCStr { n_hashes: Option<u8> },
 }
 
+/// `#"abc"#`, `##"a"` (fewer closing), or even `#"a` (unterminated).
+///
+/// Can capture fewer closing hashes than starting hashes,
+/// for more efficient lexing and better backwards diagnostics.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct GuardedStr {
+    pub n_hashes: u32,
+    pub terminated: bool,
+    pub token_len: u32,
+}
+
 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
 pub enum RawStrError {
     /// Non `#` characters exist between `r` and `"`, e.g. `r##~"abcde"##`
@@ -403,6 +420,12 @@ impl Cursor<'_> {
                 TokenKind::Literal { kind: literal_kind, suffix_start }
             }
 
+            // Guarded string literal prefix: `#"` or `##`
+            '#' if matches!(self.first(), '"' | '#') => {
+                self.bump();
+                TokenKind::GuardedStrPrefix
+            }
+
             // One-symbol tokens.
             ';' => Semi,
             ',' => Comma,
@@ -780,6 +803,60 @@ impl Cursor<'_> {
         false
     }
 
+    /// Attempt to lex for a guarded string literal.
+    ///
+    /// Used by `rustc_parse::lexer` to lex for guarded strings
+    /// conditionally based on edition.
+    ///
+    /// Note: this will not reset the `Cursor` when a
+    /// guarded string is not found. It is the caller's
+    /// responsibility to do so.
+    pub fn guarded_double_quoted_string(&mut self) -> Option<GuardedStr> {
+        debug_assert!(self.prev() != '#');
+
+        let mut n_start_hashes: u32 = 0;
+        while self.first() == '#' {
+            n_start_hashes += 1;
+            self.bump();
+        }
+
+        if self.first() != '"' {
+            return None;
+        }
+        self.bump();
+        debug_assert!(self.prev() == '"');
+
+        // Lex the string itself as a normal string literal
+        // so we can recover that for older editions later.
+        let terminated = self.double_quoted_string();
+        if !terminated {
+            let token_len = self.pos_within_token();
+            self.reset_pos_within_token();
+
+            return Some(GuardedStr { n_hashes: n_start_hashes, terminated: false, token_len });
+        }
+
+        // Consume closing '#' symbols.
+        // Note that this will not consume extra trailing `#` characters:
+        // `###"abcde"####` is lexed as a `GuardedStr { n_end_hashes: 3, .. }`
+        // followed by a `#` token.
+        let mut n_end_hashes = 0;
+        while self.first() == '#' && n_end_hashes < n_start_hashes {
+            n_end_hashes += 1;
+            self.bump();
+        }
+
+        // Reserved syntax, always an error, so it doesn't matter if
+        // `n_start_hashes != n_end_hashes`.
+
+        self.eat_literal_suffix();
+
+        let token_len = self.pos_within_token();
+        self.reset_pos_within_token();
+
+        Some(GuardedStr { n_hashes: n_start_hashes, terminated: true, token_len })
+    }
+
     /// Eats the double-quoted string and returns `n_hashes` and an error if encountered.
     fn raw_double_quoted_string(&mut self, prefix_len: u32) -> Result<u8, RawStrError> {
         // Wrap the actual function to handle the error with too many hashes.
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index d3799594871..c4d709aa1f9 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -581,8 +581,6 @@ lint_non_glob_import_type_ir_inherent = non-glob import of `rustc_type_ir::inher
 
 lint_non_local_definitions_cargo_update = the {$macro_kind} `{$macro_name}` may come from an old version of the `{$crate_name}` crate, try updating your dependency with `cargo update -p {$crate_name}`
 
-lint_non_local_definitions_deprecation = this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue <https://github.com/rust-lang/rust/issues/120363>
-
 lint_non_local_definitions_impl = non-local `impl` definition, `impl` blocks should be written at the same level as their item
     .non_local = an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl`
     .doctest = make this doc-test a standalone test with its own `fn main() {"{"} ... {"}"}`
@@ -740,6 +738,9 @@ lint_reserved_prefix = prefix `{$prefix}` is unknown
     .label = unknown prefix
     .suggestion = insert whitespace here to avoid this being parsed as a prefix in Rust 2021
 
+lint_reserved_string = will be parsed as a guarded string in Rust 2024
+    .suggestion = insert whitespace here to avoid this being parsed as a guarded string in Rust 2024
+
 lint_shadowed_into_iter =
     this method call resolves to `<&{$target} as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<{$target} as IntoIterator>::into_iter` in Rust {$edition}
     .use_iter_suggestion = use `.iter()` instead of `.into_iter()` to avoid ambiguity
diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs
index b5ab56912cb..565c3c04252 100644
--- a/compiler/rustc_lint/src/context/diagnostics.rs
+++ b/compiler/rustc_lint/src/context/diagnostics.rs
@@ -176,6 +176,9 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &
             lints::RawPrefix { label: label_span, suggestion: label_span.shrink_to_hi() }
                 .decorate_lint(diag);
         }
+        BuiltinLintDiag::ReservedString(suggestion) => {
+            lints::ReservedString { suggestion }.decorate_lint(diag);
+        }
         BuiltinLintDiag::UnusedBuiltinAttribute { attr_name, macro_name, invoc_span } => {
             lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name }.decorate_lint(diag);
         }
diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs
index cdd0e80c458..58fd11fcc29 100644
--- a/compiler/rustc_lint/src/if_let_rescope.rs
+++ b/compiler/rustc_lint/src/if_let_rescope.rs
@@ -24,7 +24,7 @@ declare_lint! {
     /// ### Example
     ///
     /// ```rust,edition2021
-    /// #![cfg_attr(not(bootstrap), feature(if_let_rescope))] // Simplify this in bootstrap bump.
+    /// #![feature(if_let_rescope)]
     /// #![warn(if_let_rescope)]
     /// #![allow(unused_variables)]
     ///
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index a861894a444..16cfae17d40 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -1430,8 +1430,6 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag {
                         );
                     }
                 }
-
-                diag.note(fluent::lint_non_local_definitions_deprecation);
             }
             NonLocalDefinitionsDiag::MacroRules {
                 depth,
@@ -1452,7 +1450,6 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag {
                 }
 
                 diag.note(fluent::lint_non_local);
-                diag.note(fluent::lint_non_local_definitions_deprecation);
 
                 if let Some(cargo_update) = cargo_update {
                     diag.subdiagnostic(cargo_update);
@@ -3053,3 +3050,10 @@ pub(crate) enum MutRefSugg {
 #[derive(LintDiagnostic)]
 #[diag(lint_unqualified_local_imports)]
 pub(crate) struct UnqualifiedLocalImportsDiag {}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_reserved_string)]
+pub(crate) struct ReservedString {
+    #[suggestion(code = " ", applicability = "machine-applicable")]
+    pub suggestion: Span,
+}
diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs
index 56f930ea7f6..3c31b879bd6 100644
--- a/compiler/rustc_lint/src/non_local_def.rs
+++ b/compiler/rustc_lint/src/non_local_def.rs
@@ -124,16 +124,6 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
                 // If that's the case this means that this impl block declaration
                 // is using local items and so we don't lint on it.
 
-                // We also ignore anon-const in item by including the anon-const
-                // parent as well.
-                let parent_parent = if parent_def_kind == DefKind::Const
-                    && parent_opt_item_name == Some(kw::Underscore)
-                {
-                    Some(cx.tcx.parent(parent))
-                } else {
-                    None
-                };
-
                 // 1. We collect all the `hir::Path` from the `Self` type and `Trait` ref
                 // of the `impl` definition
                 let mut collector = PathCollector { paths: Vec::new() };
@@ -148,13 +138,33 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
                     |p| matches!(p.res, Res::Def(def_kind, _) if def_kind != DefKind::TyParam),
                 );
 
-                // 2. We check if any of path reference a "local" parent and if that the case
-                // we bail out as asked by T-lang, even though this isn't correct from a
-                // type-system point of view, as inference exists and could still leak the impl.
+                // 1.9. We retrieve the parent def id of the impl item, ...
+                //
+                // ... modulo const-anons items, for enhanced compatibility with the ecosystem
+                // as that pattern is common with `serde`, `bevy`, ...
+                //
+                // For this example we want the `DefId` parent of the outermost const-anon items.
+                // ```
+                // const _: () = { // the parent of this const-anon
+                //     const _: () = {
+                //         impl Foo {}
+                //     };
+                // };
+                // ```
+                let outermost_impl_parent = peel_parent_while(cx.tcx, parent, |tcx, did| {
+                    tcx.def_kind(did) == DefKind::Const
+                        && tcx.opt_item_name(did) == Some(kw::Underscore)
+                });
+
+                // 2. We check if any of the paths reference a the `impl`-parent.
+                //
+                // If that the case we bail out, as was asked by T-lang, even though this isn't
+                // correct from a type-system point of view, as inference exists and one-impl-rule
+                // make its so that we could still leak the impl.
                 if collector
                     .paths
                     .iter()
-                    .any(|path| path_has_local_parent(path, cx, parent, parent_parent))
+                    .any(|path| path_has_local_parent(path, cx, parent, outermost_impl_parent))
                 {
                     return;
                 }
@@ -253,8 +263,8 @@ impl<'tcx> Visitor<'tcx> for PathCollector<'tcx> {
     }
 }
 
-/// Given a path and a parent impl def id, this checks if the if parent resolution
-/// def id correspond to the def id of the parent impl definition.
+/// Given a path, this checks if the if the parent resolution def id corresponds to
+/// the def id of the parent impl definition (the direct one and the outermost one).
 ///
 /// Given this path, we will look at the path (and ignore any generic args):
 ///
@@ -267,32 +277,54 @@ fn path_has_local_parent(
     path: &Path<'_>,
     cx: &LateContext<'_>,
     impl_parent: DefId,
-    impl_parent_parent: Option<DefId>,
+    outermost_impl_parent: Option<DefId>,
 ) -> bool {
     path.res
         .opt_def_id()
-        .is_some_and(|did| did_has_local_parent(did, cx.tcx, impl_parent, impl_parent_parent))
+        .is_some_and(|did| did_has_local_parent(did, cx.tcx, impl_parent, outermost_impl_parent))
 }
 
-/// Given a def id and a parent impl def id, this checks if the parent
-/// def id (modulo modules) correspond to the def id of the parent impl definition.
+/// Given a def id this checks if the parent def id (modulo modules) correspond to
+/// the def id of the parent impl definition (the direct one and the outermost one).
 #[inline]
 fn did_has_local_parent(
     did: DefId,
     tcx: TyCtxt<'_>,
     impl_parent: DefId,
-    impl_parent_parent: Option<DefId>,
+    outermost_impl_parent: Option<DefId>,
 ) -> bool {
-    did.is_local()
-        && if let Some(did_parent) = tcx.opt_parent(did) {
-            did_parent == impl_parent
-                || Some(did_parent) == impl_parent_parent
-                || !did_parent.is_crate_root()
-                    && tcx.def_kind(did_parent) == DefKind::Mod
-                    && did_has_local_parent(did_parent, tcx, impl_parent, impl_parent_parent)
-        } else {
-            false
-        }
+    if !did.is_local() {
+        return false;
+    }
+
+    let Some(parent_did) = tcx.opt_parent(did) else {
+        return false;
+    };
+
+    peel_parent_while(tcx, parent_did, |tcx, did| {
+        tcx.def_kind(did) == DefKind::Mod
+            || (tcx.def_kind(did) == DefKind::Const
+                && tcx.opt_item_name(did) == Some(kw::Underscore))
+    })
+    .map(|parent_did| parent_did == impl_parent || Some(parent_did) == outermost_impl_parent)
+    .unwrap_or(false)
+}
+
+/// Given a `DefId` checks if it satisfies `f` if it does check with it's parent and continue
+/// until it doesn't satisfies `f` and return the last `DefId` checked.
+///
+/// In other word this method return the first `DefId` that doesn't satisfies `f`.
+#[inline]
+fn peel_parent_while(
+    tcx: TyCtxt<'_>,
+    mut did: DefId,
+    mut f: impl FnMut(TyCtxt<'_>, DefId) -> bool,
+) -> Option<DefId> {
+    while !did.is_crate_root() && f(tcx, did) {
+        did = tcx.opt_parent(did).filter(|parent_did| parent_did.is_local())?;
+    }
+
+    Some(did)
 }
 
 /// Return for a given `Path` the span until the last args
diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs
index c0a01b0065e..5a3666dcbd4 100644
--- a/compiler/rustc_lint/src/traits.rs
+++ b/compiler/rustc_lint/src/traits.rs
@@ -112,10 +112,11 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
 
     fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) {
         let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { return };
-        for (bound, modifier) in &bounds[..] {
+        for bound in &bounds[..] {
             let def_id = bound.trait_ref.trait_def_id();
             if def_id.is_some_and(|def_id| cx.tcx.is_lang_item(def_id, LangItem::Drop))
-                && *modifier != hir::TraitBoundModifier::Maybe
+                // FIXME: ?Drop is not a thing.
+                && bound.modifiers != hir::TraitBoundModifier::Maybe
             {
                 let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { return };
                 cx.emit_span_lint(DYN_DROP, bound.span, DropGlue { tcx: cx.tcx, def_id });
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 12d5b5cf979..1a007250961 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -804,7 +804,15 @@ trait UnusedDelimLint {
                 .find_ancestor_inside(value.span)
                 .map(|span| (value.span.with_hi(span.lo()), value.span.with_lo(span.hi()))),
             ast::ExprKind::Paren(ref expr) => {
-                expr.span.find_ancestor_inside(value.span).map(|expr_span| {
+                // For the expr with attributes, like `let _ = (#[inline] || println!("Hello!"));`,
+                // the span should contains the attributes, or the suggestion will remove them.
+                let expr_span_with_attrs =
+                    if let Some(attr_lo) = expr.attrs.iter().map(|attr| attr.span.lo()).min() {
+                        expr.span.with_lo(attr_lo)
+                    } else {
+                        expr.span
+                    };
+                expr_span_with_attrs.find_ancestor_inside(value.span).map(|expr_span| {
                     (value.span.with_hi(expr_span.lo()), value.span.with_lo(expr_span.hi()))
                 })
             }
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index a123059df8f..45a5ce0ca20 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -92,6 +92,7 @@ declare_lint_pass! {
         RUST_2021_INCOMPATIBLE_OR_PATTERNS,
         RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
         RUST_2021_PRELUDE_COLLISIONS,
+        RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX,
         RUST_2024_INCOMPATIBLE_PAT,
         RUST_2024_PRELUDE_COLLISIONS,
         SELF_CONSTRUCTOR_FROM_OUTER_ITEM,
@@ -123,6 +124,7 @@ declare_lint_pass! {
         UNSTABLE_NAME_COLLISIONS,
         UNSTABLE_SYNTAX_PRE_EXPANSION,
         UNSUPPORTED_CALLING_CONVENTIONS,
+        UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS,
         UNUSED_ASSIGNMENTS,
         UNUSED_ASSOCIATED_TYPE_BOUNDS,
         UNUSED_ATTRIBUTES,
@@ -1649,7 +1651,6 @@ declare_lint! {
     /// ### Example
     ///
     /// ```rust,edition2021
-    /// #![feature(ref_pat_eat_one_layer_2024)]
     /// #![warn(rust_2024_incompatible_pat)]
     ///
     /// if let Some(&a) = &Some(&0u8) {
@@ -1670,12 +1671,10 @@ declare_lint! {
     pub RUST_2024_INCOMPATIBLE_PAT,
     Allow,
     "detects patterns whose meaning will change in Rust 2024",
-    @feature_gate = ref_pat_eat_one_layer_2024;
-    // FIXME uncomment below upon stabilization
-    /*@future_incompatible = FutureIncompatibleInfo {
+    @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
         reference: "123076",
-    };*/
+    };
 }
 
 declare_lint! {
@@ -1869,7 +1868,6 @@ declare_lint! {
     /// ### Example
     ///
     /// ```rust,compile_fail
-    /// # #[cfg_attr(bootstrap)] compile_error!(); // Remove this in bootstrap bump.
     /// #![deny(elided_named_lifetimes)]
     /// struct Foo;
     /// impl Foo {
@@ -3839,6 +3837,50 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `unsupported_fn_ptr_calling_conventions` lint is output whenever there is a use of
+    /// a target dependent calling convention on a target that does not support this calling
+    /// convention on a function pointer.
+    ///
+    /// For example `stdcall` does not make much sense for a x86_64 or, more apparently, powerpc
+    /// code, because this calling convention was never specified for those targets.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,ignore (needs specific targets)
+    /// fn stdcall_ptr(f: extern "stdcall" fn ()) {
+    ///     f()
+    /// }
+    /// ```
+    ///
+    /// This will produce:
+    ///
+    /// ```text
+    /// warning: the calling convention `"stdcall"` is not supported on this target
+    ///   --> $DIR/unsupported.rs:34:15
+    ///    |
+    /// LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
+    ///    |               ^^^^^^^^^^^^^^^^^^^^^^^^
+    ///    |
+    ///    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+    ///    = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+    ///    = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+    /// ```
+    ///
+    /// ### Explanation
+    ///
+    /// On most of the targets the behaviour of `stdcall` and similar calling conventions is not
+    /// defined at all, but was previously accepted due to a bug in the implementation of the
+    /// compiler.
+    pub UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS,
+    Warn,
+    "use of unsupported calling convention for function pointer",
+    @future_incompatible = FutureIncompatibleInfo {
+        reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
+        reference: "issue #130260 <https://github.com/rust-lang/rust/issues/130260>",
+    };
+}
+
+declare_lint! {
     /// The `break_with_label_and_loop` lint detects labeled `break` expressions with
     /// an unlabeled loop as their value expression.
     ///
@@ -4996,3 +5038,43 @@ declare_lint! {
     Warn,
     "detects pointer to integer transmutes in const functions and associated constants",
 }
+
+declare_lint! {
+    /// The `rust_2024_guarded_string_incompatible_syntax` lint detects `#` tokens
+    /// that will be parsed as part of a guarded string literal in Rust 2024.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,edition2021,compile_fail
+    /// #![deny(rust_2024_guarded_string_incompatible_syntax)]
+    ///
+    /// macro_rules! m {
+    ///     (# $x:expr #) => ();
+    ///     (# $x:expr) => ();
+    /// }
+    ///
+    /// m!(#"hey"#);
+    /// m!(#"hello");
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Prior to Rust 2024, `#"hey"#` is three tokens: the first `#`
+    /// followed by the string literal `"hey"` then the final `#`.
+    /// In Rust 2024, the whole sequence is considered a single token.
+    ///
+    /// This lint suggests to add whitespace between the leading `#`
+    /// and the string to keep them separated in Rust 2024.
+    // Allow this lint -- rustdoc doesn't yet support threading edition into this lint's parser.
+    #[allow(rustdoc::invalid_rust_codeblocks)]
+    pub RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX,
+    Allow,
+    "will be parsed as a guarded string in Rust 2024",
+    @future_incompatible = FutureIncompatibleInfo {
+        reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
+        reference: "issue #123735 <https://github.com/rust-lang/rust/issues/123735>",
+    };
+    crate_level_only
+}
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 386918a5c41..c01fa5c54d6 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -614,6 +614,8 @@ pub enum BuiltinLintDiag {
     ReservedPrefix(Span, String),
     /// `'r#` in edition < 2021.
     RawPrefix(Span),
+    /// `##` or `#"` is edition < 2024.
+    ReservedString(Span),
     TrailingMacro(bool, Ident),
     BreakWithLabelAndLoop(Span),
     UnicodeTextFlow(Span, String),
diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
index 8ee05977320..cda81d4a9b5 100644
--- a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
@@ -183,7 +183,7 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer(
            RustMappingRegions, NumMappingRegions)) {
     MappingRegions.emplace_back(
         fromRust(Region.Count), fromRust(Region.FalseCount),
-#if LLVM_VERSION_GE(18, 0) && LLVM_VERSION_LT(19, 0)
+#if LLVM_VERSION_LT(19, 0)
         coverage::CounterMappingRegion::MCDCParameters{},
 #endif
         Region.FileID, Region.ExpandedFileID, // File IDs, then region info.
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 8faa5212408..72b03fa0560 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -1533,27 +1533,40 @@ extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty,
 
 extern "C" LLVMValueRef
 LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M) {
+#if LLVM_VERSION_GE(20, 0)
+  return wrap(llvm::Intrinsic::getOrInsertDeclaration(
+      unwrap(M), llvm::Intrinsic::instrprof_increment));
+#else
   return wrap(llvm::Intrinsic::getDeclaration(
       unwrap(M), llvm::Intrinsic::instrprof_increment));
+#endif
 }
 
 extern "C" LLVMValueRef
 LLVMRustGetInstrProfMCDCParametersIntrinsic(LLVMModuleRef M) {
-#if LLVM_VERSION_GE(19, 0)
-  return wrap(llvm::Intrinsic::getDeclaration(
+#if LLVM_VERSION_LT(19, 0)
+  report_fatal_error("LLVM 19.0 is required for mcdc intrinsic functions");
+#endif
+#if LLVM_VERSION_GE(20, 0)
+  return wrap(llvm::Intrinsic::getOrInsertDeclaration(
       unwrap(M), llvm::Intrinsic::instrprof_mcdc_parameters));
 #else
-  report_fatal_error("LLVM 19.0 is required for mcdc intrinsic functions");
+  return wrap(llvm::Intrinsic::getDeclaration(
+      unwrap(M), llvm::Intrinsic::instrprof_mcdc_parameters));
 #endif
 }
 
 extern "C" LLVMValueRef
 LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(LLVMModuleRef M) {
-#if LLVM_VERSION_GE(19, 0)
-  return wrap(llvm::Intrinsic::getDeclaration(
+#if LLVM_VERSION_LT(19, 0)
+  report_fatal_error("LLVM 19.0 is required for mcdc intrinsic functions");
+#endif
+#if LLVM_VERSION_GE(20, 0)
+  return wrap(llvm::Intrinsic::getOrInsertDeclaration(
       unwrap(M), llvm::Intrinsic::instrprof_mcdc_tvbitmap_update));
 #else
-  report_fatal_error("LLVM 19.0 is required for mcdc intrinsic functions");
+  return wrap(llvm::Intrinsic::getDeclaration(
+      unwrap(M), llvm::Intrinsic::instrprof_mcdc_tvbitmap_update));
 #endif
 }
 
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index f02fd2ab6fe..4b406496337 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -1107,8 +1107,6 @@ impl<'a> CrateMetadataRef<'a> {
                 parent_did,
                 None,
                 data.is_non_exhaustive,
-                // FIXME: unnamed fields in crate metadata is unimplemented yet.
-                false,
             ),
         )
     }
@@ -1151,7 +1149,6 @@ impl<'a> CrateMetadataRef<'a> {
             adt_kind,
             variants.into_iter().map(|(_, variant)| variant).collect(),
             repr,
-            false,
         )
     }
 
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 610c682d3a4..afe03531861 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1100,9 +1100,12 @@ fn should_encode_variances<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, def_kind: Def
         | DefKind::Fn
         | DefKind::Ctor(..)
         | DefKind::AssocFn => true,
+        DefKind::AssocTy => {
+            // Only encode variances for RPITITs (for traits)
+            matches!(tcx.opt_rpitit_info(def_id), Some(ty::ImplTraitInTraitData::Trait { .. }))
+        }
         DefKind::Mod
         | DefKind::Field
-        | DefKind::AssocTy
         | DefKind::AssocConst
         | DefKind::TyParam
         | DefKind::ConstParam
diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml
index b23589afb58..8cb602d9ea8 100644
--- a/compiler/rustc_middle/Cargo.toml
+++ b/compiler/rustc_middle/Cargo.toml
@@ -12,6 +12,7 @@ field-offset = "0.3.5"
 gsgdt = "0.1.2"
 polonius-engine = "0.13.0"
 rustc-rayon-core = { version = "0.5.0", optional = true }
+rustc_abi = { path = "../rustc_abi" }
 rustc_apfloat = "0.2.0"
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs
index 70e61df1ab4..e9b73d25ba2 100644
--- a/compiler/rustc_middle/src/lib.rs
+++ b/compiler/rustc_middle/src/lib.rs
@@ -37,7 +37,6 @@
 #![feature(box_as_ptr)]
 #![feature(box_patterns)]
 #![feature(closure_track_caller)]
-#![feature(const_option)]
 #![feature(const_type_name)]
 #![feature(core_intrinsics)]
 #![feature(coroutines)]
diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs
index 4602c918160..3eb563d7d6e 100644
--- a/compiler/rustc_middle/src/mir/basic_blocks.rs
+++ b/compiler/rustc_middle/src/mir/basic_blocks.rs
@@ -26,7 +26,6 @@ type SwitchSources = FxHashMap<(BasicBlock, BasicBlock), SmallVec<[Option<u128>;
 struct Cache {
     predecessors: OnceLock<Predecessors>,
     switch_sources: OnceLock<SwitchSources>,
-    is_cyclic: OnceLock<bool>,
     reverse_postorder: OnceLock<Vec<BasicBlock>>,
     dominators: OnceLock<Dominators<BasicBlock>>,
 }
@@ -37,12 +36,6 @@ impl<'tcx> BasicBlocks<'tcx> {
         BasicBlocks { basic_blocks, cache: Cache::default() }
     }
 
-    /// Returns true if control-flow graph contains a cycle reachable from the `START_BLOCK`.
-    #[inline]
-    pub fn is_cfg_cyclic(&self) -> bool {
-        *self.cache.is_cyclic.get_or_init(|| graph::is_cyclic(self))
-    }
-
     pub fn dominators(&self) -> &Dominators<BasicBlock> {
         self.cache.dominators.get_or_init(|| dominators(self))
     }
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index 431043b0431..fcb87e19435 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -754,6 +754,7 @@ impl Drop for Guard {
 ///
 /// We also make things panic if this type is ever implicitly dropped.
 #[derive(Debug)]
+#[must_use]
 pub struct InterpResult_<'tcx, T> {
     res: Result<T, InterpErrorInfo<'tcx>>,
     guard: Guard,
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index caa86da8a85..8ee8b4c4823 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -25,6 +25,7 @@ use rustc_span::{DUMMY_SP, Span};
 // FIXME: Remove this import and import via `solve::`
 pub use rustc_type_ir::solve::{BuiltinImplSource, Reveal};
 use smallvec::{SmallVec, smallvec};
+use thin_vec::ThinVec;
 
 pub use self::select::{EvaluationCache, EvaluationResult, OverflowError, SelectionCache};
 use crate::mir::ConstraintCategory;
@@ -625,14 +626,14 @@ pub enum ImplSource<'tcx, N> {
     /// for some type parameter. The `Vec<N>` represents the
     /// obligations incurred from normalizing the where-clause (if
     /// any).
-    Param(Vec<N>),
+    Param(ThinVec<N>),
 
     /// Successful resolution for a builtin impl.
-    Builtin(BuiltinImplSource, Vec<N>),
+    Builtin(BuiltinImplSource, ThinVec<N>),
 }
 
 impl<'tcx, N> ImplSource<'tcx, N> {
-    pub fn nested_obligations(self) -> Vec<N> {
+    pub fn nested_obligations(self) -> ThinVec<N> {
         match self {
             ImplSource::UserDefined(i) => i.nested,
             ImplSource::Param(n) | ImplSource::Builtin(_, n) => n,
@@ -686,7 +687,7 @@ impl<'tcx, N> ImplSource<'tcx, N> {
 pub struct ImplSourceUserDefinedData<'tcx, N> {
     pub impl_def_id: DefId,
     pub args: GenericArgsRef<'tcx>,
-    pub nested: Vec<N>,
+    pub nested: ThinVec<N>,
 }
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable, PartialOrd, Ord)]
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 8f89f5c25af..3322a2643d7 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -259,12 +259,8 @@ impl AdtDefData {
         kind: AdtKind,
         variants: IndexVec<VariantIdx, VariantDef>,
         repr: ReprOptions,
-        is_anonymous: bool,
     ) -> Self {
-        debug!(
-            "AdtDef::new({:?}, {:?}, {:?}, {:?}, {:?})",
-            did, kind, variants, repr, is_anonymous
-        );
+        debug!("AdtDef::new({:?}, {:?}, {:?}, {:?})", did, kind, variants, repr);
         let mut flags = AdtFlags::NO_ADT_FLAGS;
 
         if kind == AdtKind::Enum && tcx.has_attr(did, sym::non_exhaustive) {
@@ -297,9 +293,6 @@ impl AdtDefData {
         if tcx.is_lang_item(did, LangItem::UnsafeCell) {
             flags |= AdtFlags::IS_UNSAFE_CELL;
         }
-        if is_anonymous {
-            flags |= AdtFlags::IS_ANONYMOUS;
-        }
 
         AdtDefData { did, variants, flags, repr }
     }
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 4a02fce5e7d..c2a55060490 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -12,6 +12,7 @@ use std::marker::PhantomData;
 use std::ops::{Bound, Deref};
 use std::{fmt, iter, mem};
 
+use rustc_abi::{FieldIdx, Layout, LayoutS, TargetDataLayout, VariantIdx};
 use rustc_ast::{self as ast, attr};
 use rustc_data_structures::defer;
 use rustc_data_structures::fingerprint::Fingerprint;
@@ -48,7 +49,6 @@ use rustc_session::{Limit, MetadataKind, Session};
 use rustc_span::def_id::{CRATE_DEF_ID, DefPathHash, StableCrateId};
 use rustc_span::symbol::{Ident, Symbol, kw, sym};
 use rustc_span::{DUMMY_SP, Span};
-use rustc_target::abi::{FieldIdx, Layout, LayoutS, TargetDataLayout, VariantIdx};
 use rustc_target::spec::abi;
 use rustc_type_ir::TyKind::*;
 use rustc_type_ir::fold::TypeFoldable;
@@ -539,6 +539,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.trait_def(trait_def_id).implement_via_object
     }
 
+    fn is_impl_trait_in_trait(self, def_id: DefId) -> bool {
+        self.is_impl_trait_in_trait(def_id)
+    }
+
     fn delay_bug(self, msg: impl ToString) -> ErrorGuaranteed {
         self.dcx().span_delayed_bug(DUMMY_SP, msg.to_string())
     }
@@ -698,6 +702,12 @@ impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>> for &'tcx rustc_featu
     }
 }
 
+impl<'tcx> rustc_type_ir::inherent::Span<TyCtxt<'tcx>> for Span {
+    fn dummy() -> Self {
+        DUMMY_SP
+    }
+}
+
 type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>;
 
 pub struct CtxtInterners<'tcx> {
@@ -1419,16 +1429,8 @@ impl<'tcx> TyCtxt<'tcx> {
         kind: AdtKind,
         variants: IndexVec<VariantIdx, ty::VariantDef>,
         repr: ReprOptions,
-        is_anonymous: bool,
     ) -> ty::AdtDef<'tcx> {
-        self.mk_adt_def_from_data(ty::AdtDefData::new(
-            self,
-            did,
-            kind,
-            variants,
-            repr,
-            is_anonymous,
-        ))
+        self.mk_adt_def_from_data(ty::AdtDefData::new(self, did, kind, variants, repr))
     }
 
     /// Allocates a read-only byte or string literal for `mir::interpret`.
@@ -3126,11 +3128,11 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     pub fn next_trait_solver_globally(self) -> bool {
-        self.sess.opts.unstable_opts.next_solver.map_or(false, |c| c.globally)
+        self.sess.opts.unstable_opts.next_solver.globally
     }
 
     pub fn next_trait_solver_in_coherence(self) -> bool {
-        self.sess.opts.unstable_opts.next_solver.map_or(false, |c| c.coherence)
+        self.sess.opts.unstable_opts.next_solver.coherence
     }
 
     pub fn is_impl_trait_in_trait(self, def_id: DefId) -> bool {
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index 751f0c71eb4..4f408ee1574 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -192,7 +192,8 @@ fn suggest_changing_unsized_bound(
             .iter()
             .enumerate()
             .filter(|(_, bound)| {
-                if let hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe) = bound
+                if let hir::GenericBound::Trait(poly) = bound
+                    && poly.modifiers == hir::TraitBoundModifier::Maybe
                     && poly.trait_ref.trait_def_id() == def_id
                 {
                     true
@@ -325,7 +326,7 @@ pub fn suggest_constraining_type_params<'a>(
             let suggestion = if span_to_replace.is_some() {
                 constraint.clone()
             } else if constraint.starts_with('<') {
-                constraint.to_string()
+                constraint.clone()
             } else if bound_list_non_empty {
                 format!(" + {constraint}")
             } else {
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 4ba2a9b1d73..6c12b691c26 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -2,11 +2,15 @@ use std::num::NonZero;
 use std::ops::Bound;
 use std::{cmp, fmt};
 
+use rustc_abi::Primitive::{self, Float, Int, Pointer};
+use rustc_abi::{
+    Abi, AddressSpace, Align, FieldsShape, HasDataLayout, Integer, LayoutCalculator, LayoutS,
+    PointeeInfo, PointerKind, ReprOptions, Scalar, Size, TagEncoding, TargetDataLayout, Variants,
+};
 use rustc_error_messages::DiagMessage;
 use rustc_errors::{
     Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level,
 };
-use rustc_hir as hir;
 use rustc_hir::LangItem;
 use rustc_hir::def_id::DefId;
 use rustc_index::IndexVec;
@@ -15,10 +19,11 @@ use rustc_session::config::OptLevel;
 use rustc_span::symbol::{Symbol, sym};
 use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
 use rustc_target::abi::call::FnAbi;
-use rustc_target::abi::*;
+use rustc_target::abi::{FieldIdx, TyAbiInterface, VariantIdx, call};
 use rustc_target::spec::abi::Abi as SpecAbi;
 use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, PanicStrategy, Target, WasmCAbi};
 use tracing::debug;
+use {rustc_abi as abi, rustc_hir as hir};
 
 use crate::error::UnsupportedFnAbi;
 use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
@@ -27,9 +32,10 @@ use crate::ty::normalize_erasing_regions::NormalizationError;
 use crate::ty::{self, CoroutineArgsExt, Ty, TyCtxt, TypeVisitableExt};
 
 #[extension(pub trait IntegerExt)]
-impl Integer {
+impl abi::Integer {
     #[inline]
     fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>, signed: bool) -> Ty<'tcx> {
+        use abi::Integer::{I8, I16, I32, I64, I128};
         match (*self, signed) {
             (I8, false) => tcx.types.u8,
             (I16, false) => tcx.types.u16,
@@ -44,7 +50,8 @@ impl Integer {
         }
     }
 
-    fn from_int_ty<C: HasDataLayout>(cx: &C, ity: ty::IntTy) -> Integer {
+    fn from_int_ty<C: HasDataLayout>(cx: &C, ity: ty::IntTy) -> abi::Integer {
+        use abi::Integer::{I8, I16, I32, I64, I128};
         match ity {
             ty::IntTy::I8 => I8,
             ty::IntTy::I16 => I16,
@@ -54,7 +61,8 @@ impl Integer {
             ty::IntTy::Isize => cx.data_layout().ptr_sized_integer(),
         }
     }
-    fn from_uint_ty<C: HasDataLayout>(cx: &C, ity: ty::UintTy) -> Integer {
+    fn from_uint_ty<C: HasDataLayout>(cx: &C, ity: ty::UintTy) -> abi::Integer {
+        use abi::Integer::{I8, I16, I32, I64, I128};
         match ity {
             ty::UintTy::U8 => I8,
             ty::UintTy::U16 => I16,
@@ -102,7 +110,7 @@ impl Integer {
             tcx.data_layout().c_enum_min_size
         } else {
             // repr(Rust) enums try to be as small as possible
-            I8
+            Integer::I8
         };
 
         // If there are no negative values, we can use the unsigned fit.
@@ -115,9 +123,10 @@ impl Integer {
 }
 
 #[extension(pub trait FloatExt)]
-impl Float {
+impl abi::Float {
     #[inline]
     fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
+        use abi::Float::*;
         match *self {
             F16 => tcx.types.f16,
             F32 => tcx.types.f32,
@@ -127,6 +136,7 @@ impl Float {
     }
 
     fn from_float_ty(fty: ty::FloatTy) -> Self {
+        use abi::Float::*;
         match fty {
             ty::FloatTy::F16 => F16,
             ty::FloatTy::F32 => F32,
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index f32daee7c44..ed24fcc7eb8 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1155,8 +1155,6 @@ bitflags::bitflags! {
         const NO_VARIANT_FLAGS        = 0;
         /// Indicates whether the field list of this variant is `#[non_exhaustive]`.
         const IS_FIELD_LIST_NON_EXHAUSTIVE = 1 << 0;
-        /// Indicates whether this variant has unnamed fields.
-        const HAS_UNNAMED_FIELDS = 1 << 1;
     }
 }
 rustc_data_structures::external_bitflags_debug! { VariantFlags }
@@ -1209,12 +1207,11 @@ impl VariantDef {
         parent_did: DefId,
         recover_tainted: Option<ErrorGuaranteed>,
         is_field_list_non_exhaustive: bool,
-        has_unnamed_fields: bool,
     ) -> Self {
         debug!(
             "VariantDef::new(name = {:?}, variant_did = {:?}, ctor = {:?}, discr = {:?},
-             fields = {:?}, adt_kind = {:?}, parent_did = {:?}, has_unnamed_fields = {:?})",
-            name, variant_did, ctor, discr, fields, adt_kind, parent_did, has_unnamed_fields,
+             fields = {:?}, adt_kind = {:?}, parent_did = {:?})",
+            name, variant_did, ctor, discr, fields, adt_kind, parent_did,
         );
 
         let mut flags = VariantFlags::NO_VARIANT_FLAGS;
@@ -1222,10 +1219,6 @@ impl VariantDef {
             flags |= VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE;
         }
 
-        if has_unnamed_fields {
-            flags |= VariantFlags::HAS_UNNAMED_FIELDS;
-        }
-
         VariantDef {
             def_id: variant_did.unwrap_or(parent_did),
             ctor,
@@ -1243,12 +1236,6 @@ impl VariantDef {
         self.flags.intersects(VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE)
     }
 
-    /// Does this variant contains unnamed fields
-    #[inline]
-    pub fn has_unnamed_fields(&self) -> bool {
-        self.flags.intersects(VariantFlags::HAS_UNNAMED_FIELDS)
-    }
-
     /// Computes the `Ident` of this variant by looking up the `Span`
     pub fn ident(&self, tcx: TyCtxt<'_>) -> Ident {
         Ident::new(self.name, tcx.def_ident_span(self.def_id).unwrap())
@@ -1434,11 +1421,6 @@ impl<'tcx> FieldDef {
     pub fn ident(&self, tcx: TyCtxt<'_>) -> Ident {
         Ident::new(self.name, tcx.def_ident_span(self.did).unwrap())
     }
-
-    /// Returns whether the field is unnamed
-    pub fn is_unnamed(&self) -> bool {
-        self.name == rustc_span::symbol::kw::Underscore
-    }
 }
 
 #[derive(Debug, PartialEq, Eq)]
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index 17280c3d047..6aa2b2cd480 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -42,12 +42,6 @@ pub struct TypeckResults<'tcx> {
     /// belongs, but it may not exist if it's a tuple field (`tuple.0`).
     field_indices: ItemLocalMap<FieldIdx>,
 
-    /// Resolved types and indices for the nested fields' accesses of `obj.field` (expanded
-    /// to `obj._(1)._(2).field` in THIR). This map only stores the intermediate type
-    /// of `obj._(1)` and index of `_(1)._(2)`, and the type of `_(1)._(2)`, and the index of
-    /// `_(2).field`.
-    nested_fields: ItemLocalMap<Vec<(Ty<'tcx>, FieldIdx)>>,
-
     /// Stores the types for various nodes in the AST. Note that this table
     /// is not guaranteed to be populated outside inference. See
     /// typeck::check::fn_ctxt for details.
@@ -79,9 +73,10 @@ pub struct TypeckResults<'tcx> {
     /// Stores the actual binding mode for all instances of [`BindingMode`].
     pat_binding_modes: ItemLocalMap<BindingMode>,
 
-    /// Top-level patterns whose match ergonomics need to be desugared
-    /// by the Rust 2021 -> 2024 migration lint.
-    rust_2024_migration_desugared_pats: ItemLocalSet,
+    /// Top-level patterns whose match ergonomics need to be desugared by the Rust 2021 -> 2024
+    /// migration lint. The boolean indicates whether the emitted diagnostic should be a hard error
+    /// (if any of the incompatible pattern elements are in edition 2024).
+    rust_2024_migration_desugared_pats: ItemLocalMap<bool>,
 
     /// Stores the types which were implicitly dereferenced in pattern binding modes
     /// for later usage in THIR lowering. For example,
@@ -225,7 +220,6 @@ impl<'tcx> TypeckResults<'tcx> {
             hir_owner,
             type_dependent_defs: Default::default(),
             field_indices: Default::default(),
-            nested_fields: Default::default(),
             user_provided_types: Default::default(),
             user_provided_sigs: Default::default(),
             node_types: Default::default(),
@@ -299,18 +293,6 @@ impl<'tcx> TypeckResults<'tcx> {
         self.field_indices().get(id).cloned()
     }
 
-    pub fn nested_fields(&self) -> LocalTableInContext<'_, Vec<(Ty<'tcx>, FieldIdx)>> {
-        LocalTableInContext { hir_owner: self.hir_owner, data: &self.nested_fields }
-    }
-
-    pub fn nested_fields_mut(&mut self) -> LocalTableInContextMut<'_, Vec<(Ty<'tcx>, FieldIdx)>> {
-        LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.nested_fields }
-    }
-
-    pub fn nested_field_tys_and_indices(&self, id: HirId) -> &[(Ty<'tcx>, FieldIdx)] {
-        self.nested_fields().get(id).map_or(&[], Vec::as_slice)
-    }
-
     pub fn user_provided_types(&self) -> LocalTableInContext<'_, CanonicalUserType<'tcx>> {
         LocalTableInContext { hir_owner: self.hir_owner, data: &self.user_provided_types }
     }
@@ -437,15 +419,15 @@ impl<'tcx> TypeckResults<'tcx> {
         LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments }
     }
 
-    pub fn rust_2024_migration_desugared_pats(&self) -> LocalSetInContext<'_> {
-        LocalSetInContext {
+    pub fn rust_2024_migration_desugared_pats(&self) -> LocalTableInContext<'_, bool> {
+        LocalTableInContext {
             hir_owner: self.hir_owner,
             data: &self.rust_2024_migration_desugared_pats,
         }
     }
 
-    pub fn rust_2024_migration_desugared_pats_mut(&mut self) -> LocalSetInContextMut<'_> {
-        LocalSetInContextMut {
+    pub fn rust_2024_migration_desugared_pats_mut(&mut self) -> LocalTableInContextMut<'_, bool> {
+        LocalTableInContextMut {
             hir_owner: self.hir_owner,
             data: &mut self.rust_2024_migration_desugared_pats,
         }
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl
index 1c4e9fd11cb..55149570dbc 100644
--- a/compiler/rustc_mir_build/messages.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
@@ -265,7 +265,7 @@ mir_build_pointer_pattern = function pointers and raw pointers not derived from
 
 mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
 
-mir_build_rust_2024_incompatible_pat = the semantics of this pattern will change in edition 2024
+mir_build_rust_2024_incompatible_pat = patterns are not allowed to reset the default binding mode in edition 2024
 
 mir_build_rustc_box_attribute_error = `#[rustc_box]` attribute used incorrectly
     .attributes = no other attributes may be applied
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 42be7f9402e..00f65e0c7d0 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -983,6 +983,8 @@ pub(crate) struct Rust2024IncompatiblePat {
 
 pub(crate) struct Rust2024IncompatiblePatSugg {
     pub(crate) suggestion: Vec<(Span, String)>,
+    /// Whether the incompatibility is a hard error because a relevant span is in edition 2024.
+    pub(crate) is_hard_error: bool,
 }
 
 impl Subdiagnostic for Rust2024IncompatiblePatSugg {
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index abf486af962..5995d60e7e0 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -808,21 +808,11 @@ impl<'tcx> Cx<'tcx> {
                 });
                 ExprKind::Loop { body }
             }
-            hir::ExprKind::Field(source, ..) => {
-                let mut kind = ExprKind::Field {
-                    lhs: self.mirror_expr(source),
-                    variant_index: FIRST_VARIANT,
-                    name: self.typeck_results.field_index(expr.hir_id),
-                };
-                let nested_field_tys_and_indices =
-                    self.typeck_results.nested_field_tys_and_indices(expr.hir_id);
-                for &(ty, idx) in nested_field_tys_and_indices {
-                    let expr = Expr { temp_lifetime, ty, span: source.span, kind };
-                    let lhs = self.thir.exprs.push(expr);
-                    kind = ExprKind::Field { lhs, variant_index: FIRST_VARIANT, name: idx };
-                }
-                kind
-            }
+            hir::ExprKind::Field(source, ..) => ExprKind::Field {
+                lhs: self.mirror_expr(source),
+                variant_index: FIRST_VARIANT,
+                name: self.typeck_results.field_index(expr.hir_id),
+            },
             hir::ExprKind::Cast(source, cast_ty) => {
                 // Check for a user-given type annotation on this `cast`
                 let user_provided_types = self.typeck_results.user_provided_types();
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 04e921ecc2e..56e5156a91f 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -25,6 +25,7 @@ use tracing::{debug, instrument};
 
 pub(crate) use self::check_match::check_match;
 use crate::errors::*;
+use crate::fluent_generated as fluent;
 use crate::thir::util::UserAnnotatedTyHelpers;
 
 struct PatCtxt<'a, 'tcx> {
@@ -48,18 +49,28 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
         typeck_results,
         rust_2024_migration_suggestion: typeck_results
             .rust_2024_migration_desugared_pats()
-            .contains(pat.hir_id)
-            .then_some(Rust2024IncompatiblePatSugg { suggestion: Vec::new() }),
+            .get(pat.hir_id)
+            .map(|&is_hard_error| Rust2024IncompatiblePatSugg {
+                suggestion: Vec::new(),
+                is_hard_error,
+            }),
     };
     let result = pcx.lower_pattern(pat);
     debug!("pat_from_hir({:?}) = {:?}", pat, result);
     if let Some(sugg) = pcx.rust_2024_migration_suggestion {
-        tcx.emit_node_span_lint(
-            lint::builtin::RUST_2024_INCOMPATIBLE_PAT,
-            pat.hir_id,
-            pat.span,
-            Rust2024IncompatiblePat { sugg },
-        );
+        if sugg.is_hard_error {
+            let mut err =
+                tcx.dcx().struct_span_err(pat.span, fluent::mir_build_rust_2024_incompatible_pat);
+            err.subdiagnostic(sugg);
+            err.emit();
+        } else {
+            tcx.emit_node_span_lint(
+                lint::builtin::RUST_2024_INCOMPATIBLE_PAT,
+                pat.hir_id,
+                pat.span,
+                Rust2024IncompatiblePat { sugg },
+            );
+        }
     }
     result
 }
diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs
index 7cfaef22689..5ebb343f4e1 100644
--- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs
@@ -7,7 +7,6 @@ use rustc_index::bit_set::BitSet;
 use rustc_middle::mir::{self, BasicBlock, Location};
 
 use super::{Analysis, Direction, Effect, EffectIndex, Results};
-use crate::framework::BitSetExt;
 
 /// Allows random access inspection of the results of a dataflow analysis.
 ///
@@ -221,16 +220,6 @@ where
     }
 }
 
-impl<'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A>
-where
-    A: crate::GenKillAnalysis<'tcx>,
-    A::Domain: BitSetExt<A::Idx>,
-{
-    pub fn contains(&self, elem: A::Idx) -> bool {
-        self.get().contains(elem)
-    }
-}
-
 #[derive(Clone, Copy, Debug)]
 struct CursorPosition {
     block: BasicBlock,
diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs
index 88a9a78f8ad..3e01f0512c4 100644
--- a/compiler/rustc_mir_dataflow/src/framework/direction.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs
@@ -5,7 +5,7 @@ use rustc_middle::mir::{
 };
 
 use super::visitor::{ResultsVisitable, ResultsVisitor};
-use super::{Analysis, Effect, EffectIndex, GenKillAnalysis, GenKillSet, SwitchIntTarget};
+use super::{Analysis, Effect, EffectIndex, SwitchIntTarget};
 
 pub trait Direction {
     const IS_FORWARD: bool;
@@ -29,19 +29,10 @@ pub trait Direction {
         state: &mut A::Domain,
         block: BasicBlock,
         block_data: &'mir mir::BasicBlockData<'tcx>,
-        statement_effect: Option<&dyn Fn(BasicBlock, &mut A::Domain)>,
     ) -> TerminatorEdges<'mir, 'tcx>
     where
         A: Analysis<'tcx>;
 
-    fn gen_kill_statement_effects_in_block<'tcx, A>(
-        analysis: &mut A,
-        trans: &mut GenKillSet<A::Idx>,
-        block: BasicBlock,
-        block_data: &mir::BasicBlockData<'tcx>,
-    ) where
-        A: GenKillAnalysis<'tcx>;
-
     fn visit_results_in_block<'mir, 'tcx, D, R>(
         state: &mut D,
         block: BasicBlock,
@@ -73,7 +64,6 @@ impl Direction for Backward {
         state: &mut A::Domain,
         block: BasicBlock,
         block_data: &'mir mir::BasicBlockData<'tcx>,
-        statement_effect: Option<&dyn Fn(BasicBlock, &mut A::Domain)>,
     ) -> TerminatorEdges<'mir, 'tcx>
     where
         A: Analysis<'tcx>,
@@ -82,31 +72,12 @@ impl Direction for Backward {
         let location = Location { block, statement_index: block_data.statements.len() };
         analysis.apply_before_terminator_effect(state, terminator, location);
         let edges = analysis.apply_terminator_effect(state, terminator, location);
-        if let Some(statement_effect) = statement_effect {
-            statement_effect(block, state)
-        } else {
-            for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
-                let location = Location { block, statement_index };
-                analysis.apply_before_statement_effect(state, statement, location);
-                analysis.apply_statement_effect(state, statement, location);
-            }
-        }
-        edges
-    }
-
-    fn gen_kill_statement_effects_in_block<'tcx, A>(
-        analysis: &mut A,
-        trans: &mut GenKillSet<A::Idx>,
-        block: BasicBlock,
-        block_data: &mir::BasicBlockData<'tcx>,
-    ) where
-        A: GenKillAnalysis<'tcx>,
-    {
         for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
             let location = Location { block, statement_index };
-            analysis.before_statement_effect(trans, statement, location);
-            analysis.statement_effect(trans, statement, location);
+            analysis.apply_before_statement_effect(state, statement, location);
+            analysis.apply_statement_effect(state, statement, location);
         }
+        edges
     }
 
     fn apply_effects_in_range<'tcx, A>(
@@ -330,42 +301,21 @@ impl Direction for Forward {
         state: &mut A::Domain,
         block: BasicBlock,
         block_data: &'mir mir::BasicBlockData<'tcx>,
-        statement_effect: Option<&dyn Fn(BasicBlock, &mut A::Domain)>,
     ) -> TerminatorEdges<'mir, 'tcx>
     where
         A: Analysis<'tcx>,
     {
-        if let Some(statement_effect) = statement_effect {
-            statement_effect(block, state)
-        } else {
-            for (statement_index, statement) in block_data.statements.iter().enumerate() {
-                let location = Location { block, statement_index };
-                analysis.apply_before_statement_effect(state, statement, location);
-                analysis.apply_statement_effect(state, statement, location);
-            }
+        for (statement_index, statement) in block_data.statements.iter().enumerate() {
+            let location = Location { block, statement_index };
+            analysis.apply_before_statement_effect(state, statement, location);
+            analysis.apply_statement_effect(state, statement, location);
         }
-
         let terminator = block_data.terminator();
         let location = Location { block, statement_index: block_data.statements.len() };
         analysis.apply_before_terminator_effect(state, terminator, location);
         analysis.apply_terminator_effect(state, terminator, location)
     }
 
-    fn gen_kill_statement_effects_in_block<'tcx, A>(
-        analysis: &mut A,
-        trans: &mut GenKillSet<A::Idx>,
-        block: BasicBlock,
-        block_data: &mir::BasicBlockData<'tcx>,
-    ) where
-        A: GenKillAnalysis<'tcx>,
-    {
-        for (statement_index, statement) in block_data.statements.iter().enumerate() {
-            let location = Location { block, statement_index };
-            analysis.before_statement_effect(trans, statement, location);
-            analysis.statement_effect(trans, statement, location);
-        }
-    }
-
     fn apply_effects_in_range<'tcx, A>(
         analysis: &mut A,
         state: &mut A::Domain,
diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs
index 9d50e57d668..cbd1083d037 100644
--- a/compiler/rustc_mir_dataflow/src/framework/engine.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs
@@ -5,7 +5,7 @@ use std::path::PathBuf;
 
 use rustc_data_structures::work_queue::WorkQueue;
 use rustc_hir::def_id::DefId;
-use rustc_index::{Idx, IndexVec};
+use rustc_index::IndexVec;
 use rustc_middle::bug;
 use rustc_middle::mir::{self, BasicBlock, create_dump_file, dump_enabled, traversal};
 use rustc_middle::ty::TyCtxt;
@@ -16,15 +16,13 @@ use {rustc_ast as ast, rustc_graphviz as dot};
 
 use super::fmt::DebugWithContext;
 use super::{
-    Analysis, AnalysisDomain, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice,
-    ResultsCursor, ResultsVisitor, graphviz, visit_results,
+    Analysis, Direction, JoinSemiLattice, ResultsCursor, ResultsVisitor, graphviz, visit_results,
 };
 use crate::errors::{
     DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter,
 };
-use crate::framework::BitSetExt;
 
-type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as AnalysisDomain<'tcx>>::Domain>;
+type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as Analysis<'tcx>>::Domain>;
 
 /// A dataflow analysis that has converged to fixpoint.
 #[derive(Clone)]
@@ -82,53 +80,6 @@ where
     entry_sets: IndexVec<BasicBlock, A::Domain>,
     pass_name: Option<&'static str>,
     analysis: A,
-
-    /// Cached, cumulative transfer functions for each block.
-    //
-    // FIXME(ecstaticmorse): This boxed `Fn` trait object is invoked inside a tight loop for
-    // gen/kill problems on cyclic CFGs. This is not ideal, but it doesn't seem to degrade
-    // performance in practice. I've tried a few ways to avoid this, but they have downsides. See
-    // the message for the commit that added this FIXME for more information.
-    apply_statement_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
-}
-
-impl<'mir, 'tcx, A, D, T> Engine<'mir, 'tcx, A>
-where
-    A: GenKillAnalysis<'tcx, Idx = T, Domain = D>,
-    D: Clone + JoinSemiLattice + GenKill<T> + BitSetExt<T>,
-    T: Idx,
-{
-    /// Creates a new `Engine` to solve a gen-kill dataflow problem.
-    pub fn new_gen_kill(tcx: TyCtxt<'tcx>, body: &'mir mir::Body<'tcx>, mut analysis: A) -> Self {
-        // If there are no back-edges in the control-flow graph, we only ever need to apply the
-        // transfer function for each block exactly once (assuming that we process blocks in RPO).
-        //
-        // In this case, there's no need to compute the block transfer functions ahead of time.
-        if !body.basic_blocks.is_cfg_cyclic() {
-            return Self::new(tcx, body, analysis, None);
-        }
-
-        // Otherwise, compute and store the cumulative transfer function for each block.
-
-        let identity = GenKillSet::identity(analysis.domain_size(body));
-        let mut trans_for_block = IndexVec::from_elem(identity, &body.basic_blocks);
-
-        for (block, block_data) in body.basic_blocks.iter_enumerated() {
-            let trans = &mut trans_for_block[block];
-            A::Direction::gen_kill_statement_effects_in_block(
-                &mut analysis,
-                trans,
-                block,
-                block_data,
-            );
-        }
-
-        let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| {
-            trans_for_block[bb].apply(state);
-        });
-
-        Self::new(tcx, body, analysis, Some(apply_trans as Box<_>))
-    }
 }
 
 impl<'mir, 'tcx, A, D> Engine<'mir, 'tcx, A>
@@ -138,19 +89,7 @@ where
 {
     /// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer
     /// function.
-    ///
-    /// Gen-kill problems should use `new_gen_kill`, which will coalesce transfer functions for
-    /// better performance.
-    pub fn new_generic(tcx: TyCtxt<'tcx>, body: &'mir mir::Body<'tcx>, analysis: A) -> Self {
-        Self::new(tcx, body, analysis, None)
-    }
-
-    fn new(
-        tcx: TyCtxt<'tcx>,
-        body: &'mir mir::Body<'tcx>,
-        analysis: A,
-        apply_statement_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
-    ) -> Self {
+    pub(crate) fn new(tcx: TyCtxt<'tcx>, body: &'mir mir::Body<'tcx>, analysis: A) -> Self {
         let mut entry_sets =
             IndexVec::from_fn_n(|_| analysis.bottom_value(body), body.basic_blocks.len());
         analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);
@@ -160,7 +99,7 @@ where
             bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
         }
 
-        Engine { analysis, tcx, body, pass_name: None, entry_sets, apply_statement_trans_for_block }
+        Engine { analysis, tcx, body, pass_name: None, entry_sets }
     }
 
     /// Adds an identifier to the graphviz output for this particular run of a dataflow analysis.
@@ -177,14 +116,7 @@ where
     where
         A::Domain: DebugWithContext<A>,
     {
-        let Engine {
-            mut analysis,
-            body,
-            mut entry_sets,
-            tcx,
-            apply_statement_trans_for_block,
-            pass_name,
-        } = self;
+        let Engine { mut analysis, body, mut entry_sets, tcx, pass_name } = self;
 
         let mut dirty_queue: WorkQueue<BasicBlock> = WorkQueue::with_none(body.basic_blocks.len());
 
@@ -213,13 +145,8 @@ where
             state.clone_from(&entry_sets[bb]);
 
             // Apply the block transfer function, using the cached one if it exists.
-            let edges = A::Direction::apply_effects_in_block(
-                &mut analysis,
-                &mut state,
-                bb,
-                bb_data,
-                apply_statement_trans_for_block.as_deref(),
-            );
+            let edges =
+                A::Direction::apply_effects_in_block(&mut analysis, &mut state, bb, bb_data);
 
             A::Direction::join_state_into_successors_of(
                 &mut analysis,
diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs
index d0472e35fe0..959f1ea5340 100644
--- a/compiler/rustc_mir_dataflow/src/framework/mod.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs
@@ -1,9 +1,11 @@
 //! A framework that can express both [gen-kill] and generic dataflow problems.
 //!
-//! To use this framework, implement either the [`Analysis`] or the
-//! [`GenKillAnalysis`] trait. If your transfer function can be expressed with only gen/kill
-//! operations, prefer `GenKillAnalysis` since it will run faster while iterating to fixpoint. The
-//! `impls` module contains several examples of gen/kill dataflow analyses.
+//! To use this framework, implement the [`Analysis`] trait. There used to be a `GenKillAnalysis`
+//! alternative trait for gen-kill analyses that would pre-compute the transfer function for each
+//! block. It was intended as an optimization, but it ended up not being any faster than
+//! `Analysis`.
+//!
+//! The `impls` module contains several examples of dataflow analyses.
 //!
 //! Create an `Engine` for your analysis using the `into_engine` method on the `Analysis` trait,
 //! then call `iterate_to_fixpoint`. From there, you can use a `ResultsCursor` to inspect the
@@ -87,11 +89,26 @@ impl<T: Idx> BitSetExt<T> for ChunkedBitSet<T> {
     }
 }
 
-/// Defines the domain of a dataflow problem.
+/// A dataflow problem with an arbitrarily complex transfer function.
+///
+/// This trait specifies the lattice on which this analysis operates (the domain), its
+/// initial value at the entry point of each basic block, and various operations.
+///
+/// # Convergence
+///
+/// When implementing this trait it's possible to choose a transfer function such that the analysis
+/// does not reach fixpoint. To guarantee convergence, your transfer functions must maintain the
+/// following invariant:
+///
+/// > If the dataflow state **before** some point in the program changes to be greater
+/// than the prior state **before** that point, the dataflow state **after** that point must
+/// also change to be greater than the prior state **after** that point.
 ///
-/// This trait specifies the lattice on which this analysis operates (the domain) as well as its
-/// initial value at the entry point of each basic block.
-pub trait AnalysisDomain<'tcx> {
+/// This invariant guarantees that the dataflow state at a given point in the program increases
+/// monotonically until fixpoint is reached. Note that this monotonicity requirement only applies
+/// to the same point in the program at different points in time. The dataflow state at a given
+/// point in the program may or may not be greater than the state at any preceding point.
+pub trait Analysis<'tcx> {
     /// The type that holds the dataflow state at any given point in the program.
     type Domain: Clone + JoinSemiLattice;
 
@@ -116,25 +133,7 @@ pub trait AnalysisDomain<'tcx> {
     // block where control flow could exit the MIR body (e.g., those terminated with `return` or
     // `resume`). It's not obvious how to handle `yield` points in coroutines, however.
     fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain);
-}
 
-/// A dataflow problem with an arbitrarily complex transfer function.
-///
-/// # Convergence
-///
-/// When implementing this trait directly (not via [`GenKillAnalysis`]), it's possible to choose a
-/// transfer function such that the analysis does not reach fixpoint. To guarantee convergence,
-/// your transfer functions must maintain the following invariant:
-///
-/// > If the dataflow state **before** some point in the program changes to be greater
-/// than the prior state **before** that point, the dataflow state **after** that point must
-/// also change to be greater than the prior state **after** that point.
-///
-/// This invariant guarantees that the dataflow state at a given point in the program increases
-/// monotonically until fixpoint is reached. Note that this monotonicity requirement only applies
-/// to the same point in the program at different points in time. The dataflow state at a given
-/// point in the program may or may not be greater than the state at any preceding point.
-pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
     /// Updates the current dataflow state with the effect of evaluating a statement.
     fn apply_statement_effect(
         &mut self,
@@ -165,10 +164,12 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
     /// initialized here.
     fn apply_terminator_effect<'mir>(
         &mut self,
-        state: &mut Self::Domain,
+        _state: &mut Self::Domain,
         terminator: &'mir mir::Terminator<'tcx>,
-        location: Location,
-    ) -> TerminatorEdges<'mir, 'tcx>;
+        _location: Location,
+    ) -> TerminatorEdges<'mir, 'tcx> {
+        terminator.edges()
+    }
 
     /// Updates the current dataflow state with an effect that occurs immediately *before* the
     /// given terminator.
@@ -193,10 +194,11 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
     /// edges.
     fn apply_call_return_effect(
         &mut self,
-        state: &mut Self::Domain,
-        block: BasicBlock,
-        return_places: CallReturnPlaces<'_, 'tcx>,
-    );
+        _state: &mut Self::Domain,
+        _block: BasicBlock,
+        _return_places: CallReturnPlaces<'_, 'tcx>,
+    ) {
+    }
 
     /// Updates the current dataflow state with the effect of taking a particular branch in a
     /// `SwitchInt` terminator.
@@ -223,9 +225,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
 
     /// Creates an `Engine` to find the fixpoint for this dataflow problem.
     ///
-    /// You shouldn't need to override this outside this module, since the combination of the
-    /// default impl and the one for all `A: GenKillAnalysis` will do the right thing.
-    /// Its purpose is to enable method chaining like so:
+    /// You shouldn't need to override this. Its purpose is to enable method chaining like so:
     ///
     /// ```ignore (cross-crate-imports)
     /// let results = MyAnalysis::new(tcx, body)
@@ -242,164 +242,11 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
     where
         Self: Sized,
     {
-        Engine::new_generic(tcx, body, self)
-    }
-}
-
-/// A gen/kill dataflow problem.
-///
-/// Each method in this trait has a corresponding one in `Analysis`. However, the first two methods
-/// here only allow modification of the dataflow state via "gen" and "kill" operations. By defining
-/// transfer functions for each statement in this way, the transfer function for an entire basic
-/// block can be computed efficiently. The remaining methods match up with `Analysis` exactly.
-///
-/// `Analysis` is automatically implemented for all implementers of `GenKillAnalysis` via a blanket
-/// impl below.
-pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
-    type Idx: Idx;
-
-    fn domain_size(&self, body: &mir::Body<'tcx>) -> usize;
-
-    /// See `Analysis::apply_statement_effect`. Note how the second arg differs.
-    fn statement_effect(
-        &mut self,
-        trans: &mut impl GenKill<Self::Idx>,
-        statement: &mir::Statement<'tcx>,
-        location: Location,
-    );
-
-    /// See `Analysis::apply_before_statement_effect`. Note how the second arg
-    /// differs.
-    fn before_statement_effect(
-        &mut self,
-        _trans: &mut impl GenKill<Self::Idx>,
-        _statement: &mir::Statement<'tcx>,
-        _location: Location,
-    ) {
-    }
-
-    /// See `Analysis::apply_terminator_effect`.
-    fn terminator_effect<'mir>(
-        &mut self,
-        trans: &mut Self::Domain,
-        terminator: &'mir mir::Terminator<'tcx>,
-        location: Location,
-    ) -> TerminatorEdges<'mir, 'tcx>;
-
-    /// See `Analysis::apply_before_terminator_effect`.
-    fn before_terminator_effect(
-        &mut self,
-        _trans: &mut Self::Domain,
-        _terminator: &mir::Terminator<'tcx>,
-        _location: Location,
-    ) {
-    }
-
-    /* Edge-specific effects */
-
-    /// See `Analysis::apply_call_return_effect`.
-    fn call_return_effect(
-        &mut self,
-        trans: &mut Self::Domain,
-        block: BasicBlock,
-        return_places: CallReturnPlaces<'_, 'tcx>,
-    );
-
-    /// See `Analysis::apply_switch_int_edge_effects`.
-    fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
-        &mut self,
-        _block: BasicBlock,
-        _discr: &mir::Operand<'tcx>,
-        _edge_effects: &mut impl SwitchIntEdgeEffects<G>,
-    ) {
-    }
-}
-
-// Blanket impl: any impl of `GenKillAnalysis` automatically impls `Analysis`.
-impl<'tcx, A> Analysis<'tcx> for A
-where
-    A: GenKillAnalysis<'tcx>,
-    A::Domain: GenKill<A::Idx> + BitSetExt<A::Idx>,
-{
-    fn apply_statement_effect(
-        &mut self,
-        state: &mut A::Domain,
-        statement: &mir::Statement<'tcx>,
-        location: Location,
-    ) {
-        self.statement_effect(state, statement, location);
-    }
-
-    fn apply_before_statement_effect(
-        &mut self,
-        state: &mut A::Domain,
-        statement: &mir::Statement<'tcx>,
-        location: Location,
-    ) {
-        self.before_statement_effect(state, statement, location);
-    }
-
-    fn apply_terminator_effect<'mir>(
-        &mut self,
-        state: &mut A::Domain,
-        terminator: &'mir mir::Terminator<'tcx>,
-        location: Location,
-    ) -> TerminatorEdges<'mir, 'tcx> {
-        self.terminator_effect(state, terminator, location)
-    }
-
-    fn apply_before_terminator_effect(
-        &mut self,
-        state: &mut A::Domain,
-        terminator: &mir::Terminator<'tcx>,
-        location: Location,
-    ) {
-        self.before_terminator_effect(state, terminator, location);
-    }
-
-    /* Edge-specific effects */
-
-    fn apply_call_return_effect(
-        &mut self,
-        state: &mut A::Domain,
-        block: BasicBlock,
-        return_places: CallReturnPlaces<'_, 'tcx>,
-    ) {
-        self.call_return_effect(state, block, return_places);
-    }
-
-    fn apply_switch_int_edge_effects(
-        &mut self,
-        block: BasicBlock,
-        discr: &mir::Operand<'tcx>,
-        edge_effects: &mut impl SwitchIntEdgeEffects<A::Domain>,
-    ) {
-        self.switch_int_edge_effects(block, discr, edge_effects);
-    }
-
-    /* Extension methods */
-    #[inline]
-    fn into_engine<'mir>(
-        self,
-        tcx: TyCtxt<'tcx>,
-        body: &'mir mir::Body<'tcx>,
-    ) -> Engine<'mir, 'tcx, Self>
-    where
-        Self: Sized,
-    {
-        Engine::new_gen_kill(tcx, body, self)
+        Engine::new(tcx, body, self)
     }
 }
 
 /// The legal operations for a transfer function in a gen/kill problem.
-///
-/// This abstraction exists because there are two different contexts in which we call the methods in
-/// `GenKillAnalysis`. Sometimes we need to store a single transfer function that can be efficiently
-/// applied multiple times, such as when computing the cumulative transfer function for each block.
-/// These cases require a `GenKillSet`, which in turn requires two `BitSet`s of storage. Oftentimes,
-/// however, we only need to apply an effect once. In *these* cases, it is more efficient to pass the
-/// `BitSet` representing the state vector directly into the `*_effect` methods as opposed to
-/// building up a `GenKillSet` and then throwing it away.
 pub trait GenKill<T> {
     /// Inserts `elem` into the state vector.
     fn gen_(&mut self, elem: T);
@@ -422,44 +269,6 @@ pub trait GenKill<T> {
     }
 }
 
-/// Stores a transfer function for a gen/kill problem.
-///
-/// Calling `gen_`/`kill` on a `GenKillSet` will "build up" a transfer function so that it can be
-/// applied multiple times efficiently. When there are multiple calls to `gen_` and/or `kill` for
-/// the same element, the most recent one takes precedence.
-#[derive(Clone)]
-pub struct GenKillSet<T> {
-    gen_: HybridBitSet<T>,
-    kill: HybridBitSet<T>,
-}
-
-impl<T: Idx> GenKillSet<T> {
-    /// Creates a new transfer function that will leave the dataflow state unchanged.
-    pub fn identity(universe: usize) -> Self {
-        GenKillSet {
-            gen_: HybridBitSet::new_empty(universe),
-            kill: HybridBitSet::new_empty(universe),
-        }
-    }
-
-    pub fn apply(&self, state: &mut impl BitSetExt<T>) {
-        state.union(&self.gen_);
-        state.subtract(&self.kill);
-    }
-}
-
-impl<T: Idx> GenKill<T> for GenKillSet<T> {
-    fn gen_(&mut self, elem: T) {
-        self.gen_.insert(elem);
-        self.kill.remove(elem);
-    }
-
-    fn kill(&mut self, elem: T) {
-        self.kill.insert(elem);
-        self.gen_.remove(elem);
-    }
-}
-
 impl<T: Idx> GenKill<T> for BitSet<T> {
     fn gen_(&mut self, elem: T) {
         self.insert(elem);
diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs
index 1861f4cffc7..14fb6dfb50c 100644
--- a/compiler/rustc_mir_dataflow/src/framework/tests.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs
@@ -154,7 +154,7 @@ impl<D: Direction> MockAnalysis<'_, D> {
     }
 }
 
-impl<'tcx, D: Direction> AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> {
+impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
     type Domain = BitSet<usize>;
     type Direction = D;
 
@@ -167,9 +167,7 @@ impl<'tcx, D: Direction> AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> {
     fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
         unimplemented!("This is never called since `MockAnalysis` is never iterated to fixpoint");
     }
-}
 
-impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
     fn apply_statement_effect(
         &mut self,
         state: &mut Self::Domain,
@@ -210,14 +208,6 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
         let idx = self.effect(Effect::Before.at_index(location.statement_index));
         assert!(state.insert(idx));
     }
-
-    fn apply_call_return_effect(
-        &mut self,
-        _state: &mut Self::Domain,
-        _block: BasicBlock,
-        _return_places: CallReturnPlaces<'_, 'tcx>,
-    ) {
-    }
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
index 8b082ef2667..859019fd1f6 100644
--- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
@@ -2,7 +2,7 @@ use rustc_index::bit_set::BitSet;
 use rustc_middle::mir::visit::Visitor;
 use rustc_middle::mir::*;
 
-use crate::{AnalysisDomain, GenKill, GenKillAnalysis};
+use crate::{Analysis, GenKill};
 
 /// A dataflow analysis that tracks whether a pointer or reference could possibly exist that points
 /// to a given local. This analysis ignores fake borrows, so it should not be used by
@@ -20,7 +20,7 @@ impl MaybeBorrowedLocals {
     }
 }
 
-impl<'tcx> AnalysisDomain<'tcx> for MaybeBorrowedLocals {
+impl<'tcx> Analysis<'tcx> for MaybeBorrowedLocals {
     type Domain = BitSet<Local>;
     const NAME: &'static str = "maybe_borrowed_locals";
 
@@ -32,25 +32,17 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeBorrowedLocals {
     fn initialize_start_block(&self, _: &Body<'tcx>, _: &mut Self::Domain) {
         // No locals are aliased on function entry
     }
-}
-
-impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals {
-    type Idx = Local;
 
-    fn domain_size(&self, body: &Body<'tcx>) -> usize {
-        body.local_decls.len()
-    }
-
-    fn statement_effect(
+    fn apply_statement_effect(
         &mut self,
-        trans: &mut impl GenKill<Self::Idx>,
+        trans: &mut Self::Domain,
         statement: &Statement<'tcx>,
         location: Location,
     ) {
         self.transfer_function(trans).visit_statement(statement, location);
     }
 
-    fn terminator_effect<'mir>(
+    fn apply_terminator_effect<'mir>(
         &mut self,
         trans: &mut Self::Domain,
         terminator: &'mir Terminator<'tcx>,
@@ -59,14 +51,6 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals {
         self.transfer_function(trans).visit_terminator(terminator, location);
         terminator.edges()
     }
-
-    fn call_return_effect(
-        &mut self,
-        _trans: &mut Self::Domain,
-        _block: BasicBlock,
-        _return_places: CallReturnPlaces<'_, 'tcx>,
-    ) {
-    }
 }
 
 /// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`.
diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs
index 25e8726cf61..9bb50d1e056 100644
--- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs
@@ -11,9 +11,8 @@ use crate::elaborate_drops::DropFlagState;
 use crate::framework::SwitchIntEdgeEffects;
 use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
 use crate::{
-    AnalysisDomain, GenKill, GenKillAnalysis, MaybeReachable, drop_flag_effects,
-    drop_flag_effects_for_function_entry, drop_flag_effects_for_location, lattice,
-    on_all_children_bits, on_lookup_result_bits,
+    Analysis, GenKill, MaybeReachable, drop_flag_effects, drop_flag_effects_for_function_entry,
+    drop_flag_effects_for_location, lattice, on_all_children_bits, on_lookup_result_bits,
 };
 
 /// `MaybeInitializedPlaces` tracks all places that might be
@@ -270,7 +269,7 @@ impl<'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'_, 'tcx> {
 
 impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
     fn update_bits(
-        trans: &mut impl GenKill<MovePathIndex>,
+        trans: &mut <Self as Analysis<'tcx>>::Domain,
         path: MovePathIndex,
         state: DropFlagState,
     ) {
@@ -283,7 +282,7 @@ impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
 
 impl<'tcx> MaybeUninitializedPlaces<'_, 'tcx> {
     fn update_bits(
-        trans: &mut impl GenKill<MovePathIndex>,
+        trans: &mut <Self as Analysis<'tcx>>::Domain,
         path: MovePathIndex,
         state: DropFlagState,
     ) {
@@ -296,7 +295,7 @@ impl<'tcx> MaybeUninitializedPlaces<'_, 'tcx> {
 
 impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
     fn update_bits(
-        trans: &mut impl GenKill<MovePathIndex>,
+        trans: &mut <Self as Analysis<'tcx>>::Domain,
         path: MovePathIndex,
         state: DropFlagState,
     ) {
@@ -307,7 +306,7 @@ impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
     }
 }
 
-impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
+impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
     /// There can be many more `MovePathIndex` than there are locals in a MIR body.
     /// We use a chunked bitset to avoid paying too high a memory footprint.
     type Domain = MaybeReachable<ChunkedBitSet<MovePathIndex>>;
@@ -327,18 +326,10 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
             state.gen_(path);
         });
     }
-}
-
-impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
-    type Idx = MovePathIndex;
 
-    fn domain_size(&self, _: &Body<'tcx>) -> usize {
-        self.move_data().move_paths.len()
-    }
-
-    fn statement_effect(
+    fn apply_statement_effect(
         &mut self,
-        trans: &mut impl GenKill<Self::Idx>,
+        trans: &mut Self::Domain,
         statement: &mir::Statement<'tcx>,
         location: Location,
     ) {
@@ -360,7 +351,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
         }
     }
 
-    fn terminator_effect<'mir>(
+    fn apply_terminator_effect<'mir>(
         &mut self,
         state: &mut Self::Domain,
         terminator: &'mir mir::Terminator<'tcx>,
@@ -380,7 +371,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
         edges
     }
 
-    fn call_return_effect(
+    fn apply_call_return_effect(
         &mut self,
         trans: &mut Self::Domain,
         _block: mir::BasicBlock,
@@ -399,11 +390,11 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
         });
     }
 
-    fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
+    fn apply_switch_int_edge_effects(
         &mut self,
         block: mir::BasicBlock,
         discr: &mir::Operand<'tcx>,
-        edge_effects: &mut impl SwitchIntEdgeEffects<G>,
+        edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
     ) {
         if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
             return;
@@ -442,7 +433,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
     }
 }
 
-impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
+impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
     /// There can be many more `MovePathIndex` than there are locals in a MIR body.
     /// We use a chunked bitset to avoid paying too high a memory footprint.
     type Domain = ChunkedBitSet<MovePathIndex>;
@@ -464,18 +455,10 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
             state.remove(path);
         });
     }
-}
-
-impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
-    type Idx = MovePathIndex;
 
-    fn domain_size(&self, _: &Body<'tcx>) -> usize {
-        self.move_data().move_paths.len()
-    }
-
-    fn statement_effect(
+    fn apply_statement_effect(
         &mut self,
-        trans: &mut impl GenKill<Self::Idx>,
+        trans: &mut Self::Domain,
         _statement: &mir::Statement<'tcx>,
         location: Location,
     ) {
@@ -487,7 +470,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
         // mutable borrow occurs. Places cannot become uninitialized through a mutable reference.
     }
 
-    fn terminator_effect<'mir>(
+    fn apply_terminator_effect<'mir>(
         &mut self,
         trans: &mut Self::Domain,
         terminator: &'mir mir::Terminator<'tcx>,
@@ -505,7 +488,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
         }
     }
 
-    fn call_return_effect(
+    fn apply_call_return_effect(
         &mut self,
         trans: &mut Self::Domain,
         _block: mir::BasicBlock,
@@ -524,11 +507,11 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
         });
     }
 
-    fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
+    fn apply_switch_int_edge_effects(
         &mut self,
         block: mir::BasicBlock,
         discr: &mir::Operand<'tcx>,
-        edge_effects: &mut impl SwitchIntEdgeEffects<G>,
+        edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
     ) {
         if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
             return;
@@ -571,7 +554,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> AnalysisDomain<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
+impl<'a, 'tcx> Analysis<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
     /// Use set intersection as the join operator.
     type Domain = lattice::Dual<BitSet<MovePathIndex>>;
 
@@ -591,18 +574,10 @@ impl<'a, 'tcx> AnalysisDomain<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
             state.0.insert(path);
         });
     }
-}
-
-impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
-    type Idx = MovePathIndex;
 
-    fn domain_size(&self, _: &Body<'tcx>) -> usize {
-        self.move_data().move_paths.len()
-    }
-
-    fn statement_effect(
+    fn apply_statement_effect(
         &mut self,
-        trans: &mut impl GenKill<Self::Idx>,
+        trans: &mut Self::Domain,
         _statement: &mir::Statement<'tcx>,
         location: Location,
     ) {
@@ -611,7 +586,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
         })
     }
 
-    fn terminator_effect<'mir>(
+    fn apply_terminator_effect<'mir>(
         &mut self,
         trans: &mut Self::Domain,
         terminator: &'mir mir::Terminator<'tcx>,
@@ -623,7 +598,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
         terminator.edges()
     }
 
-    fn call_return_effect(
+    fn apply_call_return_effect(
         &mut self,
         trans: &mut Self::Domain,
         _block: mir::BasicBlock,
@@ -643,7 +618,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
     }
 }
 
-impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> {
+impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
     /// There can be many more `InitIndex` than there are locals in a MIR body.
     /// We use a chunked bitset to avoid paying too high a memory footprint.
     type Domain = ChunkedBitSet<InitIndex>;
@@ -660,19 +635,11 @@ impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> {
             state.insert(InitIndex::new(arg_init));
         }
     }
-}
-
-impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
-    type Idx = InitIndex;
-
-    fn domain_size(&self, _: &Body<'tcx>) -> usize {
-        self.move_data().inits.len()
-    }
 
     #[instrument(skip(self, trans), level = "debug")]
-    fn statement_effect(
+    fn apply_statement_effect(
         &mut self,
-        trans: &mut impl GenKill<Self::Idx>,
+        trans: &mut Self::Domain,
         stmt: &mir::Statement<'tcx>,
         location: Location,
     ) {
@@ -698,7 +665,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
     }
 
     #[instrument(skip(self, trans, terminator), level = "debug")]
-    fn terminator_effect<'mir>(
+    fn apply_terminator_effect<'mir>(
         &mut self,
         trans: &mut Self::Domain,
         terminator: &'mir mir::Terminator<'tcx>,
@@ -720,7 +687,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
         terminator.edges()
     }
 
-    fn call_return_effect(
+    fn apply_call_return_effect(
         &mut self,
         trans: &mut Self::Domain,
         block: mir::BasicBlock,
diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
index 1559c131a37..e06c1f2bb49 100644
--- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
@@ -4,7 +4,7 @@ use rustc_middle::mir::{
     self, CallReturnPlaces, Local, Location, Place, StatementKind, TerminatorEdges,
 };
 
-use crate::{Analysis, AnalysisDomain, Backward, GenKill, GenKillAnalysis};
+use crate::{Analysis, Backward, GenKill};
 
 /// A [live-variable dataflow analysis][liveness].
 ///
@@ -25,7 +25,7 @@ use crate::{Analysis, AnalysisDomain, Backward, GenKill, GenKillAnalysis};
 /// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis
 pub struct MaybeLiveLocals;
 
-impl<'tcx> AnalysisDomain<'tcx> for MaybeLiveLocals {
+impl<'tcx> Analysis<'tcx> for MaybeLiveLocals {
     type Domain = BitSet<Local>;
     type Direction = Backward;
 
@@ -39,25 +39,17 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeLiveLocals {
     fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
         // No variables are live until we observe a use
     }
-}
-
-impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
-    type Idx = Local;
-
-    fn domain_size(&self, body: &mir::Body<'tcx>) -> usize {
-        body.local_decls.len()
-    }
 
-    fn statement_effect(
+    fn apply_statement_effect(
         &mut self,
-        trans: &mut impl GenKill<Self::Idx>,
+        trans: &mut Self::Domain,
         statement: &mir::Statement<'tcx>,
         location: Location,
     ) {
         TransferFunction(trans).visit_statement(statement, location);
     }
 
-    fn terminator_effect<'mir>(
+    fn apply_terminator_effect<'mir>(
         &mut self,
         trans: &mut Self::Domain,
         terminator: &'mir mir::Terminator<'tcx>,
@@ -67,7 +59,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
         terminator.edges()
     }
 
-    fn call_return_effect(
+    fn apply_call_return_effect(
         &mut self,
         trans: &mut Self::Domain,
         _block: mir::BasicBlock,
@@ -89,12 +81,9 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
     }
 }
 
-pub struct TransferFunction<'a, T>(pub &'a mut T);
+pub struct TransferFunction<'a>(pub &'a mut BitSet<Local>);
 
-impl<'tcx, T> Visitor<'tcx> for TransferFunction<'_, T>
-where
-    T: GenKill<Local>,
-{
+impl<'tcx> Visitor<'tcx> for TransferFunction<'_> {
     fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) {
         if let PlaceContext::MutatingUse(MutatingUseContext::Yield) = context {
             // The resume place is evaluated and assigned to only after coroutine resumes, so its
@@ -108,10 +97,10 @@ where
                     MutatingUseContext::Call | MutatingUseContext::AsmOutput,
                 ) = context
                 {
-                    // For the associated terminators, this is only a `Def` when the terminator returns
-                    // "successfully." As such, we handle this case separately in `call_return_effect`
-                    // above. However, if the place looks like `*_5`, this is still unconditionally a use of
-                    // `_5`.
+                    // For the associated terminators, this is only a `Def` when the terminator
+                    // returns "successfully." As such, we handle this case separately in
+                    // `call_return_effect` above. However, if the place looks like `*_5`, this is
+                    // still unconditionally a use of `_5`.
                 } else {
                     self.0.kill(place.local);
                 }
@@ -128,12 +117,9 @@ where
     }
 }
 
-struct YieldResumeEffect<'a, T>(&'a mut T);
+struct YieldResumeEffect<'a>(&'a mut BitSet<Local>);
 
-impl<'tcx, T> Visitor<'tcx> for YieldResumeEffect<'_, T>
-where
-    T: GenKill<Local>,
-{
+impl<'tcx> Visitor<'tcx> for YieldResumeEffect<'_> {
     fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) {
         DefUse::apply(self.0, *place, context);
         self.visit_projection(place.as_ref(), context, location);
@@ -151,7 +137,7 @@ enum DefUse {
 }
 
 impl DefUse {
-    fn apply(trans: &mut impl GenKill<Local>, place: Place<'_>, context: PlaceContext) {
+    fn apply(trans: &mut BitSet<Local>, place: Place<'_>, context: PlaceContext) {
         match DefUse::for_place(place, context) {
             Some(DefUse::Def) => trans.kill(place.local),
             Some(DefUse::Use) => trans.gen_(place.local),
@@ -231,7 +217,7 @@ impl<'a> MaybeTransitiveLiveLocals<'a> {
     }
 }
 
-impl<'a, 'tcx> AnalysisDomain<'tcx> for MaybeTransitiveLiveLocals<'a> {
+impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
     type Domain = BitSet<Local>;
     type Direction = Backward;
 
@@ -245,9 +231,7 @@ impl<'a, 'tcx> AnalysisDomain<'tcx> for MaybeTransitiveLiveLocals<'a> {
     fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
         // No variables are live until we observe a use
     }
-}
 
-impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
     fn apply_statement_effect(
         &mut self,
         trans: &mut Self::Domain,
diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
index 34ac5809a2e..c5fd2a631ff 100644
--- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
@@ -5,7 +5,7 @@ use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
 
 use super::MaybeBorrowedLocals;
-use crate::{GenKill, ResultsCursor};
+use crate::{Analysis, GenKill, ResultsCursor};
 
 pub struct MaybeStorageLive<'a> {
     always_live_locals: Cow<'a, BitSet<Local>>,
@@ -17,7 +17,7 @@ impl<'a> MaybeStorageLive<'a> {
     }
 }
 
-impl<'a, 'tcx> crate::AnalysisDomain<'tcx> for MaybeStorageLive<'a> {
+impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageLive<'a> {
     type Domain = BitSet<Local>;
 
     const NAME: &'static str = "maybe_storage_live";
@@ -37,18 +37,10 @@ impl<'a, 'tcx> crate::AnalysisDomain<'tcx> for MaybeStorageLive<'a> {
             on_entry.insert(arg);
         }
     }
-}
-
-impl<'a, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> {
-    type Idx = Local;
 
-    fn domain_size(&self, body: &Body<'tcx>) -> usize {
-        body.local_decls.len()
-    }
-
-    fn statement_effect(
+    fn apply_statement_effect(
         &mut self,
-        trans: &mut impl GenKill<Self::Idx>,
+        trans: &mut Self::Domain,
         stmt: &Statement<'tcx>,
         _: Location,
     ) {
@@ -58,25 +50,6 @@ impl<'a, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> {
             _ => (),
         }
     }
-
-    fn terminator_effect<'mir>(
-        &mut self,
-        _trans: &mut Self::Domain,
-        terminator: &'mir Terminator<'tcx>,
-        _: Location,
-    ) -> TerminatorEdges<'mir, 'tcx> {
-        // Terminators have no effect
-        terminator.edges()
-    }
-
-    fn call_return_effect(
-        &mut self,
-        _trans: &mut Self::Domain,
-        _block: BasicBlock,
-        _return_places: CallReturnPlaces<'_, 'tcx>,
-    ) {
-        // Nothing to do when a call returns successfully
-    }
 }
 
 pub struct MaybeStorageDead<'a> {
@@ -89,7 +62,7 @@ impl<'a> MaybeStorageDead<'a> {
     }
 }
 
-impl<'a, 'tcx> crate::AnalysisDomain<'tcx> for MaybeStorageDead<'a> {
+impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageDead<'a> {
     type Domain = BitSet<Local>;
 
     const NAME: &'static str = "maybe_storage_dead";
@@ -108,18 +81,10 @@ impl<'a, 'tcx> crate::AnalysisDomain<'tcx> for MaybeStorageDead<'a> {
             }
         }
     }
-}
-
-impl<'a, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead<'a> {
-    type Idx = Local;
-
-    fn domain_size(&self, body: &Body<'tcx>) -> usize {
-        body.local_decls.len()
-    }
 
-    fn statement_effect(
+    fn apply_statement_effect(
         &mut self,
-        trans: &mut impl GenKill<Self::Idx>,
+        trans: &mut Self::Domain,
         stmt: &Statement<'tcx>,
         _: Location,
     ) {
@@ -129,25 +94,6 @@ impl<'a, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead<'a> {
             _ => (),
         }
     }
-
-    fn terminator_effect<'mir>(
-        &mut self,
-        _: &mut Self::Domain,
-        terminator: &'mir Terminator<'tcx>,
-        _: Location,
-    ) -> TerminatorEdges<'mir, 'tcx> {
-        // Terminators have no effect
-        terminator.edges()
-    }
-
-    fn call_return_effect(
-        &mut self,
-        _trans: &mut Self::Domain,
-        _block: BasicBlock,
-        _return_places: CallReturnPlaces<'_, 'tcx>,
-    ) {
-        // Nothing to do when a call returns successfully
-    }
 }
 
 type BorrowedLocalsResults<'mir, 'tcx> = ResultsCursor<'mir, 'tcx, MaybeBorrowedLocals>;
@@ -164,7 +110,7 @@ impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
     }
 }
 
-impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
+impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
     type Domain = BitSet<Local>;
 
     const NAME: &'static str = "requires_storage";
@@ -181,23 +127,15 @@ impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
             on_entry.insert(arg);
         }
     }
-}
-
-impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
-    type Idx = Local;
 
-    fn domain_size(&self, body: &Body<'tcx>) -> usize {
-        body.local_decls.len()
-    }
-
-    fn before_statement_effect(
+    fn apply_before_statement_effect(
         &mut self,
-        trans: &mut impl GenKill<Self::Idx>,
+        trans: &mut Self::Domain,
         stmt: &Statement<'tcx>,
         loc: Location,
     ) {
         // If a place is borrowed in a statement, it needs storage for that statement.
-        self.borrowed_locals.mut_analysis().statement_effect(trans, stmt, loc);
+        self.borrowed_locals.mut_analysis().apply_statement_effect(trans, stmt, loc);
 
         match &stmt.kind {
             StatementKind::StorageDead(l) => trans.kill(*l),
@@ -223,9 +161,9 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
         }
     }
 
-    fn statement_effect(
+    fn apply_statement_effect(
         &mut self,
-        trans: &mut impl GenKill<Self::Idx>,
+        trans: &mut Self::Domain,
         _: &Statement<'tcx>,
         loc: Location,
     ) {
@@ -234,7 +172,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
         self.check_for_move(trans, loc);
     }
 
-    fn before_terminator_effect(
+    fn apply_before_terminator_effect(
         &mut self,
         trans: &mut Self::Domain,
         terminator: &Terminator<'tcx>,
@@ -292,7 +230,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
         }
     }
 
-    fn terminator_effect<'t>(
+    fn apply_terminator_effect<'t>(
         &mut self,
         trans: &mut Self::Domain,
         terminator: &'t Terminator<'tcx>,
@@ -333,7 +271,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
         terminator.edges()
     }
 
-    fn call_return_effect(
+    fn apply_call_return_effect(
         &mut self,
         trans: &mut Self::Domain,
         _block: BasicBlock,
@@ -345,26 +283,23 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
 
 impl<'tcx> MaybeRequiresStorage<'_, 'tcx> {
     /// Kill locals that are fully moved and have not been borrowed.
-    fn check_for_move(&mut self, trans: &mut impl GenKill<Local>, loc: Location) {
+    fn check_for_move(&mut self, trans: &mut <Self as Analysis<'tcx>>::Domain, loc: Location) {
         let body = self.borrowed_locals.body();
         let mut visitor = MoveVisitor { trans, borrowed_locals: &mut self.borrowed_locals };
         visitor.visit_location(body, loc);
     }
 }
 
-struct MoveVisitor<'a, 'mir, 'tcx, T> {
+struct MoveVisitor<'a, 'mir, 'tcx> {
     borrowed_locals: &'a mut BorrowedLocalsResults<'mir, 'tcx>,
-    trans: &'a mut T,
+    trans: &'a mut BitSet<Local>,
 }
 
-impl<'tcx, T> Visitor<'tcx> for MoveVisitor<'_, '_, 'tcx, T>
-where
-    T: GenKill<Local>,
-{
+impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, 'tcx> {
     fn visit_local(&mut self, local: Local, context: PlaceContext, loc: Location) {
         if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context {
             self.borrowed_locals.seek_before_primary_effect(loc);
-            if !self.borrowed_locals.contains(local) {
+            if !self.borrowed_locals.get().contains(local) {
                 self.trans.kill(local);
             }
         }
diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs
index cd926c07641..b284f0308f9 100644
--- a/compiler/rustc_mir_dataflow/src/lib.rs
+++ b/compiler/rustc_mir_dataflow/src/lib.rs
@@ -18,9 +18,9 @@ pub use self::drop_flag_effects::{
     move_path_children_matching, on_all_children_bits, on_lookup_result_bits,
 };
 pub use self::framework::{
-    Analysis, AnalysisDomain, Backward, Direction, Engine, Forward, GenKill, GenKillAnalysis,
-    JoinSemiLattice, MaybeReachable, Results, ResultsCursor, ResultsVisitable, ResultsVisitor,
-    SwitchIntEdgeEffects, fmt, graphviz, lattice, visit_results,
+    Analysis, Backward, Direction, Engine, Forward, GenKill, JoinSemiLattice, MaybeReachable,
+    Results, ResultsCursor, ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects, fmt, graphviz,
+    lattice, visit_results,
 };
 use self::move_paths::MoveData;
 
diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs
index aa09fe1dd45..faee40faa3f 100644
--- a/compiler/rustc_mir_dataflow/src/value_analysis.rs
+++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs
@@ -51,7 +51,7 @@ use tracing::debug;
 
 use crate::fmt::DebugWithContext;
 use crate::lattice::{HasBottom, HasTop};
-use crate::{Analysis, AnalysisDomain, JoinSemiLattice, SwitchIntEdgeEffects};
+use crate::{Analysis, JoinSemiLattice, SwitchIntEdgeEffects};
 
 pub trait ValueAnalysis<'tcx> {
     /// For each place of interest, the analysis tracks a value of the given type.
@@ -334,7 +334,7 @@ pub trait ValueAnalysis<'tcx> {
 
 pub struct ValueAnalysisWrapper<T>(pub T);
 
-impl<'tcx, T: ValueAnalysis<'tcx>> AnalysisDomain<'tcx> for ValueAnalysisWrapper<T> {
+impl<'tcx, T: ValueAnalysis<'tcx>> Analysis<'tcx> for ValueAnalysisWrapper<T> {
     type Domain = State<T::Value>;
 
     const NAME: &'static str = T::NAME;
@@ -351,12 +351,7 @@ impl<'tcx, T: ValueAnalysis<'tcx>> AnalysisDomain<'tcx> for ValueAnalysisWrapper
             state.flood(PlaceRef { local: arg, projection: &[] }, self.0.map());
         }
     }
-}
 
-impl<'tcx, T> Analysis<'tcx> for ValueAnalysisWrapper<T>
-where
-    T: ValueAnalysis<'tcx>,
-{
     fn apply_statement_effect(
         &mut self,
         state: &mut Self::Domain,
diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs
index c35aec42408..f4ac4d9fee6 100644
--- a/compiler/rustc_mir_transform/src/elaborate_drops.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs
@@ -133,7 +133,7 @@ impl InitializationData<'_, '_> {
     }
 
     fn maybe_live_dead(&self, path: MovePathIndex) -> (bool, bool) {
-        (self.inits.contains(path), self.uninits.contains(path))
+        (self.inits.get().contains(path), self.uninits.get().contains(path))
     }
 }
 
diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs
index a62a892716f..53e53d9d5ba 100644
--- a/compiler/rustc_mir_transform/src/ref_prop.rs
+++ b/compiler/rustc_mir_transform/src/ref_prop.rs
@@ -179,7 +179,7 @@ fn compute_replacement<'tcx>(
         } else {
             // This is a proper dereference. We can only allow it if `target` is live.
             maybe_dead.seek_after_primary_effect(loc);
-            let maybe_dead = maybe_dead.contains(target.local);
+            let maybe_dead = maybe_dead.get().contains(target.local);
             !maybe_dead
         }
     };
diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs
index e353be6a105..25e68f44456 100644
--- a/compiler/rustc_mir_transform/src/validate.rs
+++ b/compiler/rustc_mir_transform/src/validate.rs
@@ -595,6 +595,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         &self,
         pred: impl Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>>,
     ) -> bool {
+        let pred: ty::Predicate<'tcx> = pred.upcast(self.tcx);
+
+        // We sometimes have to use `defining_opaque_types` for predicates
+        // to succeed here and figuring out how exactly that should work
+        // is annoying. It is harmless enough to just not validate anything
+        // in that case. We still check this after analysis as all opaque
+        // types have been revealed at this point.
+        if pred.has_opaque_types() {
+            return true;
+        }
+
         let infcx = self.tcx.infer_ctxt().build();
         let ocx = ObligationCtxt::new(&infcx);
         ocx.register_obligation(Obligation::new(
diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs
index 5bd484d7bb0..9bf7e67417e 100644
--- a/compiler/rustc_monomorphize/src/partitioning.rs
+++ b/compiler/rustc_monomorphize/src/partitioning.rs
@@ -119,6 +119,7 @@ use rustc_middle::util::Providers;
 use rustc_session::CodegenUnits;
 use rustc_session::config::{DumpMonoStatsFormat, SwitchWithOptPath};
 use rustc_span::symbol::Symbol;
+use rustc_target::spec::SymbolVisibility;
 use tracing::debug;
 
 use crate::collector::{self, MonoItemCollectionStrategy, UsageMap};
@@ -904,6 +905,11 @@ fn mono_item_visibility<'tcx>(
 }
 
 fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibility {
+    // Fast-path to avoid expensive query call below
+    if tcx.sess.default_visibility() == SymbolVisibility::Interposable {
+        return Visibility::Default;
+    }
+
     let export_level = if is_generic {
         // Generic functions never have export-level C.
         SymbolExportLevel::Rust
@@ -913,6 +919,7 @@ fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibilit
             _ => SymbolExportLevel::Rust,
         }
     };
+
     match export_level {
         // C-export level items remain at `Default` to allow C code to
         // access and interpose them.
diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs
index ca140500e2c..d67ae2550d9 100644
--- a/compiler/rustc_next_trait_solver/src/lib.rs
+++ b/compiler/rustc_next_trait_solver/src/lib.rs
@@ -5,13 +5,12 @@
 //! So if you got to this crate from the old solver, it's totally normal.
 
 // tidy-alphabetical-start
-#![cfg_attr(not(bootstrap), allow(rustc::usage_of_type_ir_inherent))]
+#![allow(rustc::usage_of_type_ir_inherent)]
 #![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 pub mod canonicalizer;
 pub mod coherence;
 pub mod delegate;
-pub mod relate;
 pub mod resolve;
 pub mod solve;
diff --git a/compiler/rustc_next_trait_solver/src/relate.rs b/compiler/rustc_next_trait_solver/src/relate.rs
deleted file mode 100644
index db819961bbd..00000000000
--- a/compiler/rustc_next_trait_solver/src/relate.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-pub use rustc_type_ir::relate::*;
-
-pub mod combine;
-
-/// Whether aliases should be related structurally or not. Used
-/// to adjust the behavior of generalization and combine.
-///
-/// This should always be `No` unless in a few special-cases when
-/// instantiating canonical responses and in the new solver. Each
-/// such case should have a comment explaining why it is used.
-#[derive(Debug, Copy, Clone)]
-pub enum StructurallyRelateAliases {
-    Yes,
-    No,
-}
diff --git a/compiler/rustc_next_trait_solver/src/relate/combine.rs b/compiler/rustc_next_trait_solver/src/relate/combine.rs
deleted file mode 100644
index 96968327d8e..00000000000
--- a/compiler/rustc_next_trait_solver/src/relate/combine.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-pub use rustc_type_ir::relate::*;
-use rustc_type_ir::solve::Goal;
-use rustc_type_ir::{InferCtxtLike, Interner, Upcast};
-
-use super::StructurallyRelateAliases;
-
-pub trait PredicateEmittingRelation<Infcx, I = <Infcx as InferCtxtLike>::Interner>:
-    TypeRelation<I>
-where
-    Infcx: InferCtxtLike<Interner = I>,
-    I: Interner,
-{
-    fn span(&self) -> I::Span;
-
-    fn param_env(&self) -> I::ParamEnv;
-
-    /// Whether aliases should be related structurally. This is pretty much
-    /// always `No` unless you're equating in some specific locations of the
-    /// new solver. See the comments in these use-cases for more details.
-    fn structurally_relate_aliases(&self) -> StructurallyRelateAliases;
-
-    /// Register obligations that must hold in order for this relation to hold
-    fn register_goals(&mut self, obligations: impl IntoIterator<Item = Goal<I, I::Predicate>>);
-
-    /// Register predicates that must hold in order for this relation to hold.
-    /// This uses the default `param_env` of the obligation.
-    fn register_predicates(
-        &mut self,
-        obligations: impl IntoIterator<Item: Upcast<I, I::Predicate>>,
-    );
-
-    /// Register `AliasRelate` obligation(s) that both types must be related to each other.
-    fn register_alias_relate_predicate(&mut self, a: I::Ty, b: I::Ty);
-}
diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
index cebeef76bfc..c9c0d6391fc 100644
--- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
@@ -6,6 +6,7 @@ use derive_where::derive_where;
 use rustc_type_ir::fold::TypeFoldable;
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::lang_items::TraitSolverLangItem;
+use rustc_type_ir::solve::inspect;
 use rustc_type_ir::visit::TypeVisitableExt as _;
 use rustc_type_ir::{self as ty, Interner, Upcast as _, elaborate};
 use tracing::{debug, instrument};
@@ -288,6 +289,25 @@ where
         let Ok(normalized_self_ty) =
             self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty())
         else {
+            // FIXME: We register a fake candidate when normalization fails so that
+            // we can point at the reason for *why*. I'm tempted to say that this
+            // is the wrong way to do this, though.
+            let result =
+                self.probe(|&result| inspect::ProbeKind::RigidAlias { result }).enter(|this| {
+                    let normalized_ty = this.next_ty_infer();
+                    let alias_relate_goal = Goal::new(
+                        this.cx(),
+                        goal.param_env,
+                        ty::PredicateKind::AliasRelate(
+                            goal.predicate.self_ty().into(),
+                            normalized_ty.into(),
+                            ty::AliasRelationDirection::Equate,
+                        ),
+                    );
+                    this.add_goal(GoalSource::AliasWellFormed, alias_relate_goal);
+                    this.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+                });
+            assert_eq!(result, Err(NoSolution));
             return vec![];
         };
 
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 252a9ed1a2e..fdefec33eeb 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
@@ -14,6 +14,7 @@ use std::iter;
 use rustc_index::IndexVec;
 use rustc_type_ir::fold::TypeFoldable;
 use rustc_type_ir::inherent::*;
+use rustc_type_ir::relate::solver_relating::RelateExt;
 use rustc_type_ir::{self as ty, Canonical, CanonicalVarValues, InferCtxtLike, Interner};
 use tracing::{instrument, trace};
 
@@ -443,7 +444,6 @@ where
     for &arg in &state.value.var_values.var_values.as_slice()
         [orig_values.len()..state.value.var_values.len()]
     {
-        // FIXME: This is so ugly.
         let unconstrained = delegate.fresh_var_for_kind_with_span(arg, span);
         orig_values.push(unconstrained);
     }
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 ffa800348f2..0f8b796d602 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
@@ -4,9 +4,11 @@ use derive_where::derive_where;
 #[cfg(feature = "nightly")]
 use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable};
 use rustc_type_ir::data_structures::{HashMap, HashSet, ensure_sufficient_stack};
+use rustc_type_ir::fast_reject::DeepRejectCtxt;
 use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::relate::Relate;
+use rustc_type_ir::relate::solver_relating::RelateExt;
 use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
 use rustc_type_ir::{self as ty, CanonicalVarValues, InferCtxtLike, Interner};
 use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic};
@@ -17,8 +19,8 @@ use crate::delegate::SolverDelegate;
 use crate::solve::inspect::{self, ProofTreeBuilder};
 use crate::solve::search_graph::SearchGraph;
 use crate::solve::{
-    CanonicalInput, CanonicalResponse, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluationKind,
-    GoalSource, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryResult,
+    CanonicalInput, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluationKind, GoalSource,
+    HasChanged, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryResult,
     SolverMode,
 };
 
@@ -91,7 +93,6 @@ where
 #[derive_where(Clone, Debug, Default; I: Interner)]
 #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
 #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
-// FIXME: This can be made crate-private once `EvalCtxt` also lives in this crate.
 struct NestedGoals<I: Interner> {
     /// These normalizes-to goals are treated specially during the evaluation
     /// loop. In each iteration we take the RHS of the projection, replace it with
@@ -126,11 +127,31 @@ pub enum GenerateProofTree {
 }
 
 pub trait SolverDelegateEvalExt: SolverDelegate {
+    /// Evaluates a goal from **outside** of the trait solver.
+    ///
+    /// Using this while inside of the solver is wrong as it uses a new
+    /// search graph which would break cycle detection.
     fn evaluate_root_goal(
         &self,
         goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
         generate_proof_tree: GenerateProofTree,
-    ) -> (Result<(bool, Certainty), NoSolution>, Option<inspect::GoalEvaluation<Self::Interner>>);
+    ) -> (
+        Result<(HasChanged, Certainty), NoSolution>,
+        Option<inspect::GoalEvaluation<Self::Interner>>,
+    );
+
+    /// Check whether evaluating `goal` with a depth of `root_depth` may
+    /// succeed. This only returns `false` if the goal is guaranteed to
+    /// not hold. In case evaluation overflows and fails with ambiguity this
+    /// returns `true`.
+    ///
+    /// This is only intended to be used as a performance optimization
+    /// in coherence checking.
+    fn root_goal_may_hold_with_depth(
+        &self,
+        root_depth: usize,
+        goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
+    ) -> bool;
 
     // FIXME: This is only exposed because we need to use it in `analyse.rs`
     // which is not yet uplifted. Once that's done, we should remove this.
@@ -139,7 +160,7 @@ pub trait SolverDelegateEvalExt: SolverDelegate {
         goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
         generate_proof_tree: GenerateProofTree,
     ) -> (
-        Result<(NestedNormalizationGoals<Self::Interner>, bool, Certainty), NoSolution>,
+        Result<(NestedNormalizationGoals<Self::Interner>, HasChanged, Certainty), NoSolution>,
         Option<inspect::GoalEvaluation<Self::Interner>>,
     );
 }
@@ -149,31 +170,41 @@ where
     D: SolverDelegate<Interner = I>,
     I: Interner,
 {
-    /// Evaluates a goal from **outside** of the trait solver.
-    ///
-    /// Using this while inside of the solver is wrong as it uses a new
-    /// search graph which would break cycle detection.
     #[instrument(level = "debug", skip(self))]
     fn evaluate_root_goal(
         &self,
         goal: Goal<I, I::Predicate>,
         generate_proof_tree: GenerateProofTree,
-    ) -> (Result<(bool, Certainty), NoSolution>, Option<inspect::GoalEvaluation<I>>) {
-        EvalCtxt::enter_root(self, generate_proof_tree, |ecx| {
+    ) -> (Result<(HasChanged, Certainty), NoSolution>, Option<inspect::GoalEvaluation<I>>) {
+        EvalCtxt::enter_root(self, self.cx().recursion_limit(), generate_proof_tree, |ecx| {
             ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal)
         })
     }
 
+    fn root_goal_may_hold_with_depth(
+        &self,
+        root_depth: usize,
+        goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
+    ) -> bool {
+        self.probe(|| {
+            EvalCtxt::enter_root(self, root_depth, GenerateProofTree::No, |ecx| {
+                ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal)
+            })
+            .0
+        })
+        .is_ok()
+    }
+
     #[instrument(level = "debug", skip(self))]
     fn evaluate_root_goal_raw(
         &self,
         goal: Goal<I, I::Predicate>,
         generate_proof_tree: GenerateProofTree,
     ) -> (
-        Result<(NestedNormalizationGoals<I>, bool, Certainty), NoSolution>,
+        Result<(NestedNormalizationGoals<I>, HasChanged, Certainty), NoSolution>,
         Option<inspect::GoalEvaluation<I>>,
     ) {
-        EvalCtxt::enter_root(self, generate_proof_tree, |ecx| {
+        EvalCtxt::enter_root(self, self.cx().recursion_limit(), generate_proof_tree, |ecx| {
             ecx.evaluate_goal_raw(GoalEvaluationKind::Root, GoalSource::Misc, goal)
         })
     }
@@ -197,10 +228,11 @@ where
     /// over using this manually (such as [`SolverDelegateEvalExt::evaluate_root_goal`]).
     pub(super) fn enter_root<R>(
         delegate: &D,
+        root_depth: usize,
         generate_proof_tree: GenerateProofTree,
         f: impl FnOnce(&mut EvalCtxt<'_, D>) -> R,
     ) -> (R, Option<inspect::GoalEvaluation<I>>) {
-        let mut search_graph = SearchGraph::new(delegate.solver_mode());
+        let mut search_graph = SearchGraph::new(delegate.solver_mode(), root_depth);
 
         let mut ecx = EvalCtxt {
             delegate,
@@ -339,7 +371,7 @@ where
         goal_evaluation_kind: GoalEvaluationKind,
         source: GoalSource,
         goal: Goal<I, I::Predicate>,
-    ) -> Result<(bool, Certainty), NoSolution> {
+    ) -> Result<(HasChanged, Certainty), NoSolution> {
         let (normalization_nested_goals, has_changed, certainty) =
             self.evaluate_goal_raw(goal_evaluation_kind, source, goal)?;
         assert!(normalization_nested_goals.is_empty());
@@ -360,7 +392,7 @@ where
         goal_evaluation_kind: GoalEvaluationKind,
         _source: GoalSource,
         goal: Goal<I, I::Predicate>,
-    ) -> Result<(NestedNormalizationGoals<I>, bool, Certainty), NoSolution> {
+    ) -> Result<(NestedNormalizationGoals<I>, HasChanged, Certainty), NoSolution> {
         let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
         let mut goal_evaluation =
             self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind);
@@ -378,12 +410,18 @@ where
             Ok(response) => response,
         };
 
-        let has_changed = !response.value.var_values.is_identity_modulo_regions()
-            || !response.value.external_constraints.opaque_types.is_empty();
+        let has_changed = if !response.value.var_values.is_identity_modulo_regions()
+            || !response.value.external_constraints.opaque_types.is_empty()
+        {
+            HasChanged::Yes
+        } else {
+            HasChanged::No
+        };
 
         let (normalization_nested_goals, certainty) =
             self.instantiate_and_apply_query_response(goal.param_env, orig_values, response);
         self.inspect.goal_evaluation(goal_evaluation);
+
         // FIXME: We previously had an assert here that checked that recomputing
         // a goal after applying its constraints did not change its response.
         //
@@ -552,7 +590,7 @@ where
         for (source, goal) in goals.goals {
             let (has_changed, certainty) =
                 self.evaluate_goal(GoalEvaluationKind::Nested, source, goal)?;
-            if has_changed {
+            if has_changed == HasChanged::Yes {
                 unchanged_certainty = None;
             }
 
@@ -835,7 +873,7 @@ where
         lhs: T,
         rhs: T,
     ) -> Result<Vec<Goal<I, I::Predicate>>, NoSolution> {
-        self.delegate.relate(param_env, lhs, ty::Variance::Invariant, rhs)
+        Ok(self.delegate.relate(param_env, lhs, ty::Variance::Invariant, rhs)?)
     }
 
     pub(super) fn instantiate_binder_with_infer<T: TypeFoldable<I> + Copy>(
@@ -945,45 +983,27 @@ where
             hidden_ty,
             &mut goals,
         );
-        self.add_goals(GoalSource::Misc, goals);
+        self.add_goals(GoalSource::AliasWellFormed, goals);
     }
 
     // Do something for each opaque/hidden pair defined with `def_id` in the
     // current inference context.
-    pub(super) fn unify_existing_opaque_tys(
+    pub(super) fn probe_existing_opaque_ty(
         &mut self,
-        param_env: I::ParamEnv,
         key: ty::OpaqueTypeKey<I>,
-        ty: I::Ty,
-    ) -> Vec<CanonicalResponse<I>> {
-        // FIXME: Super inefficient to be cloning this...
-        let opaques = self.delegate.clone_opaque_types_for_query_response();
-
-        let mut values = vec![];
-        for (candidate_key, candidate_ty) in opaques {
-            if candidate_key.def_id != key.def_id {
-                continue;
-            }
-            values.extend(
-                self.probe(|result| inspect::ProbeKind::OpaqueTypeStorageLookup {
-                    result: *result,
-                })
-                .enter(|ecx| {
-                    for (a, b) in std::iter::zip(candidate_key.args.iter(), key.args.iter()) {
-                        ecx.eq(param_env, a, b)?;
-                    }
-                    ecx.eq(param_env, candidate_ty, ty)?;
-                    ecx.add_item_bounds_for_hidden_type(
-                        candidate_key.def_id.into(),
-                        candidate_key.args,
-                        param_env,
-                        candidate_ty,
-                    );
-                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-                }),
+    ) -> Option<(ty::OpaqueTypeKey<I>, I::Ty)> {
+        let mut matching =
+            self.delegate.clone_opaque_types_for_query_response().into_iter().filter(
+                |(candidate_key, _)| {
+                    candidate_key.def_id == key.def_id
+                        && DeepRejectCtxt::relate_rigid_rigid(self.cx())
+                            .args_may_unify(candidate_key.args, key.args)
+                },
             );
-        }
-        values
+        let first = matching.next();
+        let second = matching.next();
+        assert_eq!(second, None);
+        first
     }
 
     // Try to evaluate a const, or return `None` if the const is too generic.
diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs
index 309ab7f28d1..8fe39bb4ee1 100644
--- a/compiler/rustc_next_trait_solver/src/solve/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs
@@ -10,9 +10,6 @@
 //!
 //! For a high-level overview of how this solver works, check out the relevant
 //! section of the rustc-dev-guide.
-//!
-//! FIXME(@lcnr): Write that section. If you read this before then ask me
-//! about it on zulip.
 
 mod alias_relate;
 mod assembly;
@@ -48,6 +45,14 @@ enum GoalEvaluationKind {
     Nested,
 }
 
+/// Whether evaluating this goal ended up changing the
+/// inference state.
+#[derive(PartialEq, Eq, Debug, Hash, Clone, Copy)]
+pub enum HasChanged {
+    Yes,
+    No,
+}
+
 // FIXME(trait-system-refactor-initiative#117): we don't detect whether a response
 // ended up pulling down any universes.
 fn has_no_inference_or_external_constraints<I: Interner>(
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
index fc9c634942b..4d8b193ee49 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
@@ -15,7 +15,7 @@ use crate::solve::assembly::{self, Candidate};
 use crate::solve::inspect::ProbeKind;
 use crate::solve::{
     BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
-    NoSolution, QueryResult,
+    NoSolution, QueryResult, Reveal,
 };
 
 impl<D, I> EvalCtxt<'_, D>
@@ -37,10 +37,61 @@ where
         match normalize_result {
             Ok(res) => Ok(res),
             Err(NoSolution) => {
-                let Goal { param_env, predicate: NormalizesTo { alias, term } } = goal;
-                self.relate_rigid_alias_non_alias(param_env, alias, ty::Invariant, term)?;
-                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                self.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| {
+                    let Goal { param_env, predicate: NormalizesTo { alias, term } } = goal;
+                    this.add_rigid_constraints(param_env, alias)?;
+                    this.relate_rigid_alias_non_alias(param_env, alias, ty::Invariant, term)?;
+                    this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                })
+            }
+        }
+    }
+
+    /// Register any obligations that are used to validate that an alias should be
+    /// treated as rigid.
+    ///
+    /// An alias may be considered rigid if it fails normalization, but we also don't
+    /// want to consider aliases that are not well-formed to be rigid simply because
+    /// they fail normalization.
+    ///
+    /// For example, some `<T as Trait>::Assoc` where `T: Trait` does not hold, or an
+    /// opaque type whose hidden type doesn't actually satisfy the opaque item bounds.
+    fn add_rigid_constraints(
+        &mut self,
+        param_env: I::ParamEnv,
+        rigid_alias: ty::AliasTerm<I>,
+    ) -> Result<(), NoSolution> {
+        let cx = self.cx();
+        match rigid_alias.kind(cx) {
+            // Projections are rigid only if their trait ref holds,
+            // and the GAT where-clauses hold.
+            ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => {
+                let trait_ref = rigid_alias.trait_ref(cx);
+                self.add_goal(GoalSource::AliasWellFormed, Goal::new(cx, param_env, trait_ref));
+                Ok(())
+            }
+            ty::AliasTermKind::OpaqueTy => {
+                match param_env.reveal() {
+                    // In user-facing mode, paques are only rigid if we may not define it.
+                    Reveal::UserFacing => {
+                        if rigid_alias
+                            .def_id
+                            .as_local()
+                            .is_some_and(|def_id| self.can_define_opaque_ty(def_id))
+                        {
+                            Err(NoSolution)
+                        } else {
+                            Ok(())
+                        }
+                    }
+                    // Opaques are never rigid in reveal-all mode.
+                    Reveal::All => Err(NoSolution),
+                }
             }
+            // FIXME(generic_const_exprs): we would need to support generic consts here
+            ty::AliasTermKind::UnevaluatedConst => Err(NoSolution),
+            // Inherent and weak types are never rigid. This type must not be well-formed.
+            ty::AliasTermKind::WeakTy | ty::AliasTermKind::InherentTy => Err(NoSolution),
         }
     }
 
@@ -124,6 +175,7 @@ where
                     ecx.instantiate_normalizes_to_term(goal, assumption_projection_pred.term);
 
                     // Add GAT where clauses from the trait's definition
+                    // FIXME: We don't need these, since these are the type's own WF obligations.
                     ecx.add_goals(
                         GoalSource::Misc,
                         cx.own_predicates_of(goal.predicate.def_id())
@@ -179,7 +231,8 @@ where
                 .map(|pred| goal.with(cx, pred));
             ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds);
 
-            // Add GAT where clauses from the trait's definition
+            // Add GAT where clauses from the trait's definition.
+            // FIXME: We don't need these, since these are the type's own WF obligations.
             ecx.add_goals(
                 GoalSource::Misc,
                 cx.own_predicates_of(goal.predicate.def_id())
@@ -899,7 +952,7 @@ where
                 for ty in types.iter() {
                     // We can't find the intersection if the types used are generic.
                     //
-                    // FIXME(effects) do we want to look at where clauses to get some
+                    // FIXME(effects): do we want to look at where clauses to get some
                     // clue for the case where generic types are being used?
                     let Some(kind) = ty::EffectKind::try_from_ty(cx, ty) else {
                         return Err(NoSolution);
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 6a0703c5313..f8d51f304f3 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
@@ -7,7 +7,9 @@ use rustc_type_ir::inherent::*;
 use rustc_type_ir::{self as ty, Interner};
 
 use crate::delegate::SolverDelegate;
-use crate::solve::{Certainty, EvalCtxt, Goal, NoSolution, QueryResult, Reveal, SolverMode};
+use crate::solve::{
+    Certainty, EvalCtxt, Goal, NoSolution, QueryResult, Reveal, SolverMode, inspect,
+};
 
 impl<D, I> EvalCtxt<'_, D>
 where
@@ -52,14 +54,28 @@ where
                 //
                 // If that fails, we insert `expected` as a new hidden type instead of
                 // eagerly emitting an error.
-                let matches =
-                    self.unify_existing_opaque_tys(goal.param_env, opaque_type_key, expected);
-                if !matches.is_empty() {
-                    if let Some(response) = self.try_merge_responses(&matches) {
-                        return Ok(response);
-                    } else {
-                        return self.flounder(&matches);
-                    }
+                let existing = self.probe_existing_opaque_ty(opaque_type_key);
+                if let Some((candidate_key, candidate_ty)) = existing {
+                    return self
+                        .probe(|result| inspect::ProbeKind::OpaqueTypeStorageLookup {
+                            result: *result,
+                        })
+                        .enter(|ecx| {
+                            for (a, b) in std::iter::zip(
+                                candidate_key.args.iter(),
+                                opaque_type_key.args.iter(),
+                            ) {
+                                ecx.eq(goal.param_env, a, b)?;
+                            }
+                            ecx.eq(goal.param_env, candidate_ty, expected)?;
+                            ecx.add_item_bounds_for_hidden_type(
+                                candidate_key.def_id.into(),
+                                candidate_key.args,
+                                goal.param_env,
+                                candidate_ty,
+                            );
+                            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                        });
                 }
 
                 // Otherwise, define a new opaque type
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 e47cc03f5ad..0e3f179b0c8 100644
--- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs
@@ -40,9 +40,6 @@ where
     }
 
     const DIVIDE_AVAILABLE_DEPTH_ON_OVERFLOW: usize = 4;
-    fn recursion_limit(cx: I) -> usize {
-        cx.recursion_limit()
-    }
 
     fn initial_provisional_result(
         cx: I,
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 2074bdec485..5828b2ecf34 100644
--- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -108,7 +108,6 @@ where
         ecx: &mut EvalCtxt<'_, D>,
         _guar: I::ErrorGuaranteed,
     ) -> Result<Candidate<I>, NoSolution> {
-        // FIXME: don't need to enter a probe here.
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
             .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
     }
@@ -463,7 +462,6 @@ where
         // Async coroutine unconditionally implement `Future`
         // Technically, we need to check that the future output type is Sized,
         // but that's already proven by the coroutine being WF.
-        // FIXME: use `consider_implied`
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
             .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
     }
@@ -489,7 +487,6 @@ where
         // Gen coroutines unconditionally implement `Iterator`
         // Technically, we need to check that the iterator output type is Sized,
         // but that's already proven by the coroutines being WF.
-        // FIXME: use `consider_implied`
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
             .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
     }
@@ -512,8 +509,7 @@ where
             return Err(NoSolution);
         }
 
-        // Gen coroutines unconditionally implement `FusedIterator`
-        // FIXME: use `consider_implied`
+        // Gen coroutines unconditionally implement `FusedIterator`.
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
             .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
     }
@@ -539,7 +535,6 @@ where
         // Gen coroutines unconditionally implement `Iterator`
         // Technically, we need to check that the iterator output type is Sized,
         // but that's already proven by the coroutines being WF.
-        // FIXME: use `consider_implied`
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
             .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
     }
@@ -610,7 +605,7 @@ where
             return Err(NoSolution);
         }
 
-        // FIXME(-Znext-solver): Implement this when we get const working in the new solver
+        // FIXME(effects): Implement this when we get const working in the new solver
 
         // `Destruct` is automatically implemented for every type in
         // non-const environments.
@@ -631,8 +626,6 @@ where
             return Err(NoSolution);
         }
 
-        // FIXME: This actually should destructure the `Result` we get from transmutability and
-        // register candidates. We probably need to register >1 since we may have an OR of ANDs.
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
             let certainty = ecx.is_transmutable(
                 goal.param_env,
diff --git a/compiler/rustc_parse/Cargo.toml b/compiler/rustc_parse/Cargo.toml
index c59ae48a07d..2360914a0ab 100644
--- a/compiler/rustc_parse/Cargo.toml
+++ b/compiler/rustc_parse/Cargo.toml
@@ -20,7 +20,7 @@ rustc_span = { path = "../rustc_span" }
 thin-vec = "0.2.12"
 tracing = "0.1"
 unicode-normalization = "0.1.11"
-unicode-width = "0.1.4"
+unicode-width = "0.2.0"
 # tidy-alphabetical-end
 
 [dev-dependencies]
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 948199fd55c..1af7fb9b86a 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -422,6 +422,9 @@ parse_invalid_meta_item = expected unsuffixed literal, found `{$token}`
 
 parse_invalid_offset_of = offset_of expects dot-separated field and variant names
 
+parse_invalid_path_sep_in_fn_definition = invalid path separator in function definition
+    .suggestion = remove invalid path separator
+
 parse_invalid_unicode_escape = invalid unicode character escape
     .label = invalid escape
     .help = unicode escape must {$surrogate ->
@@ -706,6 +709,10 @@ parse_require_colon_after_labeled_expression = labeled expression must be follow
     .label = the label
     .suggestion = add `:` after the label
 
+parse_reserved_string = invalid string literal
+    .note = unprefixed guarded string literals are reserved for future use since Rust 2024
+    .suggestion_whitespace = consider inserting whitespace here
+
 parse_return_types_use_thin_arrow = return types are denoted using `->`
     .suggestion = use `->` instead
 
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index dade3912751..fdd500e90f8 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -1756,6 +1756,14 @@ pub(crate) struct MissingFnParams {
 }
 
 #[derive(Diagnostic)]
+#[diag(parse_invalid_path_sep_in_fn_definition)]
+pub(crate) struct InvalidPathSepInFnDefinition {
+    #[primary_span]
+    #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(parse_missing_trait_in_trait_impl)]
 pub(crate) struct MissingTraitInTraitImpl {
     #[primary_span]
@@ -2111,6 +2119,24 @@ pub(crate) enum UnknownPrefixSugg {
 }
 
 #[derive(Diagnostic)]
+#[diag(parse_reserved_string)]
+#[note]
+pub(crate) struct ReservedString {
+    #[primary_span]
+    pub span: Span,
+    #[subdiagnostic]
+    pub sugg: Option<GuardedStringSugg>,
+}
+#[derive(Subdiagnostic)]
+#[suggestion(
+    parse_suggestion_whitespace,
+    code = " ",
+    applicability = "maybe-incorrect",
+    style = "verbose"
+)]
+pub(crate) struct GuardedStringSugg(#[primary_span] pub Span);
+
+#[derive(Diagnostic)]
 #[diag(parse_too_many_hashes)]
 pub(crate) struct TooManyHashes {
     #[primary_span]
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index 3e46fc93fa4..d627ef3d2cb 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -10,7 +10,8 @@ use rustc_lexer::unescape::{self, EscapeError, Mode};
 use rustc_lexer::{Base, Cursor, DocStyle, LiteralKind, RawStrError};
 use rustc_session::lint::BuiltinLintDiag;
 use rustc_session::lint::builtin::{
-    RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
+    RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX,
+    TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
 };
 use rustc_session::parse::ParseSess;
 use rustc_span::symbol::Symbol;
@@ -251,6 +252,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
                     let prefix_span = self.mk_sp(start, lit_start);
                     return (Token::new(self.ident(start), prefix_span), preceded_by_whitespace);
                 }
+                rustc_lexer::TokenKind::GuardedStrPrefix => self.maybe_report_guarded_str(start, str_before),
                 rustc_lexer::TokenKind::Literal { kind, suffix_start } => {
                     let suffix_start = start + BytePos(suffix_start);
                     let (kind, symbol) = self.cook_lexer_literal(start, suffix_start, kind);
@@ -781,6 +783,86 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
         }
     }
 
+    /// Detect guarded string literal syntax
+    ///
+    /// RFC 3598 reserved this syntax for future use. As of Rust 2024,
+    /// using this syntax produces an error. In earlier editions, however, it
+    /// only results in an (allowed by default) lint, and is treated as
+    /// separate tokens.
+    fn maybe_report_guarded_str(&mut self, start: BytePos, str_before: &'src str) -> TokenKind {
+        let span = self.mk_sp(start, self.pos);
+        let edition2024 = span.edition().at_least_rust_2024();
+
+        let space_pos = start + BytePos(1);
+        let space_span = self.mk_sp(space_pos, space_pos);
+
+        let mut cursor = Cursor::new(str_before);
+
+        let (span, unterminated) = match cursor.guarded_double_quoted_string() {
+            Some(rustc_lexer::GuardedStr { n_hashes, terminated, token_len }) => {
+                let end = start + BytePos(token_len);
+                let span = self.mk_sp(start, end);
+                let str_start = start + BytePos(n_hashes);
+
+                if edition2024 {
+                    self.cursor = cursor;
+                    self.pos = end;
+                }
+
+                let unterminated = if terminated { None } else { Some(str_start) };
+
+                (span, unterminated)
+            }
+            _ => {
+                // We should only get here in the `##+` case.
+                debug_assert_eq!(self.str_from_to(start, start + BytePos(2)), "##");
+
+                (span, None)
+            }
+        };
+        if edition2024 {
+            if let Some(str_start) = unterminated {
+                // Only a fatal error if string is unterminated.
+                self.dcx()
+                    .struct_span_fatal(
+                        self.mk_sp(str_start, self.pos),
+                        "unterminated double quote string",
+                    )
+                    .with_code(E0765)
+                    .emit()
+            }
+
+            let sugg = if span.from_expansion() {
+                None
+            } else {
+                Some(errors::GuardedStringSugg(space_span))
+            };
+
+            // In Edition 2024 and later, emit a hard error.
+            let err = self.dcx().emit_err(errors::ReservedString { span, sugg });
+
+            token::Literal(token::Lit {
+                kind: token::Err(err),
+                symbol: self.symbol_from_to(start, self.pos),
+                suffix: None,
+            })
+        } else {
+            // Before Rust 2024, only emit a lint for migration.
+            self.psess.buffer_lint(
+                RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX,
+                span,
+                ast::CRATE_NODE_ID,
+                BuiltinLintDiag::ReservedString(space_span),
+            );
+
+            // For backwards compatibility, roll back to after just the first `#`
+            // and return the `Pound` token.
+            self.pos = start + BytePos(1);
+            self.cursor = Cursor::new(&str_before[1..]);
+            token::Pound
+        }
+    }
+
     fn report_too_many_hashes(&self, start: BytePos, num: u32) -> ! {
         self.dcx().emit_fatal(errors::TooManyHashes { span: self.mk_sp(start, self.pos), num });
     }
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index f32307f6ed4..a9384501547 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -2113,7 +2113,7 @@ impl<'a> Parser<'a> {
             && let Some(poly) = bounds
                 .iter()
                 .filter_map(|bound| match bound {
-                    ast::GenericBound::Trait(poly, _) => Some(poly),
+                    ast::GenericBound::Trait(poly) => Some(poly),
                     _ => None,
                 })
                 .last()
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index b9256daa725..5aebe716b0a 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -269,6 +269,13 @@ impl<'a> Parser<'a> {
     ///                  | ( < lifetimes , typaramseq ( , )? > )
     /// where   typaramseq = ( typaram ) | ( typaram , typaramseq )
     pub(super) fn parse_generics(&mut self) -> PResult<'a, ast::Generics> {
+        // invalid path separator `::` in function definition
+        // for example `fn invalid_path_separator::<T>() {}`
+        if self.eat_noexpect(&token::PathSep) {
+            self.dcx()
+                .emit_err(errors::InvalidPathSepInFnDefinition { span: self.prev_token.span });
+        }
+
         let span_lo = self.token.span;
         let (params, span) = if self.eat_lt() {
             let params = self.parse_generic_params()?;
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 9fc82d84225..36733726564 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -1984,7 +1984,7 @@ impl<'a> Parser<'a> {
             }
         }
         self.expect_field_ty_separator()?;
-        let ty = self.parse_ty_for_field_def()?;
+        let ty = self.parse_ty()?;
         if self.token == token::Colon && self.look_ahead(1, |t| *t != token::Colon) {
             self.dcx().emit_err(errors::SingleColonStructType { span: self.token.span });
         }
@@ -2009,9 +2009,7 @@ impl<'a> Parser<'a> {
     /// for better diagnostics and suggestions.
     fn parse_field_ident(&mut self, adt_ty: &str, lo: Span) -> PResult<'a, Ident> {
         let (ident, is_raw) = self.ident_or_err(true)?;
-        if ident.name == kw::Underscore {
-            self.psess.gated_spans.gate(sym::unnamed_fields, lo);
-        } else if matches!(is_raw, IdentIsRaw::No) && ident.is_reserved() {
+        if matches!(is_raw, IdentIsRaw::No) && ident.is_reserved() {
             let snapshot = self.create_snapshot_for_diagnostic();
             let err = if self.check_fn_front_matter(false, Case::Sensitive) {
                 let inherited_vis =
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 162ff3b94de..2f19a9b6b20 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -948,8 +948,8 @@ impl<'a> Parser<'a> {
             {
                 return Ok((false, seg.ident, seg.args.as_deref().cloned()));
             } else if let ast::TyKind::TraitObject(bounds, ast::TraitObjectSyntax::None) = &ty.kind
-                && let [ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifiers::NONE)] =
-                    bounds.as_slice()
+                && let [ast::GenericBound::Trait(trait_ref)] = bounds.as_slice()
+                && trait_ref.modifiers == ast::TraitBoundModifiers::NONE
                 && let [seg] = trait_ref.trait_ref.path.segments.as_slice()
             {
                 return Ok((true, seg.ident, seg.args.as_deref().cloned()));
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index a8ed8b5df9c..c561ea3823d 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -4,7 +4,8 @@ use rustc_ast::util::case::Case;
 use rustc_ast::{
     self as ast, BareFnTy, BoundAsyncness, BoundConstness, BoundPolarity, DUMMY_NODE_ID, FnRetTy,
     GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability,
-    PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty, TyKind,
+    Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty,
+    TyKind,
 };
 use rustc_errors::{Applicability, PResult};
 use rustc_span::symbol::{Ident, kw, sym};
@@ -128,17 +129,6 @@ impl<'a> Parser<'a> {
         )
     }
 
-    /// Parse a type suitable for a field definition.
-    /// The difference from `parse_ty` is that this version
-    /// allows anonymous structs and unions.
-    pub(super) fn parse_ty_for_field_def(&mut self) -> PResult<'a, P<Ty>> {
-        if self.can_begin_anon_struct_or_union() {
-            self.parse_anon_struct_or_union()
-        } else {
-            self.parse_ty()
-        }
-    }
-
     /// Parse a type suitable for a function or function pointer parameter.
     /// The difference from `parse_ty` is that this version allows `...`
     /// (`CVarArgs`) at the top level of the type.
@@ -382,37 +372,6 @@ impl<'a> Parser<'a> {
         if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) }
     }
 
-    /// Parse an anonymous struct or union (only for field definitions):
-    /// ```ignore (feature-not-ready)
-    /// #[repr(C)]
-    /// struct Foo {
-    ///     _: struct { // anonymous struct
-    ///         x: u32,
-    ///         y: f64,
-    ///     }
-    ///     _: union { // anonymous union
-    ///         z: u32,
-    ///         w: f64,
-    ///     }
-    /// }
-    /// ```
-    fn parse_anon_struct_or_union(&mut self) -> PResult<'a, P<Ty>> {
-        assert!(self.token.is_keyword(kw::Union) || self.token.is_keyword(kw::Struct));
-        let is_union = self.token.is_keyword(kw::Union);
-
-        let lo = self.token.span;
-        self.bump();
-
-        let (fields, _recovered) =
-            self.parse_record_struct_body(if is_union { "union" } else { "struct" }, lo, false)?;
-        let span = lo.to(self.prev_token.span);
-        self.psess.gated_spans.gate(sym::unnamed_fields, span);
-        let id = ast::DUMMY_NODE_ID;
-        let kind =
-            if is_union { TyKind::AnonUnion(id, fields) } else { TyKind::AnonStruct(id, fields) };
-        Ok(self.mk_ty(span, kind))
-    }
-
     /// Parses either:
     /// - `(TYPE)`, a parenthesized type.
     /// - `(TYPE,)`, a tuple with a single field of type TYPE.
@@ -461,8 +420,13 @@ impl<'a> Parser<'a> {
         lo: Span,
         parse_plus: bool,
     ) -> PResult<'a, TyKind> {
-        let poly_trait_ref = PolyTraitRef::new(generic_params, path, lo.to(self.prev_token.span));
-        let bounds = vec![GenericBound::Trait(poly_trait_ref, TraitBoundModifiers::NONE)];
+        let poly_trait_ref = PolyTraitRef::new(
+            generic_params,
+            path,
+            TraitBoundModifiers::NONE,
+            lo.to(self.prev_token.span),
+        );
+        let bounds = vec![GenericBound::Trait(poly_trait_ref)];
         self.parse_remaining_bounds(bounds, parse_plus)
     }
 
@@ -529,7 +493,10 @@ impl<'a> Parser<'a> {
     fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> {
         let and_span = self.prev_token.span;
         let mut opt_lifetime = self.check_lifetime().then(|| self.expect_lifetime());
-        let mut mutbl = self.parse_mutability();
+        let (pinned, mut mutbl) = match self.parse_pin_and_mut() {
+            Some(pin_mut) => pin_mut,
+            None => (Pinnedness::Not, self.parse_mutability()),
+        };
         if self.token.is_lifetime() && mutbl == Mutability::Mut && opt_lifetime.is_none() {
             // A lifetime is invalid here: it would be part of a bare trait bound, which requires
             // it to be followed by a plus, but we disallow plus in the pointee type.
@@ -565,7 +532,35 @@ impl<'a> Parser<'a> {
             self.bump_with((dyn_tok, dyn_tok_sp));
         }
         let ty = self.parse_ty_no_plus()?;
-        Ok(TyKind::Ref(opt_lifetime, MutTy { ty, mutbl }))
+        Ok(match pinned {
+            Pinnedness::Not => TyKind::Ref(opt_lifetime, MutTy { ty, mutbl }),
+            Pinnedness::Pinned => TyKind::PinnedRef(opt_lifetime, MutTy { ty, mutbl }),
+        })
+    }
+
+    /// Parses `pin` and `mut` annotations on references.
+    ///
+    /// It must be either `pin const` or `pin mut`.
+    pub(crate) fn parse_pin_and_mut(&mut self) -> Option<(Pinnedness, Mutability)> {
+        if self.token.is_ident_named(sym::pin) {
+            let result = self.look_ahead(1, |token| {
+                if token.is_keyword(kw::Const) {
+                    Some((Pinnedness::Pinned, Mutability::Not))
+                } else if token.is_keyword(kw::Mut) {
+                    Some((Pinnedness::Pinned, Mutability::Mut))
+                } else {
+                    None
+                }
+            });
+            if result.is_some() {
+                self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span);
+                self.bump();
+                self.bump();
+            }
+            result
+        } else {
+            None
+        }
     }
 
     // Parses the `typeof(EXPR)`.
@@ -813,11 +808,6 @@ impl<'a> Parser<'a> {
         Ok(bounds)
     }
 
-    pub(super) fn can_begin_anon_struct_or_union(&mut self) -> bool {
-        (self.token.is_keyword(kw::Struct) || self.token.is_keyword(kw::Union))
-            && self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace))
-    }
-
     /// Can the current token begin a bound?
     fn can_begin_bound(&mut self) -> bool {
         self.check_path()
@@ -1132,8 +1122,9 @@ impl<'a> Parser<'a> {
             }
         }
 
-        let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_token.span));
-        Ok(GenericBound::Trait(poly_trait, modifiers))
+        let poly_trait =
+            PolyTraitRef::new(lifetime_defs, path, modifiers, lo.to(self.prev_token.span));
+        Ok(GenericBound::Trait(poly_trait))
     }
 
     // recovers a `Fn(..)` parenthesized-style path from `fn(..)`
diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs
index e7ef4385baf..1716f417969 100644
--- a/compiler/rustc_parse_format/src/lib.rs
+++ b/compiler/rustc_parse_format/src/lib.rs
@@ -473,19 +473,19 @@ impl<'a> Parser<'a> {
             }
 
             pos = peek_pos;
-            description = format!("expected `'}}'`, found `{maybe:?}`");
+            description = format!("expected `}}`, found `{}`", maybe.escape_debug());
         } else {
-            description = "expected `'}'` but string was terminated".to_owned();
+            description = "expected `}` but string was terminated".to_owned();
             // point at closing `"`
             pos = self.input.len() - if self.append_newline { 1 } else { 0 };
         }
 
         let pos = self.to_span_index(pos);
 
-        let label = "expected `'}'`".to_owned();
+        let label = "expected `}`".to_owned();
         let (note, secondary_label) = if arg.format.fill == Some('}') {
             (
-                Some("the character `'}'` is interpreted as a fill character because of the `:` that precedes it".to_owned()),
+                Some("the character `}` is interpreted as a fill character because of the `:` that precedes it".to_owned()),
                 arg.format.fill_span.map(|sp| ("this is not interpreted as a formatting closing brace".to_owned(), sp)),
             )
         } else {
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index be76d6cef2b..e5a14f6a156 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -49,6 +49,10 @@ passes_attr_crate_level =
 passes_attr_only_in_functions =
     `{$attr}` attribute can only be used on functions
 
+passes_autodiff_attr =
+    `#[autodiff]` should be applied to a function
+    .label = not a function
+
 passes_both_ffi_const_and_pure =
     `#[ffi_const]` function cannot be `#[ffi_pure]`
 
@@ -739,6 +743,12 @@ passes_unrecognized_repr_hint =
     unrecognized representation hint
     .help = valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`
 
+passes_unstable_attr_for_already_stable_feature =
+    can't mark as unstable using an already stable feature
+    .label = this feature is already stable
+    .item = the stability attribute annotates this item
+    .help = consider removing the attribute
+
 passes_unused =
     unused attribute
     .suggestion = remove this attribute
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 44a62383e6e..4516ea94cad 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -243,6 +243,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                     self.check_generic_attr(hir_id, attr, target, Target::Fn);
                     self.check_proc_macro(hir_id, target, ProcMacroKind::Derive)
                 }
+                [sym::autodiff, ..] => {
+                    self.check_autodiff(hir_id, attr, span, target)
+                }
                 [sym::coroutine, ..] => {
                     self.check_coroutine(attr, target);
                 }
@@ -2299,7 +2302,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 })),
                 terr,
                 false,
-                false,
             );
             diag.emit();
             self.abort.set(true);
@@ -2345,6 +2347,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             self.dcx().emit_err(errors::RustcPubTransparent { span, attr_span });
         }
     }
+
+    /// Checks if `#[autodiff]` is applied to an item other than a function item.
+    fn check_autodiff(&self, _hir_id: HirId, _attr: &Attribute, span: Span, target: Target) {
+        debug!("check_autodiff");
+        match target {
+            Target::Fn => {}
+            _ => {
+                self.dcx().emit_err(errors::AutoDiffAttr { attr_span: span });
+                self.abort.set(true);
+            }
+        }
+    }
 }
 
 impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 4f00c90fa3b..6dc3dfba58f 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -20,6 +20,14 @@ use crate::lang_items::Duplicate;
 #[diag(passes_incorrect_do_not_recommend_location)]
 pub(crate) struct IncorrectDoNotRecommendLocation;
 
+#[derive(Diagnostic)]
+#[diag(passes_autodiff_attr)]
+pub(crate) struct AutoDiffAttr {
+    #[primary_span]
+    #[label]
+    pub attr_span: Span,
+}
+
 #[derive(LintDiagnostic)]
 #[diag(passes_outer_crate_level_attr)]
 pub(crate) struct OuterCrateLevelAttr;
@@ -1481,6 +1489,17 @@ pub(crate) struct CannotStabilizeDeprecated {
 }
 
 #[derive(Diagnostic)]
+#[diag(passes_unstable_attr_for_already_stable_feature)]
+pub(crate) struct UnstableAttrForAlreadyStableFeature {
+    #[primary_span]
+    #[label]
+    #[help]
+    pub span: Span,
+    #[label(passes_item)]
+    pub item_sp: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(passes_missing_stability_attr)]
 pub(crate) struct MissingStabilityAttr<'a> {
     #[primary_span]
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index 8ad14b6eb74..a4c3d789176 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -579,11 +579,10 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
             Array,
             Ptr,
             Ref,
+            PinnedRef,
             BareFn,
             Never,
             Tup,
-            AnonStruct,
-            AnonUnion,
             Path,
             Pat,
             TraitObject,
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 46b67e930d5..751c87a9fe5 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -10,6 +10,7 @@ use rustc_attr::{
 };
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet};
+use rustc_feature::ACCEPTED_FEATURES;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModDefId};
@@ -246,12 +247,27 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
                 }
             }
 
+            if let Stability { level: Unstable { .. }, feature } = stab {
+                if ACCEPTED_FEATURES.iter().find(|f| f.name == feature).is_some() {
+                    self.tcx
+                        .dcx()
+                        .emit_err(errors::UnstableAttrForAlreadyStableFeature { span, item_sp });
+                }
+            }
             if let Stability { level: Unstable { implied_by: Some(implied_by), .. }, feature } =
                 stab
             {
                 self.index.implications.insert(implied_by, feature);
             }
 
+            if let Some(ConstStability { level: Unstable { .. }, feature, .. }) = const_stab {
+                if ACCEPTED_FEATURES.iter().find(|f| f.name == feature).is_some() {
+                    self.tcx.dcx().emit_err(errors::UnstableAttrForAlreadyStableFeature {
+                        span: const_span.unwrap(), // If const_stab contains Some(..), same is true for const_span
+                        item_sp,
+                    });
+                }
+            }
             if let Some(ConstStability {
                 level: Unstable { implied_by: Some(implied_by), .. },
                 feature,
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index d836e535669..0145ffd3a4b 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -724,29 +724,6 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
         // Record field names for error reporting.
         self.insert_field_idents(def_id, fields);
         self.insert_field_visibilities_local(def_id.to_def_id(), fields);
-
-        for field in fields {
-            match &field.ty.kind {
-                ast::TyKind::AnonStruct(id, nested_fields)
-                | ast::TyKind::AnonUnion(id, nested_fields) => {
-                    let feed = self.r.feed(*id);
-                    let local_def_id = feed.key();
-                    let def_id = local_def_id.to_def_id();
-                    let def_kind = self.r.tcx.def_kind(local_def_id);
-                    let res = Res::Def(def_kind, def_id);
-                    self.build_reduced_graph_for_struct_variant(
-                        &nested_fields,
-                        Ident::empty(),
-                        feed,
-                        res,
-                        // Anonymous adts inherit visibility from their parent adts.
-                        adt_vis,
-                        field.ty.span,
-                    );
-                }
-                _ => {}
-            }
-        }
     }
 
     /// Constructs the reduced graph for one item.
diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs
index 98662385434..0047f2c4b51 100644
--- a/compiler/rustc_resolve/src/def_collector.rs
+++ b/compiler/rustc_resolve/src/def_collector.rs
@@ -105,22 +105,6 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> {
             let name = field.ident.map_or_else(|| sym::integer(index(self)), |ident| ident.name);
             let def = self.create_def(field.id, name, DefKind::Field, field.span);
             self.with_parent(def, |this| visit::walk_field_def(this, field));
-            self.visit_anon_adt(&field.ty);
-        }
-    }
-
-    fn visit_anon_adt(&mut self, ty: &'a Ty) {
-        let def_kind = match &ty.kind {
-            TyKind::AnonStruct(..) => DefKind::Struct,
-            TyKind::AnonUnion(..) => DefKind::Union,
-            _ => return,
-        };
-        match &ty.kind {
-            TyKind::AnonStruct(node_id, _) | TyKind::AnonUnion(node_id, _) => {
-                let def_id = self.create_def(*node_id, kw::Empty, def_kind, ty.span);
-                self.with_parent(def_id, |this| visit::walk_ty(this, ty));
-            }
-            _ => {}
         }
     }
 
@@ -476,8 +460,6 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
     fn visit_ty(&mut self, ty: &'a Ty) {
         match &ty.kind {
             TyKind::MacCall(..) => self.visit_macro_invoc(ty.id),
-            // Anonymous structs or unions are visited later after defined.
-            TyKind::AnonStruct(..) | TyKind::AnonUnion(..) => {}
             TyKind::ImplTrait(id, _) => {
                 // HACK: pprust breaks strings with newlines when the type
                 // gets too long. We don't want these to show up in compiler
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 66c1ff93ce1..b84cbf9c629 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -779,7 +779,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
         let prev = self.diag_metadata.current_trait_object;
         let prev_ty = self.diag_metadata.current_type_path;
         match &ty.kind {
-            TyKind::Ref(None, _) => {
+            TyKind::Ref(None, _) | TyKind::PinnedRef(None, _) => {
                 // Elided lifetime in reference: we resolve as if there was some lifetime `'_` with
                 // NodeId `ty.id`.
                 // This span will be used in case of elision failure.
@@ -2326,7 +2326,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
         impl<'ra> Visitor<'ra> for FindReferenceVisitor<'_, '_, '_> {
             fn visit_ty(&mut self, ty: &'ra Ty) {
                 trace!("FindReferenceVisitor considering ty={:?}", ty);
-                if let TyKind::Ref(lt, _) = ty.kind {
+                if let TyKind::Ref(lt, _) | TyKind::PinnedRef(lt, _) = ty.kind {
                     // See if anything inside the &thing contains Self
                     let mut visitor =
                         SelfVisitor { r: self.r, impl_self: self.impl_self, self_found: false };
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index fce5ec36c66..50fbdcaf9dc 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -519,11 +519,12 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                     continue;
                 };
                 for bound in bounds {
-                    let ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifiers::NONE) = bound
-                    else {
+                    let ast::GenericBound::Trait(trait_ref) = bound else {
                         continue;
                     };
-                    if base_error.span == trait_ref.span {
+                    if trait_ref.modifiers == ast::TraitBoundModifiers::NONE
+                        && base_error.span == trait_ref.span
+                    {
                         err.span_suggestion_verbose(
                             constraint.ident.span.between(trait_ref.span),
                             "you might have meant to write a path instead of an associated type bound",
@@ -837,7 +838,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                 );
                 if bounds.iter().all(|bound| match bound {
                     ast::GenericBound::Outlives(_) | ast::GenericBound::Use(..) => true,
-                    ast::GenericBound::Trait(tr, _) => tr.span == base_error.span,
+                    ast::GenericBound::Trait(tr) => tr.span == base_error.span,
                 }) {
                     let mut sugg = vec![];
                     if base_error.span != start_span {
@@ -1210,7 +1211,8 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
         let param = generics.params.iter().find_map(|param| {
             // Only consider type params with exactly one trait bound.
             if let [bound] = &*param.bounds
-                && let ast::GenericBound::Trait(tref, ast::TraitBoundModifiers::NONE) = bound
+                && let ast::GenericBound::Trait(tref) = bound
+                && tref.modifiers == ast::TraitBoundModifiers::NONE
                 && tref.span == span
                 && param.ident.span.eq_ctxt(span)
             {
@@ -1333,8 +1335,9 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
             }
             if let (
                 [ast::PathSegment { args: None, .. }],
-                [ast::GenericBound::Trait(poly_trait_ref, ast::TraitBoundModifiers::NONE)],
+                [ast::GenericBound::Trait(poly_trait_ref)],
             ) = (&type_param_path.segments[..], &bounds[..])
+                && poly_trait_ref.modifiers == ast::TraitBoundModifiers::NONE
             {
                 if let [ast::PathSegment { ident, args: None, .. }] =
                     &poly_trait_ref.trait_ref.path.segments[..]
@@ -2814,7 +2817,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                             && bounded_ty.id == binder
                         {
                             for bound in bounds {
-                                if let ast::GenericBound::Trait(poly_trait_ref, _) = bound
+                                if let ast::GenericBound::Trait(poly_trait_ref) = bound
                                     && let span = poly_trait_ref
                                         .span
                                         .with_hi(poly_trait_ref.trait_ref.path.span.lo())
@@ -3233,7 +3236,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                             let mut lt_finder =
                                 LifetimeFinder { lifetime: lt.span, found: None, seen: vec![] };
                             for bound in arg_refs {
-                                if let ast::GenericBound::Trait(trait_ref, _) = bound {
+                                if let ast::GenericBound::Trait(trait_ref) = bound {
                                     lt_finder.visit_trait_ref(&trait_ref.trait_ref);
                                 }
                             }
@@ -3444,17 +3447,15 @@ fn mk_where_bound_predicate(
         span: DUMMY_SP,
         bound_generic_params: ThinVec::new(),
         bounded_ty: ast::ptr::P(ty.clone()),
-        bounds: vec![ast::GenericBound::Trait(
-            ast::PolyTraitRef {
-                bound_generic_params: ThinVec::new(),
-                trait_ref: ast::TraitRef {
-                    path: ast::Path { segments: modified_segments, span: DUMMY_SP, tokens: None },
-                    ref_id: DUMMY_NODE_ID,
-                },
-                span: DUMMY_SP,
+        bounds: vec![ast::GenericBound::Trait(ast::PolyTraitRef {
+            bound_generic_params: ThinVec::new(),
+            modifiers: ast::TraitBoundModifiers::NONE,
+            trait_ref: ast::TraitRef {
+                path: ast::Path { segments: modified_segments, span: DUMMY_SP, tokens: None },
+                ref_id: DUMMY_NODE_ID,
             },
-            ast::TraitBoundModifiers::NONE,
-        )],
+            span: DUMMY_SP,
+        })],
     };
 
     Some(new_where_bound_predicate)
@@ -3482,7 +3483,7 @@ struct LifetimeFinder<'ast> {
 
 impl<'ast> Visitor<'ast> for LifetimeFinder<'ast> {
     fn visit_ty(&mut self, t: &'ast Ty) {
-        if let TyKind::Ref(_, mut_ty) = &t.kind {
+        if let TyKind::Ref(_, mut_ty) | TyKind::PinnedRef(_, mut_ty) = &t.kind {
             self.seen.push(t);
             if t.span.lo() == self.lifetime.lo() {
                 self.found = Some(&mut_ty.ty);
diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs
index b7977a848ce..47f72298e22 100644
--- a/compiler/rustc_serialize/src/lib.rs
+++ b/compiler/rustc_serialize/src/lib.rs
@@ -10,7 +10,6 @@
     test(attr(allow(unused_variables), deny(warnings)))
 )]
 #![doc(rust_logo)]
-#![feature(const_option)]
 #![feature(core_intrinsics)]
 #![feature(min_specialization)]
 #![feature(never_type)]
diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs
index 2d37f5bdab8..db8555edd0f 100644
--- a/compiler/rustc_serialize/src/serialize.rs
+++ b/compiler/rustc_serialize/src/serialize.rs
@@ -288,7 +288,7 @@ impl<D: Decoder, T: Decodable<D>> Decodable<D> for Rc<T> {
 impl<S: Encoder, T: Encodable<S>> Encodable<S> for [T] {
     default fn encode(&self, s: &mut S) {
         s.emit_usize(self.len());
-        for e in self.iter() {
+        for e in self {
             e.encode(s);
         }
     }
@@ -325,7 +325,7 @@ impl<D: Decoder, const N: usize> Decodable<D> for [u8; N] {
     }
 }
 
-impl<'a, S: Encoder, T: Encodable<S>> Encodable<S> for Cow<'a, [T]>
+impl<S: Encoder, T: Encodable<S>> Encodable<S> for Cow<'_, [T]>
 where
     [T]: ToOwned<Owned = Vec<T>>,
 {
@@ -345,14 +345,14 @@ where
     }
 }
 
-impl<'a, S: Encoder> Encodable<S> for Cow<'a, str> {
+impl<S: Encoder> Encodable<S> for Cow<'_, str> {
     fn encode(&self, s: &mut S) {
         let val: &str = self;
         val.encode(s)
     }
 }
 
-impl<'a, D: Decoder> Decodable<D> for Cow<'a, str> {
+impl<D: Decoder> Decodable<D> for Cow<'_, str> {
     fn decode(d: &mut D) -> Cow<'static, str> {
         let v: String = Decodable::decode(d);
         Cow::Owned(v)
@@ -527,7 +527,7 @@ impl<D: Decoder, T: Decodable<D>> Decodable<D> for ThinVec<T> {
 impl<S: Encoder, T: Encodable<S>> Encodable<S> for VecDeque<T> {
     fn encode(&self, s: &mut S) {
         s.emit_usize(self.len());
-        for e in self.iter() {
+        for e in self {
             e.encode(s);
         }
     }
@@ -547,7 +547,7 @@ where
 {
     fn encode(&self, e: &mut S) {
         e.emit_usize(self.len());
-        for (key, val) in self.iter() {
+        for (key, val) in self {
             key.encode(e);
             val.encode(e);
         }
@@ -571,7 +571,7 @@ where
 {
     fn encode(&self, s: &mut S) {
         s.emit_usize(self.len());
-        for e in self.iter() {
+        for e in self {
             e.encode(s);
         }
     }
@@ -595,7 +595,7 @@ where
 {
     fn encode(&self, e: &mut E) {
         e.emit_usize(self.len());
-        for (key, val) in self.iter() {
+        for (key, val) in self {
             key.encode(e);
             val.encode(e);
         }
@@ -621,7 +621,7 @@ where
 {
     fn encode(&self, s: &mut E) {
         s.emit_usize(self.len());
-        for e in self.iter() {
+        for e in self {
             e.encode(s);
         }
     }
@@ -646,7 +646,7 @@ where
 {
     fn encode(&self, e: &mut E) {
         e.emit_usize(self.len());
-        for (key, val) in self.iter() {
+        for (key, val) in self {
             key.encode(e);
             val.encode(e);
         }
@@ -672,7 +672,7 @@ where
 {
     fn encode(&self, s: &mut E) {
         s.emit_usize(self.len());
-        for e in self.iter() {
+        for e in self {
             e.encode(s);
         }
     }
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index d2c03e588ff..d733e32f209 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -842,6 +842,11 @@ pub struct NextSolverConfig {
     /// This is only `true` if `coherence` is also enabled.
     pub globally: bool,
 }
+impl Default for NextSolverConfig {
+    fn default() -> Self {
+        NextSolverConfig { coherence: true, globally: false }
+    }
+}
 
 #[derive(Clone)]
 pub enum Input {
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index d63276db493..f9964b59a94 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -403,7 +403,7 @@ mod desc {
     pub(crate) const parse_unpretty: &str = "`string` or `string=string`";
     pub(crate) const parse_treat_err_as_bug: &str = "either no value or a non-negative number";
     pub(crate) const parse_next_solver_config: &str =
-        "a comma separated list of solver configurations: `globally` (default), and `coherence`";
+        "either `globally` (when used without an argument), `coherence` (default) or `no`";
     pub(crate) const parse_lto: &str =
         "either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, `fat`, or omitted";
     pub(crate) const parse_linker_plugin_lto: &str =
@@ -1123,27 +1123,16 @@ mod parse {
         }
     }
 
-    pub(crate) fn parse_next_solver_config(
-        slot: &mut Option<NextSolverConfig>,
-        v: Option<&str>,
-    ) -> bool {
+    pub(crate) fn parse_next_solver_config(slot: &mut NextSolverConfig, v: Option<&str>) -> bool {
         if let Some(config) = v {
-            let mut coherence = false;
-            let mut globally = true;
-            for c in config.split(',') {
-                match c {
-                    "globally" => globally = true,
-                    "coherence" => {
-                        globally = false;
-                        coherence = true;
-                    }
-                    _ => return false,
-                }
-            }
-
-            *slot = Some(NextSolverConfig { coherence: coherence || globally, globally });
+            *slot = match config {
+                "no" => NextSolverConfig { coherence: false, globally: false },
+                "coherence" => NextSolverConfig { coherence: true, globally: false },
+                "globally" => NextSolverConfig { coherence: true, globally: true },
+                _ => return false,
+            };
         } else {
-            *slot = Some(NextSolverConfig { coherence: true, globally: true });
+            *slot = NextSolverConfig { coherence: true, globally: true };
         }
 
         true
@@ -1914,7 +1903,7 @@ options! {
         "the size at which the `large_assignments` lint starts to be emitted"),
     mutable_noalias: bool = (true, parse_bool, [TRACKED],
         "emit noalias metadata for mutable references (default: yes)"),
-    next_solver: Option<NextSolverConfig> = (None, parse_next_solver_config, [TRACKED],
+    next_solver: NextSolverConfig = (NextSolverConfig::default(), parse_next_solver_config, [TRACKED],
         "enable and configure the next generation trait solver used by rustc"),
     nll_facts: bool = (false, parse_bool, [UNTRACKED],
         "dump facts from NLL analysis into side files (default: no)"),
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 9781c746520..3d44810e7dd 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -66,7 +66,7 @@ impl GatedSpans {
 #[derive(Default)]
 pub struct SymbolGallery {
     /// All symbols occurred and their first occurrence span.
-    pub symbols: Lock<FxHashMap<Symbol, Span>>,
+    pub symbols: Lock<FxIndexMap<Symbol, Span>>,
 }
 
 impl SymbolGallery {
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index d67e69fe0fb..27879d817b2 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -454,6 +454,8 @@ impl Session {
         let bin_path = filesearch::make_target_bin_path(&self.sysroot, config::host_triple());
         let fallback_sysroot_paths = filesearch::sysroot_candidates()
             .into_iter()
+            // Ignore sysroot candidate if it was the same as the sysroot path we just used.
+            .filter(|sysroot| *sysroot != self.sysroot)
             .map(|sysroot| filesearch::make_target_bin_path(&sysroot, config::host_triple()));
         let search_paths = std::iter::once(bin_path).chain(fallback_sysroot_paths);
 
diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs
index 37528a4425f..9182789cf02 100644
--- a/compiler/rustc_session/src/utils.rs
+++ b/compiler/rustc_session/src/utils.rs
@@ -137,7 +137,7 @@ pub fn extra_compiler_flags() -> Option<(Vec<String>, bool)> {
             let content = if arg.len() == a.len() {
                 // A space-separated option, like `-C incremental=foo` or `--crate-type rlib`
                 match args.next() {
-                    Some(arg) => arg.to_string(),
+                    Some(arg) => arg,
                     None => continue,
                 }
             } else if arg.get(a.len()..a.len() + 1) == Some("=") {
diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml
index c52d1fcc07f..781fe6a11fe 100644
--- a/compiler/rustc_span/Cargo.toml
+++ b/compiler/rustc_span/Cargo.toml
@@ -19,5 +19,5 @@ scoped-tls = "1.0"
 sha1 = "0.10.0"
 sha2 = "0.10.1"
 tracing = "0.1"
-unicode-width = "0.1.4"
+unicode-width = "0.2.0"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 1527600e764..6f62b4f82d7 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -481,6 +481,8 @@ symbols! {
         audit_that,
         augmented_assignments,
         auto_traits,
+        autodiff,
+        autodiff_fallback,
         automatically_derived,
         avx,
         avx512_target_feature,
@@ -544,6 +546,7 @@ symbols! {
         cfg_accessible,
         cfg_attr,
         cfg_attr_multi,
+        cfg_autodiff_fallback,
         cfg_boolean_literals,
         cfg_doctest,
         cfg_eval,
@@ -777,6 +780,7 @@ symbols! {
         dropck_eyepatch,
         dropck_parametricity,
         dylib,
+        dyn_compatible_for_dispatch,
         dyn_metadata,
         dyn_star,
         dyn_trait,
@@ -913,6 +917,10 @@ symbols! {
         fmt_debug,
         fmul_algebraic,
         fmul_fast,
+        fmuladdf128,
+        fmuladdf16,
+        fmuladdf32,
+        fmuladdf64,
         fn_align,
         fn_delegation,
         fn_must_use,
@@ -997,6 +1005,7 @@ symbols! {
         hashset_iter_ty,
         hexagon_target_feature,
         hidden,
+        hint,
         homogeneous_aggregate,
         host,
         html_favicon_url,
@@ -1479,6 +1488,7 @@ symbols! {
         powif64,
         pre_dash_lto: "pre-lto",
         precise_capturing,
+        precise_capturing_in_traits,
         precise_pointer_size_matching,
         pref_align_of,
         prefetch_read_data,
@@ -1648,6 +1658,7 @@ symbols! {
         rustc_allow_incoherent_impl,
         rustc_allowed_through_unstable_modules,
         rustc_attrs,
+        rustc_autodiff,
         rustc_box,
         rustc_builtin_macro,
         rustc_capture_analysis,
diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs
index 74970a26b23..b82d327a409 100644
--- a/compiler/rustc_target/src/asm/aarch64.rs
+++ b/compiler/rustc_target/src/asm/aarch64.rs
@@ -64,6 +64,7 @@ impl AArch64InlineAsmRegClass {
                 neon: I8, I16, I32, I64, F16, F32, F64, F128,
                     VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF16(4), VecF32(2), VecF64(1),
                     VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF16(8), VecF32(4), VecF64(2);
+                // Note: When adding support for SVE vector types, they must be rejected for Arm64EC.
             },
             Self::preg => &[],
         }
@@ -96,7 +97,7 @@ fn restricted_for_arm64ec(
     _is_clobber: bool,
 ) -> Result<(), &'static str> {
     if arch == InlineAsmArch::Arm64EC {
-        Err("x13, x14, x23, x24, x28, v16-v31 cannot be used for Arm64EC")
+        Err("x13, x14, x23, x24, x28, v16-v31, p*, ffr cannot be used for Arm64EC")
     } else {
         Ok(())
     }
@@ -165,23 +166,23 @@ def_regs! {
         v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29", "z29"] % restricted_for_arm64ec,
         v30: vreg = ["v30", "b30", "h30", "s30", "d30", "q30", "z30"] % restricted_for_arm64ec,
         v31: vreg = ["v31", "b31", "h31", "s31", "d31", "q31", "z31"] % restricted_for_arm64ec,
-        p0: preg = ["p0"],
-        p1: preg = ["p1"],
-        p2: preg = ["p2"],
-        p3: preg = ["p3"],
-        p4: preg = ["p4"],
-        p5: preg = ["p5"],
-        p6: preg = ["p6"],
-        p7: preg = ["p7"],
-        p8: preg = ["p8"],
-        p9: preg = ["p9"],
-        p10: preg = ["p10"],
-        p11: preg = ["p11"],
-        p12: preg = ["p12"],
-        p13: preg = ["p13"],
-        p14: preg = ["p14"],
-        p15: preg = ["p15"],
-        ffr: preg = ["ffr"],
+        p0: preg = ["p0"] % restricted_for_arm64ec,
+        p1: preg = ["p1"] % restricted_for_arm64ec,
+        p2: preg = ["p2"] % restricted_for_arm64ec,
+        p3: preg = ["p3"] % restricted_for_arm64ec,
+        p4: preg = ["p4"] % restricted_for_arm64ec,
+        p5: preg = ["p5"] % restricted_for_arm64ec,
+        p6: preg = ["p6"] % restricted_for_arm64ec,
+        p7: preg = ["p7"] % restricted_for_arm64ec,
+        p8: preg = ["p8"] % restricted_for_arm64ec,
+        p9: preg = ["p9"] % restricted_for_arm64ec,
+        p10: preg = ["p10"] % restricted_for_arm64ec,
+        p11: preg = ["p11"] % restricted_for_arm64ec,
+        p12: preg = ["p12"] % restricted_for_arm64ec,
+        p13: preg = ["p13"] % restricted_for_arm64ec,
+        p14: preg = ["p14"] % restricted_for_arm64ec,
+        p15: preg = ["p15"] % restricted_for_arm64ec,
+        ffr: preg = ["ffr"] % restricted_for_arm64ec,
         #error = ["x19", "w19"] =>
             "x19 is used internally by LLVM and cannot be used as an operand for inline asm",
         #error = ["x29", "w29", "fp", "wfp"] =>
@@ -200,12 +201,66 @@ impl AArch64InlineAsmReg {
         _arch: InlineAsmArch,
         modifier: Option<char>,
     ) -> fmt::Result {
-        let (prefix, index) = if (self as u32) < Self::v0 as u32 {
-            (modifier.unwrap_or('x'), self as u32 - Self::x0 as u32)
+        let (prefix, index) = if let Some(index) = self.reg_index() {
+            (modifier.unwrap_or('x'), index)
+        } else if let Some(index) = self.vreg_index() {
+            (modifier.unwrap_or('v'), index)
         } else {
-            (modifier.unwrap_or('v'), self as u32 - Self::v0 as u32)
+            return out.write_str(self.name());
         };
         assert!(index < 32);
         write!(out, "{prefix}{index}")
     }
+
+    /// If the register is an integer register then return its index.
+    pub fn reg_index(self) -> Option<u32> {
+        // Unlike `vreg_index`, we can't subtract `x0` to get the u32 because
+        // `x19` and `x29` are missing and the integer constants for the
+        // `x0`..`x30` enum variants don't all match the register number. E.g. the
+        // integer constant for `x18` is 18, but the constant for `x20` is 19.
+        use AArch64InlineAsmReg::*;
+        Some(match self {
+            x0 => 0,
+            x1 => 1,
+            x2 => 2,
+            x3 => 3,
+            x4 => 4,
+            x5 => 5,
+            x6 => 6,
+            x7 => 7,
+            x8 => 8,
+            x9 => 9,
+            x10 => 10,
+            x11 => 11,
+            x12 => 12,
+            x13 => 13,
+            x14 => 14,
+            x15 => 15,
+            x16 => 16,
+            x17 => 17,
+            x18 => 18,
+            // x19 is reserved
+            x20 => 20,
+            x21 => 21,
+            x22 => 22,
+            x23 => 23,
+            x24 => 24,
+            x25 => 25,
+            x26 => 26,
+            x27 => 27,
+            x28 => 28,
+            // x29 is reserved
+            x30 => 30,
+            _ => return None,
+        })
+    }
+
+    /// If the register is a vector register then return its index.
+    pub fn vreg_index(self) -> Option<u32> {
+        use AArch64InlineAsmReg::*;
+        if self as u32 >= v0 as u32 && self as u32 <= v31 as u32 {
+            return Some(self as u32 - v0 as u32);
+        }
+        None
+    }
 }
diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs
index df13d24cb9e..4b539eb8e20 100644
--- a/compiler/rustc_target/src/asm/mod.rs
+++ b/compiler/rustc_target/src/asm/mod.rs
@@ -890,9 +890,11 @@ pub enum InlineAsmClobberAbi {
     Arm,
     AArch64,
     AArch64NoX18,
+    Arm64EC,
     RiscV,
     LoongArch,
     S390x,
+    Msp430,
 }
 
 impl InlineAsmClobberAbi {
@@ -931,7 +933,7 @@ impl InlineAsmClobberAbi {
                 _ => Err(&["C", "system", "efiapi"]),
             },
             InlineAsmArch::Arm64EC => match name {
-                "C" | "system" => Ok(InlineAsmClobberAbi::AArch64NoX18),
+                "C" | "system" => Ok(InlineAsmClobberAbi::Arm64EC),
                 _ => Err(&["C", "system"]),
             },
             InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => match name {
@@ -946,6 +948,10 @@ impl InlineAsmClobberAbi {
                 "C" | "system" => Ok(InlineAsmClobberAbi::S390x),
                 _ => Err(&["C", "system"]),
             },
+            InlineAsmArch::Msp430 => match name {
+                "C" | "system" => Ok(InlineAsmClobberAbi::Msp430),
+                _ => Err(&["C", "system"]),
+            },
             _ => Err(&[]),
         }
     }
@@ -1028,7 +1034,6 @@ impl InlineAsmClobberAbi {
                     p0, p1, p2, p3, p4, p5, p6, p7,
                     p8, p9, p10, p11, p12, p13, p14, p15,
                     ffr,
-
                 }
             },
             InlineAsmClobberAbi::AArch64NoX18 => clobbered_regs! {
@@ -1047,7 +1052,20 @@ impl InlineAsmClobberAbi {
                     p0, p1, p2, p3, p4, p5, p6, p7,
                     p8, p9, p10, p11, p12, p13, p14, p15,
                     ffr,
+                }
+            },
+            InlineAsmClobberAbi::Arm64EC => clobbered_regs! {
+                AArch64 AArch64InlineAsmReg {
+                    // x13 and x14 cannot be used in Arm64EC.
+                    x0, x1, x2, x3, x4, x5, x6, x7,
+                    x8, x9, x10, x11, x12, x15,
+                    x16, x17, x30,
 
+                    // Technically the low 64 bits of v8-v15 are preserved, but
+                    // we have no way of expressing this using clobbers.
+                    v0, v1, v2, v3, v4, v5, v6, v7,
+                    v8, v9, v10, v11, v12, v13, v14, v15,
+                    // v16-v31, p*, and ffr cannot be used in Arm64EC.
                 }
             },
             InlineAsmClobberAbi::Arm => clobbered_regs! {
@@ -1125,6 +1143,11 @@ impl InlineAsmClobberAbi {
                     a8, a9, a10, a11, a12, a13, a14, a15,
                 }
             },
+            InlineAsmClobberAbi::Msp430 => clobbered_regs! {
+                Msp430 Msp430InlineAsmReg {
+                    r11, r12, r13, r14, r15,
+                }
+            },
         }
     }
 }
diff --git a/compiler/rustc_target/src/abi/call/aarch64.rs b/compiler/rustc_target/src/callconv/aarch64.rs
index 55b65fb1caa..55b65fb1caa 100644
--- a/compiler/rustc_target/src/abi/call/aarch64.rs
+++ b/compiler/rustc_target/src/callconv/aarch64.rs
diff --git a/compiler/rustc_target/src/abi/call/amdgpu.rs b/compiler/rustc_target/src/callconv/amdgpu.rs
index 3007a729a8b..3007a729a8b 100644
--- a/compiler/rustc_target/src/abi/call/amdgpu.rs
+++ b/compiler/rustc_target/src/callconv/amdgpu.rs
diff --git a/compiler/rustc_target/src/abi/call/arm.rs b/compiler/rustc_target/src/callconv/arm.rs
index bd6f781fb81..bd6f781fb81 100644
--- a/compiler/rustc_target/src/abi/call/arm.rs
+++ b/compiler/rustc_target/src/callconv/arm.rs
diff --git a/compiler/rustc_target/src/abi/call/avr.rs b/compiler/rustc_target/src/callconv/avr.rs
index dfc991e0954..dfc991e0954 100644
--- a/compiler/rustc_target/src/abi/call/avr.rs
+++ b/compiler/rustc_target/src/callconv/avr.rs
diff --git a/compiler/rustc_target/src/abi/call/bpf.rs b/compiler/rustc_target/src/callconv/bpf.rs
index f19772ac709..f19772ac709 100644
--- a/compiler/rustc_target/src/abi/call/bpf.rs
+++ b/compiler/rustc_target/src/callconv/bpf.rs
diff --git a/compiler/rustc_target/src/abi/call/csky.rs b/compiler/rustc_target/src/callconv/csky.rs
index b1c1ae814a7..b1c1ae814a7 100644
--- a/compiler/rustc_target/src/abi/call/csky.rs
+++ b/compiler/rustc_target/src/callconv/csky.rs
diff --git a/compiler/rustc_target/src/abi/call/hexagon.rs b/compiler/rustc_target/src/callconv/hexagon.rs
index 0a0688880c0..0a0688880c0 100644
--- a/compiler/rustc_target/src/abi/call/hexagon.rs
+++ b/compiler/rustc_target/src/callconv/hexagon.rs
diff --git a/compiler/rustc_target/src/abi/call/loongarch.rs b/compiler/rustc_target/src/callconv/loongarch.rs
index 4a21935623b..4a21935623b 100644
--- a/compiler/rustc_target/src/abi/call/loongarch.rs
+++ b/compiler/rustc_target/src/callconv/loongarch.rs
diff --git a/compiler/rustc_target/src/abi/call/m68k.rs b/compiler/rustc_target/src/callconv/m68k.rs
index 82fe81f8c52..82fe81f8c52 100644
--- a/compiler/rustc_target/src/abi/call/m68k.rs
+++ b/compiler/rustc_target/src/callconv/m68k.rs
diff --git a/compiler/rustc_target/src/abi/call/mips.rs b/compiler/rustc_target/src/callconv/mips.rs
index 37980a91c76..37980a91c76 100644
--- a/compiler/rustc_target/src/abi/call/mips.rs
+++ b/compiler/rustc_target/src/callconv/mips.rs
diff --git a/compiler/rustc_target/src/abi/call/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs
index 2c3258c8d42..2c3258c8d42 100644
--- a/compiler/rustc_target/src/abi/call/mips64.rs
+++ b/compiler/rustc_target/src/callconv/mips64.rs
diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/callconv/mod.rs
index 352861c5ccb..832246495bc 100644
--- a/compiler/rustc_target/src/abi/call/mod.rs
+++ b/compiler/rustc_target/src/callconv/mod.rs
@@ -1,10 +1,11 @@
 use std::fmt;
 use std::str::FromStr;
 
+pub use rustc_abi::{Reg, RegKind};
 use rustc_macros::HashStable_Generic;
 use rustc_span::Symbol;
 
-use crate::abi::{self, Abi, Align, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout};
+use crate::abi::{self, Abi, Align, HasDataLayout, Size, TyAbiInterface, TyAndLayout};
 use crate::spec::{self, HasTargetSpec, HasWasmCAbiOpt, WasmCAbi};
 
 mod aarch64;
@@ -192,63 +193,6 @@ impl ArgAttributes {
     }
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
-pub enum RegKind {
-    Integer,
-    Float,
-    Vector,
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
-pub struct Reg {
-    pub kind: RegKind,
-    pub size: Size,
-}
-
-macro_rules! reg_ctor {
-    ($name:ident, $kind:ident, $bits:expr) => {
-        pub fn $name() -> Reg {
-            Reg { kind: RegKind::$kind, size: Size::from_bits($bits) }
-        }
-    };
-}
-
-impl Reg {
-    reg_ctor!(i8, Integer, 8);
-    reg_ctor!(i16, Integer, 16);
-    reg_ctor!(i32, Integer, 32);
-    reg_ctor!(i64, Integer, 64);
-    reg_ctor!(i128, Integer, 128);
-
-    reg_ctor!(f32, Float, 32);
-    reg_ctor!(f64, Float, 64);
-}
-
-impl Reg {
-    pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align {
-        let dl = cx.data_layout();
-        match self.kind {
-            RegKind::Integer => match self.size.bits() {
-                1 => dl.i1_align.abi,
-                2..=8 => dl.i8_align.abi,
-                9..=16 => dl.i16_align.abi,
-                17..=32 => dl.i32_align.abi,
-                33..=64 => dl.i64_align.abi,
-                65..=128 => dl.i128_align.abi,
-                _ => panic!("unsupported integer: {self:?}"),
-            },
-            RegKind::Float => match self.size.bits() {
-                16 => dl.f16_align.abi,
-                32 => dl.f32_align.abi,
-                64 => dl.f64_align.abi,
-                128 => dl.f128_align.abi,
-                _ => panic!("unsupported float: {self:?}"),
-            },
-            RegKind::Vector => dl.vector_align(self.size).abi,
-        }
-    }
-}
-
 /// An argument passed entirely registers with the
 /// same kind (e.g., HFA / HVA on PPC64 and AArch64).
 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
@@ -380,195 +324,6 @@ impl CastTarget {
     }
 }
 
-/// Return value from the `homogeneous_aggregate` test function.
-#[derive(Copy, Clone, Debug)]
-pub enum HomogeneousAggregate {
-    /// Yes, all the "leaf fields" of this struct are passed in the
-    /// same way (specified in the `Reg` value).
-    Homogeneous(Reg),
-
-    /// There are no leaf fields at all.
-    NoData,
-}
-
-/// Error from the `homogeneous_aggregate` test function, indicating
-/// there are distinct leaf fields passed in different ways,
-/// or this is uninhabited.
-#[derive(Copy, Clone, Debug)]
-pub struct Heterogeneous;
-
-impl HomogeneousAggregate {
-    /// If this is a homogeneous aggregate, returns the homogeneous
-    /// unit, else `None`.
-    pub fn unit(self) -> Option<Reg> {
-        match self {
-            HomogeneousAggregate::Homogeneous(reg) => Some(reg),
-            HomogeneousAggregate::NoData => None,
-        }
-    }
-
-    /// Try to combine two `HomogeneousAggregate`s, e.g. from two fields in
-    /// the same `struct`. Only succeeds if only one of them has any data,
-    /// or both units are identical.
-    fn merge(self, other: HomogeneousAggregate) -> Result<HomogeneousAggregate, Heterogeneous> {
-        match (self, other) {
-            (x, HomogeneousAggregate::NoData) | (HomogeneousAggregate::NoData, x) => Ok(x),
-
-            (HomogeneousAggregate::Homogeneous(a), HomogeneousAggregate::Homogeneous(b)) => {
-                if a != b {
-                    return Err(Heterogeneous);
-                }
-                Ok(self)
-            }
-        }
-    }
-}
-
-impl<'a, Ty> TyAndLayout<'a, Ty> {
-    /// Returns `true` if this is an aggregate type (including a ScalarPair!)
-    fn is_aggregate(&self) -> bool {
-        match self.abi {
-            Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } => false,
-            Abi::ScalarPair(..) | Abi::Aggregate { .. } => true,
-        }
-    }
-
-    /// Returns `Homogeneous` if this layout is an aggregate containing fields of
-    /// only a single type (e.g., `(u32, u32)`). Such aggregates are often
-    /// special-cased in ABIs.
-    ///
-    /// Note: We generally ignore 1-ZST fields when computing this value (see #56877).
-    ///
-    /// This is public so that it can be used in unit tests, but
-    /// should generally only be relevant to the ABI details of
-    /// specific targets.
-    pub fn homogeneous_aggregate<C>(&self, cx: &C) -> Result<HomogeneousAggregate, Heterogeneous>
-    where
-        Ty: TyAbiInterface<'a, C> + Copy,
-    {
-        match self.abi {
-            Abi::Uninhabited => Err(Heterogeneous),
-
-            // The primitive for this algorithm.
-            Abi::Scalar(scalar) => {
-                let kind = match scalar.primitive() {
-                    abi::Int(..) | abi::Pointer(_) => RegKind::Integer,
-                    abi::Float(_) => RegKind::Float,
-                };
-                Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size }))
-            }
-
-            Abi::Vector { .. } => {
-                assert!(!self.is_zst());
-                Ok(HomogeneousAggregate::Homogeneous(Reg {
-                    kind: RegKind::Vector,
-                    size: self.size,
-                }))
-            }
-
-            Abi::ScalarPair(..) | Abi::Aggregate { sized: true } => {
-                // Helper for computing `homogeneous_aggregate`, allowing a custom
-                // starting offset (used below for handling variants).
-                let from_fields_at =
-                    |layout: Self,
-                     start: Size|
-                     -> Result<(HomogeneousAggregate, Size), Heterogeneous> {
-                        let is_union = match layout.fields {
-                            FieldsShape::Primitive => {
-                                unreachable!("aggregates can't have `FieldsShape::Primitive`")
-                            }
-                            FieldsShape::Array { count, .. } => {
-                                assert_eq!(start, Size::ZERO);
-
-                                let result = if count > 0 {
-                                    layout.field(cx, 0).homogeneous_aggregate(cx)?
-                                } else {
-                                    HomogeneousAggregate::NoData
-                                };
-                                return Ok((result, layout.size));
-                            }
-                            FieldsShape::Union(_) => true,
-                            FieldsShape::Arbitrary { .. } => false,
-                        };
-
-                        let mut result = HomogeneousAggregate::NoData;
-                        let mut total = start;
-
-                        for i in 0..layout.fields.count() {
-                            let field = layout.field(cx, i);
-                            if field.is_1zst() {
-                                // No data here and no impact on layout, can be ignored.
-                                // (We might be able to also ignore all aligned ZST but that's less clear.)
-                                continue;
-                            }
-
-                            if !is_union && total != layout.fields.offset(i) {
-                                // This field isn't just after the previous one we considered, abort.
-                                return Err(Heterogeneous);
-                            }
-
-                            result = result.merge(field.homogeneous_aggregate(cx)?)?;
-
-                            // Keep track of the offset (without padding).
-                            let size = field.size;
-                            if is_union {
-                                total = total.max(size);
-                            } else {
-                                total += size;
-                            }
-                        }
-
-                        Ok((result, total))
-                    };
-
-                let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?;
-
-                match &self.variants {
-                    abi::Variants::Single { .. } => {}
-                    abi::Variants::Multiple { variants, .. } => {
-                        // Treat enum variants like union members.
-                        // HACK(eddyb) pretend the `enum` field (discriminant)
-                        // is at the start of every variant (otherwise the gap
-                        // at the start of all variants would disqualify them).
-                        //
-                        // NB: for all tagged `enum`s (which include all non-C-like
-                        // `enum`s with defined FFI representation), this will
-                        // match the homogeneous computation on the equivalent
-                        // `struct { tag; union { variant1; ... } }` and/or
-                        // `union { struct { tag; variant1; } ... }`
-                        // (the offsets of variant fields should be identical
-                        // between the two for either to be a homogeneous aggregate).
-                        let variant_start = total;
-                        for variant_idx in variants.indices() {
-                            let (variant_result, variant_total) =
-                                from_fields_at(self.for_variant(cx, variant_idx), variant_start)?;
-
-                            result = result.merge(variant_result)?;
-                            total = total.max(variant_total);
-                        }
-                    }
-                }
-
-                // There needs to be no padding.
-                if total != self.size {
-                    Err(Heterogeneous)
-                } else {
-                    match result {
-                        HomogeneousAggregate::Homogeneous(_) => {
-                            assert_ne!(total, Size::ZERO);
-                        }
-                        HomogeneousAggregate::NoData => {
-                            assert_eq!(total, Size::ZERO);
-                        }
-                    }
-                    Ok(result)
-                }
-            }
-            Abi::Aggregate { sized: false } => Err(Heterogeneous),
-        }
-    }
-}
-
 /// Information about how to pass an argument to,
 /// or return a value from, a function, under some ABI.
 #[derive(Clone, PartialEq, Eq, Hash, HashStable_Generic)]
diff --git a/compiler/rustc_target/src/abi/call/msp430.rs b/compiler/rustc_target/src/callconv/msp430.rs
index 4f613aa6c15..4f613aa6c15 100644
--- a/compiler/rustc_target/src/abi/call/msp430.rs
+++ b/compiler/rustc_target/src/callconv/msp430.rs
diff --git a/compiler/rustc_target/src/abi/call/nvptx64.rs b/compiler/rustc_target/src/callconv/nvptx64.rs
index 2e8b16d3a93..2e8b16d3a93 100644
--- a/compiler/rustc_target/src/abi/call/nvptx64.rs
+++ b/compiler/rustc_target/src/callconv/nvptx64.rs
diff --git a/compiler/rustc_target/src/abi/call/powerpc.rs b/compiler/rustc_target/src/callconv/powerpc.rs
index f3b05c48173..f3b05c48173 100644
--- a/compiler/rustc_target/src/abi/call/powerpc.rs
+++ b/compiler/rustc_target/src/callconv/powerpc.rs
diff --git a/compiler/rustc_target/src/abi/call/powerpc64.rs b/compiler/rustc_target/src/callconv/powerpc64.rs
index b9767bf906b..71e533b8cc5 100644
--- a/compiler/rustc_target/src/abi/call/powerpc64.rs
+++ b/compiler/rustc_target/src/callconv/powerpc64.rs
@@ -10,6 +10,7 @@ use crate::spec::HasTargetSpec;
 enum ABI {
     ELFv1, // original ABI used for powerpc64 (big-endian)
     ELFv2, // newer ABI used for powerpc64le and musl (both endians)
+    AIX,   // used by AIX OS, big-endian only
 }
 use ABI::*;
 
@@ -23,9 +24,9 @@ where
     C: HasDataLayout,
 {
     arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| {
-        // ELFv1 only passes one-member aggregates transparently.
+        // ELFv1 and AIX only passes one-member aggregates transparently.
         // ELFv2 passes up to eight uniquely addressable members.
-        if (abi == ELFv1 && arg.layout.size > unit.size)
+        if ((abi == ELFv1 || abi == AIX) && arg.layout.size > unit.size)
             || arg.layout.size > unit.size.checked_mul(8, cx).unwrap()
         {
             return None;
@@ -55,8 +56,15 @@ where
         return;
     }
 
+    // The AIX ABI expect byval for aggregates
+    // See https://github.com/llvm/llvm-project/blob/main/clang/lib/CodeGen/Targets/PPC.cpp.
+    if !is_ret && abi == AIX {
+        arg.pass_by_stack_offset(None);
+        return;
+    }
+
     // The ELFv1 ABI doesn't return aggregates in registers
-    if is_ret && abi == ELFv1 {
+    if is_ret && (abi == ELFv1 || abi == AIX) {
         arg.make_indirect();
         return;
     }
@@ -93,6 +101,8 @@ where
 {
     let abi = if cx.target_spec().env == "musl" {
         ELFv2
+    } else if cx.target_spec().os == "aix" {
+        AIX
     } else {
         match cx.data_layout().endian {
             Endian::Big => ELFv1,
diff --git a/compiler/rustc_target/src/abi/call/riscv.rs b/compiler/rustc_target/src/callconv/riscv.rs
index be6bc701b49..be6bc701b49 100644
--- a/compiler/rustc_target/src/abi/call/riscv.rs
+++ b/compiler/rustc_target/src/callconv/riscv.rs
diff --git a/compiler/rustc_target/src/abi/call/s390x.rs b/compiler/rustc_target/src/callconv/s390x.rs
index 502e7331267..502e7331267 100644
--- a/compiler/rustc_target/src/abi/call/s390x.rs
+++ b/compiler/rustc_target/src/callconv/s390x.rs
diff --git a/compiler/rustc_target/src/abi/call/sparc.rs b/compiler/rustc_target/src/callconv/sparc.rs
index 37980a91c76..37980a91c76 100644
--- a/compiler/rustc_target/src/abi/call/sparc.rs
+++ b/compiler/rustc_target/src/callconv/sparc.rs
diff --git a/compiler/rustc_target/src/abi/call/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs
index 835353f76fc..835353f76fc 100644
--- a/compiler/rustc_target/src/abi/call/sparc64.rs
+++ b/compiler/rustc_target/src/callconv/sparc64.rs
diff --git a/compiler/rustc_target/src/abi/call/wasm.rs b/compiler/rustc_target/src/callconv/wasm.rs
index 3c4cd76a754..3c4cd76a754 100644
--- a/compiler/rustc_target/src/abi/call/wasm.rs
+++ b/compiler/rustc_target/src/callconv/wasm.rs
diff --git a/compiler/rustc_target/src/abi/call/x86.rs b/compiler/rustc_target/src/callconv/x86.rs
index d9af83d3205..d9af83d3205 100644
--- a/compiler/rustc_target/src/abi/call/x86.rs
+++ b/compiler/rustc_target/src/callconv/x86.rs
diff --git a/compiler/rustc_target/src/abi/call/x86_64.rs b/compiler/rustc_target/src/callconv/x86_64.rs
index 9910e623ac9..9910e623ac9 100644
--- a/compiler/rustc_target/src/abi/call/x86_64.rs
+++ b/compiler/rustc_target/src/callconv/x86_64.rs
diff --git a/compiler/rustc_target/src/abi/call/x86_win64.rs b/compiler/rustc_target/src/callconv/x86_win64.rs
index e5a20b248e4..e5a20b248e4 100644
--- a/compiler/rustc_target/src/abi/call/x86_win64.rs
+++ b/compiler/rustc_target/src/callconv/x86_win64.rs
diff --git a/compiler/rustc_target/src/abi/call/xtensa.rs b/compiler/rustc_target/src/callconv/xtensa.rs
index e1728b08a39..e1728b08a39 100644
--- a/compiler/rustc_target/src/abi/call/xtensa.rs
+++ b/compiler/rustc_target/src/callconv/xtensa.rs
diff --git a/compiler/rustc_target/src/json.rs b/compiler/rustc_target/src/json.rs
index 2c367defe7b..b09d8d724ef 100644
--- a/compiler/rustc_target/src/json.rs
+++ b/compiler/rustc_target/src/json.rs
@@ -134,3 +134,9 @@ impl ToJson for TargetMetadata {
         })
     }
 }
+
+impl ToJson for rustc_abi::Endian {
+    fn to_json(&self) -> Json {
+        self.as_str().to_json()
+    }
+}
diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs
index 2121c4110dd..50679ab8cc8 100644
--- a/compiler/rustc_target/src/lib.rs
+++ b/compiler/rustc_target/src/lib.rs
@@ -21,8 +21,8 @@
 
 use std::path::{Path, PathBuf};
 
-pub mod abi;
 pub mod asm;
+pub mod callconv;
 pub mod json;
 pub mod spec;
 pub mod target_features;
@@ -30,6 +30,15 @@ pub mod target_features;
 #[cfg(test)]
 mod tests;
 
+pub mod abi {
+    pub(crate) use Float::*;
+    pub(crate) use Primitive::*;
+    // Explicitly import `Float` to avoid ambiguity with `Primitive::Float`.
+    pub use rustc_abi::{Float, *};
+
+    pub use crate::callconv as call;
+}
+
 pub use rustc_abi::HashStableContext;
 
 /// The name of rustc's own place to organize libraries.
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs
index e6ce50a005f..adee6f5fe99 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs
@@ -12,7 +12,8 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:o-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+            .into(),
         arch,
         options: TargetOptions {
             mcount: "\u{1}mcount".into(),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs
index 78441ede333..efc42b909e4 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs
@@ -12,7 +12,8 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:o-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+            .into(),
         arch,
         options: TargetOptions {
             features: "+neon,+fp-armv8,+apple-a7".into(),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs
index 857abd21faf..be503d18bf1 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs
@@ -12,7 +12,8 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:o-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+            .into(),
         arch,
         options: TargetOptions {
             features: "+neon,+fp-armv8,+apple-a12".into(),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs
index cbc8b7cd95e..04bbee45cd3 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs
@@ -12,7 +12,8 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:o-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+            .into(),
         arch,
         options: TargetOptions {
             features: "+neon,+fp-armv8,+apple-a7".into(),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs
index f5176d35f80..fa0bc130e1c 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs
@@ -12,7 +12,8 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:o-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+            .into(),
         arch,
         options: TargetOptions {
             features: "+neon,+fp-armv8,+apple-a7".into(),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs
index d6738514145..428045da493 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs
@@ -12,7 +12,8 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:o-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+            .into(),
         arch,
         options: TargetOptions {
             features: "+neon,+fp-armv8,+apple-a7".into(),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs
index dc5dac4fd8f..62d6ffbd34f 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs
@@ -12,7 +12,8 @@ pub(crate) fn target() -> Target {
             std: Some(false),
         },
         pointer_width: 64,
-        data_layout: "e-m:o-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+            .into(),
         arch,
         options: TargetOptions {
             features: "+neon,+fp-armv8,+apple-a16".into(),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs
index e9fe786f65e..a66c4f6e96b 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs
@@ -12,7 +12,8 @@ pub(crate) fn target() -> Target {
             std: Some(false),
         },
         pointer_width: 64,
-        data_layout: "e-m:o-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+            .into(),
         arch,
         options: TargetOptions {
             features: "+neon,+fp-armv8,+apple-a16".into(),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs
index 4f5ce2f12e7..abd924b5934 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs
@@ -12,7 +12,8 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:o-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+            .into(),
         arch,
         options: TargetOptions {
             features: "+v8a,+neon,+fp-armv8,+apple-a7".into(),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs
index 952c98c324f..ba85647fddc 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs
@@ -12,7 +12,8 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:o-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+            .into(),
         arch,
         options: TargetOptions {
             features: "+neon,+fp-armv8,+apple-a7".into(),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_kmc_solid_asp3.rs b/compiler/rustc_target/src/spec/targets/aarch64_kmc_solid_asp3.rs
index 97ce53b2d8f..58fc703946e 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_kmc_solid_asp3.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_kmc_solid_asp3.rs
@@ -11,7 +11,7 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
         arch: "aarch64".into(),
         options: TargetOptions {
             linker: Some("aarch64-kmc-elf-gcc".into()),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_linux_android.rs b/compiler/rustc_target/src/spec/targets/aarch64_linux_android.rs
index a8ef5544a24..a021d317cc8 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_linux_android.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_linux_android.rs
@@ -13,7 +13,7 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
         arch: "aarch64".into(),
         options: TargetOptions {
             max_atomic_width: Some(128),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs b/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs
index 1f1cd966326..6ac69e0f57f 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs
@@ -15,7 +15,7 @@ pub(crate) fn target() -> Target {
             std: Some(false),
         },
         pointer_width: 64,
-        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
         arch: "aarch64".into(),
         options: TargetOptions {
             features: "+v8a".into(),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs
index 7a56809dcfc..8b96f589c74 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs
@@ -15,7 +15,9 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout:
+            "e-m:w-p270:32:32-p271:32:32-p272:64:64-p:64:64-i32:32-i64:64-i128:128-n32:64-S128-Fn32"
+                .into(),
         arch: "aarch64".into(),
         options: base,
     }
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs
index e1133a4ce05..14ce5edd2f3 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs
@@ -14,7 +14,9 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout:
+            "e-m:w-p270:32:32-p271:32:32-p272:64:64-p:64:64-i32:32-i64:64-i128:128-n32:64-S128-Fn32"
+                .into(),
         arch: "aarch64".into(),
         options: base,
     }
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs
index 2ca2f403e17..dd90161f440 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs
@@ -10,7 +10,7 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
         arch: "aarch64".into(),
         options: TargetOptions {
             features: "+v8a".into(),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs
index 26a736a2712..df13d52a223 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs
@@ -10,7 +10,7 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
         arch: "aarch64".into(),
         options: TargetOptions {
             features: "+v8a".into(),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_hermit.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_hermit.rs
index 3989cc13fba..459e888eb94 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_hermit.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_hermit.rs
@@ -11,7 +11,7 @@ pub(crate) fn target() -> Target {
         },
         pointer_width: 64,
         arch: "aarch64".into(),
-        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
         options: TargetOptions {
             features: "+v8a,+strict-align,+neon,+fp-armv8".into(),
             max_atomic_width: Some(128),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_illumos.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_illumos.rs
index b76f10b8efd..699376a7928 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_illumos.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_illumos.rs
@@ -18,7 +18,7 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
         arch: "aarch64".into(),
         options: base,
     }
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs
index 96c25af0a7f..18711cb399d 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs
@@ -10,7 +10,7 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
         arch: "aarch64".into(),
         options: TargetOptions {
             features: "+v8a,+outline-atomics".into(),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs
index 197ff2d74e5..bb65048a56d 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs
@@ -21,7 +21,7 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
         arch: "aarch64".into(),
         options: TargetOptions { mcount: "\u{1}_mcount".into(), ..base },
     }
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs
index 9d5bce05350..22b3a5f8842 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs
@@ -13,7 +13,7 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
         arch: "aarch64".into(),
         options: TargetOptions {
             features: "+reserve-x18".into(),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_netbsd.rs
index d1769d1476c..0ec76e4b42f 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_netbsd.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_netbsd.rs
@@ -10,7 +10,7 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
         arch: "aarch64".into(),
         options: TargetOptions {
             features: "+v8a".into(),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none.rs
index d124f8a3d29..05fe32734a7 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none.rs
@@ -37,7 +37,7 @@ pub(crate) fn target() -> Target {
             std: Some(false),
         },
         pointer_width: 64,
-        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
         arch: "aarch64".into(),
         options: opts,
     }
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs
index 7933041b584..d6b77ffd091 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs
@@ -32,7 +32,7 @@ pub(crate) fn target() -> Target {
             std: Some(false),
         },
         pointer_width: 64,
-        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
         arch: "aarch64".into(),
         options: opts,
     }
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs
index 6c061112b16..441ed7cf153 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs
@@ -22,7 +22,7 @@ pub(crate) fn target() -> Target {
         // i128:128  = 128-bit-integer, minimum_alignment=128, preferred_alignment=128
         // n32:64    = 32 and 64 are native integer widths; Elements of this set are considered to support most general arithmetic operations efficiently.
         // S128      = 128 bits are the natural alignment of the stack in bits.
-        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
         arch: "aarch64".into(),
         options: TargetOptions {
             features: "+v8a".into(),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_openbsd.rs
index 31c8daa98b0..0fcf5c34bb0 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_openbsd.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_openbsd.rs
@@ -10,7 +10,7 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
         arch: "aarch64".into(),
         options: TargetOptions {
             features: "+v8a".into(),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_redox.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_redox.rs
index 6bd8c306abb..7ff99c574ad 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_redox.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_redox.rs
@@ -15,7 +15,7 @@ pub(crate) fn target() -> Target {
             std: None, // ?
         },
         pointer_width: 64,
-        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
         arch: "aarch64".into(),
         options: base,
     }
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_teeos.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_teeos.rs
index 4bffef4758d..fb8b59f7729 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_teeos.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_teeos.rs
@@ -15,7 +15,7 @@ pub(crate) fn target() -> Target {
             std: None, // ?
         },
         pointer_width: 64,
-        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
         arch: "aarch64".into(),
         options: base,
     }
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_trusty.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_trusty.rs
index 69ab992e12c..cebd8ff2f68 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_trusty.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_trusty.rs
@@ -12,7 +12,7 @@ pub(crate) fn target() -> Target {
             std: Some(false),
         },
         pointer_width: 64,
-        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
         arch: "aarch64".into(),
         options: TargetOptions {
             features: "+neon,+fp-armv8,+reserve-x18".into(),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_uefi.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_uefi.rs
index bde1ce95b3f..9656024ddaa 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_uefi.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_uefi.rs
@@ -19,7 +19,9 @@ pub(crate) fn target() -> Target {
             std: None, // ?
         },
         pointer_width: 64,
-        data_layout: "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout:
+            "e-m:w-p270:32:32-p271:32:32-p272:64:64-p:64:64-i32:32-i64:64-i128:128-n32:64-S128-Fn32"
+                .into(),
         arch: "aarch64".into(),
         options: base,
     }
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/aarch64_uwp_windows_msvc.rs
index c916aa6b23c..3d7c0269808 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_uwp_windows_msvc.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_uwp_windows_msvc.rs
@@ -14,7 +14,9 @@ pub(crate) fn target() -> Target {
             std: None, // ?
         },
         pointer_width: 64,
-        data_layout: "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout:
+            "e-m:w-p270:32:32-p271:32:32-p272:64:64-p:64:64-i32:32-i64:64-i128:128-n32:64-S128-Fn32"
+                .into(),
         arch: "aarch64".into(),
         options: base,
     }
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs
index dd5298944e0..d5e78d03076 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs
@@ -10,7 +10,7 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
         arch: "aarch64".into(),
         options: TargetOptions {
             features: "+v8a".into(),
diff --git a/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs b/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs
index 4f7a1857e76..a5a6f772ac8 100644
--- a/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs
+++ b/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs
@@ -12,7 +12,8 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 32,
-        data_layout: "e-m:o-p:32:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout:
+            "e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32".into(),
         arch,
         options: TargetOptions {
             features: "+v8a,+neon,+fp-armv8,+apple-a7".into(),
diff --git a/compiler/rustc_target/src/spec/targets/arm64e_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/arm64e_apple_darwin.rs
index 04906fb0460..744d95445b8 100644
--- a/compiler/rustc_target/src/spec/targets/arm64e_apple_darwin.rs
+++ b/compiler/rustc_target/src/spec/targets/arm64e_apple_darwin.rs
@@ -12,7 +12,8 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:o-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+            .into(),
         arch,
         options: TargetOptions {
             mcount: "\u{1}mcount".into(),
diff --git a/compiler/rustc_target/src/spec/targets/arm64e_apple_ios.rs b/compiler/rustc_target/src/spec/targets/arm64e_apple_ios.rs
index 3264669c169..dace11dae24 100644
--- a/compiler/rustc_target/src/spec/targets/arm64e_apple_ios.rs
+++ b/compiler/rustc_target/src/spec/targets/arm64e_apple_ios.rs
@@ -12,7 +12,8 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:o-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+            .into(),
         arch,
         options: TargetOptions {
             features: "+neon,+fp-armv8,+apple-a12,+v8.3a,+pauth".into(),
diff --git a/compiler/rustc_target/src/spec/targets/arm64e_apple_tvos.rs b/compiler/rustc_target/src/spec/targets/arm64e_apple_tvos.rs
index 4c231cb519c..2ccdc76c52e 100644
--- a/compiler/rustc_target/src/spec/targets/arm64e_apple_tvos.rs
+++ b/compiler/rustc_target/src/spec/targets/arm64e_apple_tvos.rs
@@ -12,7 +12,8 @@ pub(crate) fn target() -> Target {
             std: Some(true),
         },
         pointer_width: 64,
-        data_layout: "e-m:o-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+            .into(),
         arch,
         options: TargetOptions {
             features: "+neon,+fp-armv8,+apple-a12,+v8.3a,+pauth".into(),
diff --git a/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs
index a6744b95a3e..03c96fbfdb0 100644
--- a/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs
+++ b/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs
@@ -18,7 +18,9 @@ pub(crate) fn target() -> Target {
             std: None, // ?
         },
         pointer_width: 64,
-        data_layout: "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        data_layout:
+            "e-m:w-p270:32:32-p271:32:32-p272:64:64-p:64:64-i32:32-i64:64-i128:128-n32:64-S128-Fn32"
+                .into(),
         arch: "arm64ec".into(),
         options: base,
     }
diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs
index de8d99977b4..e869314d4d8 100644
--- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs
+++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs
@@ -1,4 +1,4 @@
-use crate::spec::{CodeModel, Target, TargetOptions, base};
+use crate::spec::{CodeModel, SanitizerSet, Target, TargetOptions, base};
 
 pub(crate) fn target() -> Target {
     Target {
@@ -18,6 +18,11 @@ pub(crate) fn target() -> Target {
             features: "+f,+d".into(),
             llvm_abiname: "lp64d".into(),
             max_atomic_width: Some(64),
+            supported_sanitizers: SanitizerSet::ADDRESS
+                | SanitizerSet::CFI
+                | SanitizerSet::LEAK
+                | SanitizerSet::MEMORY
+                | SanitizerSet::THREAD,
             direct_access_external_data: Some(false),
             ..base::linux_gnu::opts()
         },
diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs
index a5088bcd5c2..70e8bf633a9 100644
--- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs
@@ -1,4 +1,4 @@
-use crate::spec::{CodeModel, Target, TargetOptions, base};
+use crate::spec::{CodeModel, SanitizerSet, Target, TargetOptions, base};
 
 pub(crate) fn target() -> Target {
     Target {
@@ -19,6 +19,11 @@ pub(crate) fn target() -> Target {
             llvm_abiname: "lp64d".into(),
             max_atomic_width: Some(64),
             crt_static_default: false,
+            supported_sanitizers: SanitizerSet::ADDRESS
+                | SanitizerSet::CFI
+                | SanitizerSet::LEAK
+                | SanitizerSet::MEMORY
+                | SanitizerSet::THREAD,
             ..base::linux_musl::opts()
         },
     }
diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs
index 32a856be669..90bcd9a45cf 100644
--- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs
+++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs
@@ -1,4 +1,4 @@
-use crate::spec::{Target, TargetOptions, base};
+use crate::spec::{SanitizerSet, Target, TargetOptions, base};
 
 pub(crate) fn target() -> Target {
     Target {
@@ -17,6 +17,11 @@ pub(crate) fn target() -> Target {
             features: "+f,+d".into(),
             llvm_abiname: "lp64d".into(),
             max_atomic_width: Some(64),
+            supported_sanitizers: SanitizerSet::ADDRESS
+                | SanitizerSet::CFI
+                | SanitizerSet::LEAK
+                | SanitizerSet::MEMORY
+                | SanitizerSet::THREAD,
             ..base::linux_ohos::opts()
         },
     }
diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs
index 0a98b363b1a..e92366d5c5c 100644
--- a/compiler/rustc_target/src/target_features.rs
+++ b/compiler/rustc_target/src/target_features.rs
@@ -191,6 +191,8 @@ const AARCH64_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     ("sm4", Stable, &["neon"]),
     // FEAT_SME
     ("sme", Unstable(sym::aarch64_unstable_target_feature), &["bf16"]),
+    // FEAT_SME_B16B16
+    ("sme-b16b16", Unstable(sym::aarch64_unstable_target_feature), &["bf16", "sme2", "sve-b16b16"]),
     // FEAT_SME_F16F16
     ("sme-f16f16", Unstable(sym::aarch64_unstable_target_feature), &["sme2"]),
     // FEAT_SME_F64F64
@@ -227,7 +229,7 @@ const AARCH64_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     //
     // "For backwards compatibility, Neon and VFP are required in the latest architectures."
     ("sve", Stable, &["neon"]),
-    // FEAT_SVE_B16B16 (SVE or SME Instructions)
+    // FEAT_SVE_B16B16 (SVE or SME Z-targeting instructions)
     ("sve-b16b16", Unstable(sym::aarch64_unstable_target_feature), &["bf16"]),
     // FEAT_SVE2
     ("sve2", Stable, &["sve"]),
diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml
index f023a0eb53a..d4bb84838cc 100644
--- a/compiler/rustc_trait_selection/Cargo.toml
+++ b/compiler/rustc_trait_selection/Cargo.toml
@@ -26,5 +26,6 @@ rustc_target = { path = "../rustc_target" }
 rustc_transmute = { path = "../rustc_transmute", features = ["rustc"] }
 rustc_type_ir = { path = "../rustc_type_ir" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
+thin-vec = "0.2"
 tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
index c7b3f704330..df5800ab58a 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
@@ -1127,18 +1127,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     /// the message in `secondary_span` as the primary label, and apply the message that would
     /// otherwise be used for the primary label on the `secondary_span` `Span`. This applies on
     /// E0271, like `tests/ui/issues/issue-39970.stderr`.
-    #[instrument(
-        level = "debug",
-        skip(self, diag, secondary_span, swap_secondary_and_primary, prefer_label)
-    )]
+    #[instrument(level = "debug", skip(self, diag, secondary_span, prefer_label))]
     pub fn note_type_err(
         &self,
         diag: &mut Diag<'_>,
         cause: &ObligationCause<'tcx>,
-        secondary_span: Option<(Span, Cow<'static, str>)>,
+        secondary_span: Option<(Span, Cow<'static, str>, bool)>,
         mut values: Option<ValuePairs<'tcx>>,
         terr: TypeError<'tcx>,
-        swap_secondary_and_primary: bool,
         prefer_label: bool,
     ) {
         let span = cause.span();
@@ -1285,9 +1281,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     ValuePairs::ExistentialProjection(_) => {
                         (false, Mismatch::Fixed("existential projection"))
                     }
-                    ValuePairs::Dummy => {
-                        bug!("do not expect to report a type error from a ValuePairs::Dummy")
-                    }
                 };
                 let Some(vals) = self.values_str(values) else {
                     // Derived error. Cancel the emitter.
@@ -1307,7 +1300,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 diag.span_note(span, msg);
             }
         };
-        if let Some((sp, msg)) = secondary_span {
+        if let Some((secondary_span, secondary_msg, swap_secondary_and_primary)) = secondary_span {
             if swap_secondary_and_primary {
                 let terr = if let Some(infer::ValuePairs::Terms(ExpectedFound {
                     expected, ..
@@ -1317,11 +1310,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 } else {
                     terr.to_string(self.tcx)
                 };
-                label_or_note(sp, terr);
-                label_or_note(span, msg);
+                label_or_note(secondary_span, terr);
+                label_or_note(span, secondary_msg);
             } else {
                 label_or_note(span, terr.to_string(self.tcx));
-                label_or_note(sp, msg);
+                label_or_note(secondary_span, secondary_msg);
             }
         } else if let Some(values) = values
             && let Some((e, f)) = values.ty()
@@ -1791,7 +1784,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             self.type_error_additional_suggestions(&trace, terr),
         );
         let mut diag = self.dcx().create_err(failure_code);
-        self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr, false, false);
+        self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr, false);
         diag
     }
 
@@ -1853,9 +1846,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 let (exp, fnd) = self.cmp_fn_sig(&exp_found.expected, &exp_found.found);
                 Some((exp, fnd, None))
             }
-            ValuePairs::Dummy => {
-                bug!("do not expect to report a type error from a ValuePairs::Dummy")
-            }
         }
     }
 
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs
index 7a44c2ad661..2ecd28f4868 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs
@@ -86,7 +86,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
             }
 
             hir::TyKind::TraitObject(bounds, ..) => {
-                for (bound, _) in bounds {
+                for bound in bounds {
                     self.current_index.shift_in(1);
                     self.visit_poly_trait_ref(bound);
                     self.current_index.shift_out(1);
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 a6ecd1cc987..8541621b23b 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
@@ -599,7 +599,7 @@ impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> {
             _,
         ) = t.kind
         {
-            for (ptr, _) in poly_trait_refs {
+            for ptr in poly_trait_refs {
                 if Some(self.1) == ptr.trait_ref.trait_def_id() {
                     self.0.push(ptr.span);
                 }
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs
index cf0ab630f2e..62204f63dd0 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs
@@ -894,7 +894,9 @@ fn foo(&self) -> Self::T { String::new() }
         // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting.
 
         let trait_bounds = bounds.iter().filter_map(|bound| match bound {
-            hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => Some(ptr),
+            hir::GenericBound::Trait(ptr) if ptr.modifiers == hir::TraitBoundModifier::None => {
+                Some(ptr)
+            }
             _ => None,
         });
 
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs
index 709b6eb18e3..fc2d0ba36f0 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs
@@ -740,9 +740,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     ) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| match (
                         left, right,
                     ) {
-                        (hir::GenericBound::Trait(tl, ml), hir::GenericBound::Trait(tr, mr))
+                        // FIXME: Suspicious
+                        (hir::GenericBound::Trait(tl), hir::GenericBound::Trait(tr))
                             if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
-                                && ml == mr =>
+                                && tl.modifiers == tr.modifiers =>
                         {
                             true
                         }
diff --git a/compiler/rustc_trait_selection/src/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/mod.rs
index cb7efeaae0b..82695688ae8 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/mod.rs
@@ -2,7 +2,7 @@ use std::ops::Deref;
 
 use rustc_errors::DiagCtxtHandle;
 use rustc_infer::infer::InferCtxt;
-use rustc_infer::traits::PredicateObligation;
+use rustc_infer::traits::PredicateObligations;
 use rustc_macros::extension;
 use rustc_middle::bug;
 use rustc_middle::ty::{self, Ty};
@@ -28,8 +28,7 @@ pub struct TypeErrCtxt<'a, 'tcx> {
 
     pub normalize_fn_sig: Box<dyn Fn(ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx> + 'a>,
 
-    pub autoderef_steps:
-        Box<dyn Fn(Ty<'tcx>) -> Vec<(Ty<'tcx>, Vec<PredicateObligation<'tcx>>)> + 'a>,
+    pub autoderef_steps: Box<dyn Fn(Ty<'tcx>) -> Vec<(Ty<'tcx>, PredicateObligations<'tcx>)> + 'a>,
 }
 
 #[extension(pub trait InferCtxtErrorExt<'tcx>)]
@@ -45,7 +44,7 @@ impl<'tcx> InferCtxt<'tcx> {
             normalize_fn_sig: Box::new(|fn_sig| fn_sig),
             autoderef_steps: Box::new(|ty| {
                 debug_assert!(false, "shouldn't be using autoderef_steps outside of typeck");
-                vec![(ty, vec![])]
+                vec![(ty, PredicateObligations::new())]
             }),
         }
     }
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 824c25db07d..5076152dbff 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
@@ -202,7 +202,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             notes,
                             parent_label,
                             append_const_msg,
-                        } = self.on_unimplemented_note(main_trait_ref, o, &mut long_ty_file);
+                        } = self.on_unimplemented_note(main_trait_predicate, o, &mut long_ty_file);
 
                         let have_alt_message = message.is_some() || label.is_some();
                         let is_try_conversion = self.is_try_conversion(span, main_trait_ref.def_id());
@@ -710,7 +710,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     None,
                     TypeError::Sorts(ty::error::ExpectedFound::new(true, expected_ty, ct_ty)),
                     false,
-                    false,
                 );
                 diag
             }
@@ -1435,6 +1434,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                                 cx.into_buffer()
                             }
                         ))),
+                        true,
                     )),
                     _ => None,
                 }
@@ -1452,7 +1452,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     ))
                 }),
                 err,
-                true,
                 false,
             );
             self.note_obligation_cause(&mut diag, obligation);
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 becc1acfb66..ba57909fc23 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
@@ -439,7 +439,7 @@ pub fn report_dyn_incompatibility<'tcx>(
         if tcx.parent_hir_node(hir_id).fn_sig().is_some() {
             // Do not suggest `impl Trait` when dealing with things like super-traits.
             err.span_suggestion_verbose(
-                ty.span.until(trait_ref.0.span),
+                ty.span.until(trait_ref.span),
                 "consider using an opaque type instead",
                 "impl ",
                 Applicability::MaybeIncorrect,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
index c3100c48b0a..cd41ab9fa6c 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
@@ -9,7 +9,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_macros::LintDiagnostic;
 use rustc_middle::bug;
 use rustc_middle::ty::print::PrintTraitRefExt as _;
-use rustc_middle::ty::{self, GenericArgsRef, GenericParamDefKind, TyCtxt};
+use rustc_middle::ty::{self, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, TyCtxt};
 use rustc_parse_format::{ParseMode, Parser, Piece, Position};
 use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
 use rustc_span::Span;
@@ -108,14 +108,18 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     pub fn on_unimplemented_note(
         &self,
-        trait_ref: ty::PolyTraitRef<'tcx>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
         obligation: &PredicateObligation<'tcx>,
         long_ty_file: &mut Option<PathBuf>,
     ) -> OnUnimplementedNote {
+        if trait_pred.polarity() != ty::PredicatePolarity::Positive {
+            return OnUnimplementedNote::default();
+        }
+
         let (def_id, args) = self
-            .impl_similar_to(trait_ref, obligation)
-            .unwrap_or_else(|| (trait_ref.def_id(), trait_ref.skip_binder().args));
-        let trait_ref = trait_ref.skip_binder();
+            .impl_similar_to(trait_pred.to_poly_trait_ref(), obligation)
+            .unwrap_or_else(|| (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args));
+        let trait_pred = trait_pred.skip_binder();
 
         let mut flags = vec![];
         // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): HIR is not present for RPITITs,
@@ -144,13 +148,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             flags.push((sym::cause, Some("MainFunctionType".to_string())));
         }
 
-        flags.push((sym::Trait, Some(trait_ref.print_trait_sugared().to_string())));
+        flags.push((sym::Trait, Some(trait_pred.trait_ref.print_trait_sugared().to_string())));
 
         // Add all types without trimmed paths or visible paths, ensuring they end up with
         // their "canonical" def path.
         ty::print::with_no_trimmed_paths!(ty::print::with_no_visible_paths!({
             let generics = self.tcx.generics_of(def_id);
-            let self_ty = trait_ref.self_ty();
+            let self_ty = trait_pred.self_ty();
             // This is also included through the generics list as `Self`,
             // but the parser won't allow you to use it
             flags.push((sym::_Self, Some(self_ty.to_string())));
@@ -266,7 +270,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }));
 
         if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, def_id) {
-            command.evaluate(self.tcx, trait_ref, &flags, long_ty_file)
+            command.evaluate(self.tcx, trait_pred.trait_ref, &flags, long_ty_file)
         } else {
             OnUnimplementedNote::default()
         }
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 87834c329e1..733baaa99e5 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -3074,11 +3074,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     match ty.kind {
                         hir::TyKind::TraitObject(traits, _, _) => {
                             let (span, kw) = match traits {
-                                [(first, _), ..] if first.span.lo() == ty.span.lo() => {
+                                [first, ..] if first.span.lo() == ty.span.lo() => {
                                     // Missing `dyn` in front of trait object.
                                     (ty.span.shrink_to_lo(), "dyn ")
                                 }
-                                [(first, _), ..] => (ty.span.until(first.span), ""),
+                                [first, ..] => (ty.span.until(first.span), ""),
                                 [] => span_bug!(ty.span, "trait object with no traits: {ty:?}"),
                             };
                             let needs_parens = traits.len() != 1;
@@ -5162,7 +5162,7 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>(
     let async_span = tcx.sess.source_map().span_extend_while_whitespace(async_span);
 
     let future = tcx.hir_node_by_def_id(opaque_def_id).expect_opaque_ty();
-    let [hir::GenericBound::Trait(trait_ref, _)] = future.bounds else {
+    let [hir::GenericBound::Trait(trait_ref)] = future.bounds else {
         // `async fn` should always lower to a single bound... but don't ICE.
         return None;
     };
diff --git a/compiler/rustc_trait_selection/src/solve.rs b/compiler/rustc_trait_selection/src/solve.rs
index e47f5389cd1..d425ab50ae0 100644
--- a/compiler/rustc_trait_selection/src/solve.rs
+++ b/compiler/rustc_trait_selection/src/solve.rs
@@ -6,6 +6,7 @@ pub mod inspect;
 mod normalize;
 mod select;
 
+pub(crate) use delegate::SolverDelegate;
 pub use fulfill::{FulfillmentCtxt, NextSolverError};
 pub(crate) use normalize::deeply_normalize_for_diagnostics;
 pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes};
diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs
index 5c344930314..df9ac2b80fd 100644
--- a/compiler/rustc_trait_selection/src/solve/delegate.rs
+++ b/compiler/rustc_trait_selection/src/solve/delegate.rs
@@ -223,6 +223,8 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
         if eligible { Ok(Some(node_item.item.def_id)) } else { Ok(None) }
     }
 
+    // FIXME: This actually should destructure the `Result` we get from transmutability and
+    // register candidates. We probably need to register >1 since we may have an OR of ANDs.
     fn is_transmutable(
         &self,
         param_env: ty::ParamEnv<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index c6e3ba3c957..53e5420d27a 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -2,18 +2,19 @@ use std::marker::PhantomData;
 use std::mem;
 use std::ops::ControlFlow;
 
+use rustc_data_structures::thinvec::ExtractIf;
 use rustc_infer::infer::InferCtxt;
 use rustc_infer::traits::query::NoSolution;
 use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause};
 use rustc_infer::traits::{
     self, FromSolverError, MismatchedProjectionTypes, Obligation, ObligationCause,
-    ObligationCauseCode, PredicateObligation, SelectionError, TraitEngine,
+    ObligationCauseCode, PredicateObligation, PredicateObligations, SelectionError, TraitEngine,
 };
 use rustc_middle::bug;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::{self, TyCtxt};
-use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _};
-use tracing::instrument;
+use rustc_next_trait_solver::solve::{GenerateProofTree, HasChanged, SolverDelegateEvalExt as _};
+use tracing::{instrument, trace};
 
 use super::Certainty;
 use super::delegate::SolverDelegate;
@@ -49,8 +50,8 @@ struct ObligationStorage<'tcx> {
     /// We cannot eagerly return these as error so we instead store them here
     /// to avoid recomputing them each time `select_where_possible` is called.
     /// This also allows us to return the correct `FulfillmentError` for them.
-    overflowed: Vec<PredicateObligation<'tcx>>,
-    pending: Vec<PredicateObligation<'tcx>>,
+    overflowed: PredicateObligations<'tcx>,
+    pending: PredicateObligations<'tcx>,
 }
 
 impl<'tcx> ObligationStorage<'tcx> {
@@ -58,13 +59,13 @@ impl<'tcx> ObligationStorage<'tcx> {
         self.pending.push(obligation);
     }
 
-    fn clone_pending(&self) -> Vec<PredicateObligation<'tcx>> {
+    fn clone_pending(&self) -> PredicateObligations<'tcx> {
         let mut obligations = self.pending.clone();
         obligations.extend(self.overflowed.iter().cloned());
         obligations
     }
 
-    fn take_pending(&mut self) -> Vec<PredicateObligation<'tcx>> {
+    fn take_pending(&mut self) -> PredicateObligations<'tcx> {
         let mut obligations = mem::take(&mut self.pending);
         obligations.append(&mut self.overflowed);
         obligations
@@ -81,15 +82,13 @@ impl<'tcx> ObligationStorage<'tcx> {
             // we get all obligations involved in the overflow. We pretty much check: if
             // we were to do another step of `select_where_possible`, which goals would
             // change.
-            self.overflowed.extend(self.pending.extract_if(|o| {
+            // FIXME: <https://github.com/Gankra/thin-vec/pull/66> is merged, this can be removed.
+            self.overflowed.extend(ExtractIf::new(&mut self.pending, |o| {
                 let goal = o.clone().into();
                 let result = <&SolverDelegate<'tcx>>::from(infcx)
                     .evaluate_root_goal(goal, GenerateProofTree::No)
                     .0;
-                match result {
-                    Ok((has_changed, _)) => has_changed,
-                    _ => false,
-                }
+                matches!(result, Ok((HasChanged::Yes, _)))
             }));
         })
     }
@@ -113,7 +112,7 @@ impl<'tcx, E: 'tcx> FulfillmentCtxt<'tcx, E> {
         &self,
         infcx: &InferCtxt<'tcx>,
         obligation: &PredicateObligation<'tcx>,
-        result: &Result<(bool, Certainty), NoSolution>,
+        result: &Result<(HasChanged, Certainty), NoSolution>,
     ) {
         if let Some(inspector) = infcx.obligation_inspector.get() {
             let result = match result {
@@ -181,7 +180,11 @@ where
                         continue;
                     }
                 };
-                has_changed |= changed;
+
+                if changed == HasChanged::Yes {
+                    has_changed = true;
+                }
+
                 match certainty {
                     Certainty::Yes => {}
                     Certainty::Maybe(_) => self.obligations.register(obligation),
@@ -196,14 +199,11 @@ where
         errors
     }
 
-    fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
+    fn pending_obligations(&self) -> PredicateObligations<'tcx> {
         self.obligations.clone_pending()
     }
 
-    fn drain_unstalled_obligations(
-        &mut self,
-        _: &InferCtxt<'tcx>,
-    ) -> Vec<PredicateObligation<'tcx>> {
+    fn drain_unstalled_obligations(&mut self, _: &InferCtxt<'tcx>) -> PredicateObligations<'tcx> {
         self.obligations.take_pending()
     }
 }
@@ -401,6 +401,7 @@ impl<'tcx> BestObligation<'tcx> {
                                         nested_goal.source(),
                                         GoalSource::ImplWhereBound
                                             | GoalSource::InstantiateHigherRanked
+                                            | GoalSource::AliasWellFormed
                                     ) && match self.consider_ambiguities {
                                         true => {
                                             matches!(
@@ -415,6 +416,13 @@ impl<'tcx> BestObligation<'tcx> {
                         })
                     });
                 }
+
+                // Prefer a non-rigid candidate if there is one.
+                if candidates.len() > 1 {
+                    candidates.retain(|candidate| {
+                        !matches!(candidate.kind(), inspect::ProbeKind::RigidAlias { .. })
+                    });
+                }
             }
         }
 
@@ -429,8 +437,11 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
         self.obligation.cause.span
     }
 
+    #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))]
     fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
         let candidates = self.non_trivial_candidates(goal);
+        trace!(candidates = ?candidates.iter().map(|c| c.kind()).collect::<Vec<_>>());
+
         let [candidate] = candidates.as_slice() else {
             return ControlFlow::Break(self.obligation.clone());
         };
@@ -464,17 +475,13 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
                     polarity: ty::PredicatePolarity::Positive,
                 }))
             }
-            ty::PredicateKind::Clause(
-                ty::ClauseKind::WellFormed(_) | ty::ClauseKind::Projection(..),
-            )
-            | ty::PredicateKind::AliasRelate(..) => ChildMode::PassThrough,
-            _ => {
-                return ControlFlow::Break(self.obligation.clone());
-            }
+            _ => ChildMode::PassThrough,
         };
 
         let mut impl_where_bound_count = 0;
         for nested_goal in candidate.instantiate_nested_goals(self.span()) {
+            trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));
+
             let make_obligation = |cause| Obligation {
                 cause,
                 param_env: nested_goal.goal().param_env,
@@ -501,7 +508,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
                 (_, GoalSource::InstantiateHigherRanked) => {
                     obligation = self.obligation.clone();
                 }
-                (ChildMode::PassThrough, _) => {
+                (ChildMode::PassThrough, _) | (_, GoalSource::AliasWellFormed) => {
                     obligation = make_obligation(self.obligation.cause.clone());
                 }
             }
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
index 254620e0b59..4975a9ce0c7 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
@@ -292,7 +292,8 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
                         | inspect::ProbeKind::Root { .. }
                         | inspect::ProbeKind::TryNormalizeNonRigid { .. }
                         | inspect::ProbeKind::TraitCandidate { .. }
-                        | inspect::ProbeKind::OpaqueTypeStorageLookup { .. } => {
+                        | inspect::ProbeKind::OpaqueTypeStorageLookup { .. }
+                        | inspect::ProbeKind::RigidAlias { .. } => {
                             // Nested probes have to prove goals added in their parent
                             // but do not leak them, so we truncate the added goals
                             // afterwards.
@@ -316,7 +317,8 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
             inspect::ProbeKind::Root { result }
             | inspect::ProbeKind::TryNormalizeNonRigid { result }
             | inspect::ProbeKind::TraitCandidate { source: _, result }
-            | inspect::ProbeKind::OpaqueTypeStorageLookup { result } => {
+            | inspect::ProbeKind::OpaqueTypeStorageLookup { result }
+            | inspect::ProbeKind::RigidAlias { result } => {
                 // We only add a candidate if `shallow_certainty` was set, which means
                 // that we ended up calling `evaluate_added_goals_and_make_canonical_response`.
                 if let Some(shallow_certainty) = shallow_certainty {
diff --git a/compiler/rustc_trait_selection/src/solve/select.rs b/compiler/rustc_trait_selection/src/solve/select.rs
index 257fd263b94..1661852903c 100644
--- a/compiler/rustc_trait_selection/src/solve/select.rs
+++ b/compiler/rustc_trait_selection/src/solve/select.rs
@@ -177,7 +177,8 @@ fn to_selection<'tcx>(
         | ProbeKind::UpcastProjectionCompatibility
         | ProbeKind::OpaqueTypeStorageLookup { result: _ }
         | ProbeKind::Root { result: _ }
-        | ProbeKind::ShadowedEnvProbing => {
+        | ProbeKind::ShadowedEnvProbing
+        | ProbeKind::RigidAlias { result: _ } => {
             span_bug!(span, "didn't expect to assemble trait candidate from {:#?}", cand.kind())
         }
     })
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 27d2a3c15b9..f4a2483cebf 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -11,6 +11,7 @@ use rustc_errors::{Diag, EmissionGuarantee};
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
+use rustc_infer::traits::PredicateObligations;
 use rustc_middle::bug;
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal};
@@ -19,6 +20,7 @@ use rustc_middle::ty::fast_reject::DeepRejectCtxt;
 use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 pub use rustc_next_trait_solver::coherence::*;
+use rustc_next_trait_solver::solve::SolverDelegateEvalExt;
 use rustc_span::symbol::sym;
 use rustc_span::{DUMMY_SP, Span};
 use tracing::{debug, instrument, warn};
@@ -28,7 +30,7 @@ use crate::error_reporting::traits::suggest_new_overflow_limit;
 use crate::infer::InferOk;
 use crate::infer::outlives::env::OutlivesEnvironment;
 use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor};
-use crate::solve::{deeply_normalize_for_diagnostics, inspect};
+use crate::solve::{SolverDelegate, deeply_normalize_for_diagnostics, inspect};
 use crate::traits::query::evaluate_obligation::InferCtxtExt;
 use crate::traits::select::IntercrateAmbiguityCause;
 use crate::traits::{
@@ -278,7 +280,7 @@ fn equate_impl_headers<'tcx>(
     param_env: ty::ParamEnv<'tcx>,
     impl1: &ty::ImplHeader<'tcx>,
     impl2: &ty::ImplHeader<'tcx>,
-) -> Option<Vec<PredicateObligation<'tcx>>> {
+) -> Option<PredicateObligations<'tcx>> {
     let result =
         match (impl1.trait_ref, impl2.trait_ref) {
             (Some(impl1_ref), Some(impl2_ref)) => infcx
@@ -333,6 +335,16 @@ fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>(
     let infcx = selcx.infcx;
 
     if infcx.next_trait_solver() {
+        // A fast path optimization, try evaluating all goals with
+        // a very low recursion depth and bail if any of them don't
+        // hold.
+        if !obligations.iter().all(|o| {
+            <&SolverDelegate<'tcx>>::from(infcx)
+                .root_goal_may_hold_with_depth(8, Goal::new(infcx.tcx, o.param_env, o.predicate))
+        }) {
+            return IntersectionHasImpossibleObligations::Yes;
+        }
+
         let ocx = ObligationCtxt::new_with_diagnostics(infcx);
         ocx.register_obligations(obligations.iter().cloned());
         let errors_and_ambiguities = ocx.select_all_or_error();
@@ -480,7 +492,7 @@ fn plug_infer_with_placeholders<'tcx>(
                 else {
                     bug!("we always expect to be able to plug an infer var with placeholder")
                 };
-                assert_eq!(obligations, &[]);
+                assert_eq!(obligations.len(), 0);
             } else {
                 ty.super_visit_with(self);
             }
@@ -503,7 +515,7 @@ fn plug_infer_with_placeholders<'tcx>(
                 else {
                     bug!("we always expect to be able to plug an infer var with placeholder")
                 };
-                assert_eq!(obligations, &[]);
+                assert_eq!(obligations.len(), 0);
             } else {
                 ct.super_visit_with(self);
             }
@@ -534,7 +546,7 @@ fn plug_infer_with_placeholders<'tcx>(
                     else {
                         bug!("we always expect to be able to plug an infer var with placeholder")
                     };
-                    assert_eq!(obligations, &[]);
+                    assert_eq!(obligations.len(), 0);
                 }
             }
         }
diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
index d5d7681a8d6..364a13b3a75 100644
--- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
+++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
@@ -1,12 +1,8 @@
-//! "Object safety" refers to the ability for a trait to be converted
-//! to an object. In general, traits may only be converted to an
-//! object if all of their methods meet certain criteria. In particular,
-//! they must:
+//! "Dyn-compatibility"[^1] refers to the ability for a trait to be converted
+//! to a trait object. In general, traits may only be converted to a trait
+//! object if certain criteria are met.
 //!
-//!   - have a suitable receiver from which we can extract a vtable and coerce to a "thin" version
-//!     that doesn't contain the vtable;
-//!   - not reference the erased type `Self` except for in this receiver;
-//!   - not have generic type parameters.
+//! [^1]: Formerly known as "object safety".
 
 use std::iter;
 use std::ops::ControlFlow;
@@ -129,7 +125,7 @@ fn sized_trait_bound_spans<'tcx>(
     bounds: hir::GenericBounds<'tcx>,
 ) -> impl 'tcx + Iterator<Item = Span> {
     bounds.iter().filter_map(move |b| match b {
-        hir::GenericBound::Trait(trait_ref, hir::TraitBoundModifier::None)
+        hir::GenericBound::Trait(trait_ref)
             if trait_has_sized_self(
                 tcx,
                 trait_ref.trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()),
@@ -506,8 +502,8 @@ fn virtual_call_violations_for_method<'tcx>(
 
 /// This code checks that `receiver_is_dispatchable` is correctly implemented.
 ///
-/// This check is outlined from the object safety check to avoid cycles with
-/// layout computation, which relies on knowing whether methods are object safe.
+/// This check is outlined from the dyn-compatibility check to avoid cycles with
+/// layout computation, which relies on knowing whether methods are dyn-compatible.
 fn check_receiver_correct<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, method: ty::AssocItem) {
     if !is_vtable_safe_method(tcx, trait_def_id, method) {
         return;
@@ -643,8 +639,8 @@ fn object_ty_for_trait<'tcx>(
 /// contained by the trait object, because the object that needs to be coerced is behind
 /// a pointer.
 ///
-/// In practice, we cannot use `dyn Trait` explicitly in the obligation because it would result
-/// in a new check that `Trait` is object safe, creating a cycle (until object_safe_for_dispatch
+/// In practice, we cannot use `dyn Trait` explicitly in the obligation because it would result in
+/// a new check that `Trait` is dyn-compatible, creating a cycle (until dyn_compatible_for_dispatch
 /// is stabilized, see tracking issue <https://github.com/rust-lang/rust/issues/43561>).
 /// Instead, we fudge a little by introducing a new type parameter `U` such that
 /// `Self: Unsize<U>` and `U: Trait + ?Sized`, and use `U` in place of `dyn Trait`.
@@ -678,7 +674,7 @@ fn receiver_is_dispatchable<'tcx>(
 
     // the type `U` in the query
     // use a bogus type parameter to mimic a forall(U) query using u32::MAX for now.
-    // FIXME(mikeyhew) this is a total hack. Once object_safe_for_dispatch is stabilized, we can
+    // FIXME(mikeyhew) this is a total hack. Once dyn_compatible_for_dispatch is stabilized, we can
     // replace this with `dyn Trait`
     let unsized_self_ty: Ty<'tcx> =
         Ty::new_param(tcx, u32::MAX, Symbol::intern("RustaceansAreAwesome"));
@@ -865,7 +861,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IllegalSelfTypeVisitor<'tcx> {
     }
 
     fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result {
-        // Constants can only influence object safety if they are generic and reference `Self`.
+        // Constants can only influence dyn-compatibility if they are generic and reference `Self`.
         // This is only possible for unevaluated constants, so we walk these here.
         self.tcx.expand_abstract_consts(ct).super_visit_with(self)
     }
diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs
index d562692c1a8..a45ec8b3c44 100644
--- a/compiler/rustc_trait_selection/src/traits/engine.rs
+++ b/compiler/rustc_trait_selection/src/traits/engine.rs
@@ -10,6 +10,7 @@ use rustc_infer::infer::canonical::{
 };
 use rustc_infer::infer::outlives::env::OutlivesEnvironment;
 use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError, TypeTrace};
+use rustc_infer::traits::PredicateObligations;
 use rustc_macros::extension;
 use rustc_middle::arena::ArenaAllocatable;
 use rustc_middle::traits::query::NoSolution;
@@ -36,10 +37,8 @@ where
         if infcx.next_trait_solver() {
             Box::new(NextFulfillmentCtxt::new(infcx))
         } else {
-            let new_solver_globally =
-                infcx.tcx.sess.opts.unstable_opts.next_solver.map_or(false, |c| c.globally);
             assert!(
-                !new_solver_globally,
+                !infcx.tcx.next_trait_solver_globally(),
                 "using old solver even though new solver is enabled globally"
             );
             Box::new(FulfillmentContext::new(infcx))
@@ -208,7 +207,7 @@ where
     /// getting ignored. You can make a new `ObligationCtxt` if this
     /// needs to be done in a loop, for example.
     #[must_use]
-    pub fn into_pending_obligations(self) -> Vec<PredicateObligation<'tcx>> {
+    pub fn into_pending_obligations(self) -> PredicateObligations<'tcx> {
         self.engine.borrow().pending_obligations()
     }
 
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 33b8cf03701..e56f1866970 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -6,13 +6,15 @@ use rustc_data_structures::obligation_forest::{
 };
 use rustc_infer::infer::DefineOpaqueTypes;
 use rustc_infer::traits::{
-    FromSolverError, PolyTraitObligation, ProjectionCacheKey, SelectionError, TraitEngine,
+    FromSolverError, PolyTraitObligation, PredicateObligations, ProjectionCacheKey, SelectionError,
+    TraitEngine,
 };
 use rustc_middle::bug;
 use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::ty::abstract_const::NotConstEvaluatable;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::{self, Binder, Const, GenericArgsRef, TypeVisitableExt};
+use thin_vec::ThinVec;
 use tracing::{debug, debug_span, instrument};
 
 use super::project::{self, ProjectAndUnifyResult};
@@ -27,6 +29,8 @@ use crate::traits::normalize::normalize_with_depth_to;
 use crate::traits::project::{PolyProjectionObligation, ProjectionCacheKeyExt as _};
 use crate::traits::query::evaluate_obligation::InferCtxtExt;
 
+pub(crate) type PendingPredicateObligations<'tcx> = ThinVec<PendingPredicateObligation<'tcx>>;
+
 impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> {
     /// Note that we include both the `ParamEnv` and the `Predicate`,
     /// as the `ParamEnv` can influence whether fulfillment succeeds
@@ -161,15 +165,16 @@ where
     fn drain_unstalled_obligations(
         &mut self,
         infcx: &InferCtxt<'tcx>,
-    ) -> Vec<PredicateObligation<'tcx>> {
-        let mut processor = DrainProcessor { removed_predicates: Vec::new(), infcx };
+    ) -> PredicateObligations<'tcx> {
+        let mut processor =
+            DrainProcessor { removed_predicates: PredicateObligations::new(), infcx };
         let outcome: Outcome<_, _> = self.predicates.process_obligations(&mut processor);
         assert!(outcome.errors.is_empty());
         return processor.removed_predicates;
 
         struct DrainProcessor<'a, 'tcx> {
             infcx: &'a InferCtxt<'tcx>,
-            removed_predicates: Vec<PredicateObligation<'tcx>>,
+            removed_predicates: PredicateObligations<'tcx>,
         }
 
         impl<'tcx> ObligationProcessor for DrainProcessor<'_, 'tcx> {
@@ -190,7 +195,7 @@ where
             ) -> ProcessResult<PendingPredicateObligation<'tcx>, !> {
                 assert!(self.needs_process_obligation(pending_obligation));
                 self.removed_predicates.push(pending_obligation.obligation.clone());
-                ProcessResult::Changed(vec![])
+                ProcessResult::Changed(Default::default())
             }
 
             fn process_backedge<'c, I>(
@@ -207,7 +212,7 @@ where
         }
     }
 
-    fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
+    fn pending_obligations(&self) -> PredicateObligations<'tcx> {
         self.predicates.map_pending_obligations(|o| o.obligation.clone())
     }
 }
@@ -216,7 +221,7 @@ struct FulfillProcessor<'a, 'tcx> {
     selcx: SelectionContext<'a, 'tcx>,
 }
 
-fn mk_pending(os: Vec<PredicateObligation<'_>>) -> Vec<PendingPredicateObligation<'_>> {
+fn mk_pending<'tcx>(os: PredicateObligations<'tcx>) -> PendingPredicateObligations<'tcx> {
     os.into_iter()
         .map(|o| PendingPredicateObligation { obligation: o, stalled_on: vec![] })
         .collect()
@@ -321,7 +326,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
         let infcx = self.selcx.infcx;
 
         if obligation.predicate.has_aliases() {
-            let mut obligations = Vec::new();
+            let mut obligations = PredicateObligations::new();
             let predicate = normalize_with_depth_to(
                 &mut self.selcx,
                 obligation.param_env,
@@ -369,7 +374,10 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                 | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
                 | ty::PredicateKind::ConstEquate(..) => {
                     let pred = ty::Binder::dummy(infcx.enter_forall_and_leak_universe(binder));
-                    ProcessResult::Changed(mk_pending(vec![obligation.with(infcx.tcx, pred)]))
+                    let mut obligations = PredicateObligations::with_capacity(1);
+                    obligations.push(obligation.with(infcx.tcx, pred));
+
+                    ProcessResult::Changed(mk_pending(obligations))
                 }
                 ty::PredicateKind::Ambiguous => ProcessResult::Unchanged,
                 ty::PredicateKind::NormalizesTo(..) => {
@@ -395,7 +403,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                         infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data));
                     }
 
-                    ProcessResult::Changed(vec![])
+                    ProcessResult::Changed(Default::default())
                 }
 
                 ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
@@ -405,7 +413,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                     if infcx.considering_regions {
                         infcx.register_region_obligation_with_cause(t_a, r_b, &obligation.cause);
                     }
-                    ProcessResult::Changed(vec![])
+                    ProcessResult::Changed(Default::default())
                 }
 
                 ty::PredicateKind::Clause(ty::ClauseKind::Projection(ref data)) => {
@@ -422,7 +430,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                     if !self.selcx.tcx().is_dyn_compatible(trait_def_id) {
                         ProcessResult::Error(FulfillmentErrorCode::Select(Unimplemented))
                     } else {
-                        ProcessResult::Changed(vec![])
+                        ProcessResult::Changed(Default::default())
                     }
                 }
 
@@ -451,7 +459,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                             pending_obligation.stalled_on.extend([var]);
                             return ProcessResult::Unchanged;
                         }
-                        ty::ConstKind::Error(_) => return ProcessResult::Changed(vec![]),
+                        ty::ConstKind::Error(_) => {
+                            return ProcessResult::Changed(PendingPredicateObligations::new());
+                        }
                         ty::ConstKind::Value(ty, _) => ty,
                         ty::ConstKind::Unevaluated(uv) => {
                             infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args)
@@ -460,7 +470,7 @@ 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(vec![]));
+                            return ProcessResult::Changed(mk_pending(PredicateObligations::new()));
                         }
                         ty::ConstKind::Placeholder(_) => {
                             bug!("placeholder const {:?} in old solver", ct)
@@ -568,7 +578,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                         obligation.param_env,
                         obligation.cause.span,
                     ) {
-                        Ok(()) => ProcessResult::Changed(vec![]),
+                        Ok(()) => ProcessResult::Changed(Default::default()),
                         Err(NotConstEvaluatable::MentionsInfer) => {
                             pending_obligation.stalled_on.clear();
                             pending_obligation.stalled_on.extend(
@@ -722,7 +732,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
             debug!("process_child_obligations: coinductive match");
             Ok(())
         } else {
-            let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect();
+            let cycle = cycle.map(|c| c.obligation.clone()).collect();
             Err(FulfillmentErrorCode::Cycle(cycle))
         }
     }
@@ -745,7 +755,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> {
                     "selecting trait at depth {} evaluated to holds",
                     obligation.recursion_depth
                 );
-                return ProcessResult::Changed(vec![]);
+                return ProcessResult::Changed(Default::default());
             }
         }
 
@@ -809,7 +819,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> {
                         .projection_cache()
                         .complete(key, EvaluationResult::EvaluatedToOk);
                 }
-                return ProcessResult::Changed(vec![]);
+                return ProcessResult::Changed(Default::default());
             } else {
                 debug!("Does NOT hold: {:?}", obligation);
             }
@@ -826,9 +836,12 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> {
                 ProcessResult::Unchanged
             }
             // Let the caller handle the recursion
-            ProjectAndUnifyResult::Recursive => ProcessResult::Changed(mk_pending(vec![
-                project_obligation.with(tcx, project_obligation.predicate),
-            ])),
+            ProjectAndUnifyResult::Recursive => {
+                let mut obligations = PredicateObligations::with_capacity(1);
+                obligations.push(project_obligation.with(tcx, project_obligation.predicate));
+
+                ProcessResult::Changed(mk_pending(obligations))
+            }
             ProjectAndUnifyResult::MismatchedProjectionTypes(e) => {
                 ProcessResult::Error(FulfillmentErrorCode::Project(e))
             }
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 655bef0bab7..0fb795fc184 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -114,7 +114,7 @@ impl<'tcx> Debug for FulfillmentError<'tcx> {
 pub enum FulfillmentErrorCode<'tcx> {
     /// Inherently impossible to fulfill; this trait is implemented if and only
     /// if it is already implemented.
-    Cycle(Vec<PredicateObligation<'tcx>>),
+    Cycle(PredicateObligations<'tcx>),
     Select(SelectionError<'tcx>),
     Project(MismatchedProjectionTypes<'tcx>),
     Subtype(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate
diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs
index a7130cbd28f..7eac3559348 100644
--- a/compiler/rustc_trait_selection/src/traits/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/normalize.rs
@@ -4,7 +4,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_infer::infer::InferOk;
 use rustc_infer::infer::at::At;
 use rustc_infer::traits::{
-    FromSolverError, Normalized, Obligation, PredicateObligation, TraitEngine,
+    FromSolverError, Normalized, Obligation, PredicateObligations, TraitEngine,
 };
 use rustc_macros::extension;
 use rustc_middle::traits::{ObligationCause, ObligationCauseCode, Reveal};
@@ -29,7 +29,7 @@ impl<'tcx> At<'_, 'tcx> {
     /// projection may be fallible.
     fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> InferOk<'tcx, T> {
         if self.infcx.next_trait_solver() {
-            InferOk { value, obligations: Vec::new() }
+            InferOk { value, obligations: PredicateObligations::new() }
         } else {
             let mut selcx = SelectionContext::new(self.infcx);
             let Normalized { value, obligations } =
@@ -83,7 +83,7 @@ pub(crate) fn normalize_with_depth<'a, 'b, 'tcx, T>(
 where
     T: TypeFoldable<TyCtxt<'tcx>>,
 {
-    let mut obligations = Vec::new();
+    let mut obligations = PredicateObligations::new();
     let value = normalize_with_depth_to(selcx, param_env, cause, depth, value, &mut obligations);
     Normalized { value, obligations }
 }
@@ -95,14 +95,14 @@ pub(crate) fn normalize_with_depth_to<'a, 'b, 'tcx, T>(
     cause: ObligationCause<'tcx>,
     depth: usize,
     value: T,
-    obligations: &mut Vec<PredicateObligation<'tcx>>,
+    obligations: &mut PredicateObligations<'tcx>,
 ) -> T
 where
     T: TypeFoldable<TyCtxt<'tcx>>,
 {
     debug!(obligations.len = obligations.len());
     let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth, obligations);
-    let result = ensure_sufficient_stack(|| normalizer.fold(value));
+    let result = ensure_sufficient_stack(|| AssocTypeNormalizer::fold(&mut normalizer, value));
     debug!(?result, obligations.len = normalizer.obligations.len());
     debug!(?normalizer.obligations,);
     result
@@ -128,7 +128,7 @@ struct AssocTypeNormalizer<'a, 'b, 'tcx> {
     selcx: &'a mut SelectionContext<'b, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     cause: ObligationCause<'tcx>,
-    obligations: &'a mut Vec<PredicateObligation<'tcx>>,
+    obligations: &'a mut PredicateObligations<'tcx>,
     depth: usize,
     universes: Vec<Option<ty::UniverseIndex>>,
 }
@@ -139,7 +139,7 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
         param_env: ty::ParamEnv<'tcx>,
         cause: ObligationCause<'tcx>,
         depth: usize,
-        obligations: &'a mut Vec<PredicateObligation<'tcx>>,
+        obligations: &'a mut PredicateObligations<'tcx>,
     ) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
         debug_assert!(!selcx.infcx.next_trait_solver());
         AssocTypeNormalizer { selcx, param_env, cause, obligations, depth, universes: vec![] }
diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
index f1faff2c036..339e4bf1f22 100644
--- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
@@ -77,7 +77,7 @@ fn implied_outlives_bounds<'a, 'tcx>(
     else {
         return vec![];
     };
-    assert_eq!(&obligations, &[]);
+    assert_eq!(obligations.len(), 0);
 
     // Because of #109628, we may have unexpected placeholders. Ignore them!
     // FIXME(#109628): panic in this case once the issue is fixed.
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 9cd99d99fc3..7f7c9bced18 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -9,7 +9,7 @@ use rustc_hir::def::DefKind;
 use rustc_hir::lang_items::LangItem;
 use rustc_infer::infer::DefineOpaqueTypes;
 use rustc_infer::infer::resolve::OpportunisticRegionResolver;
-use rustc_infer::traits::ObligationCauseCode;
+use rustc_infer::traits::{ObligationCauseCode, PredicateObligations};
 pub use rustc_middle::traits::Reveal;
 use rustc_middle::traits::select::OverflowError;
 use rustc_middle::traits::{BuiltinImplSource, ImplSource, ImplSourceUserDefinedData};
@@ -146,7 +146,7 @@ impl<'tcx> ProjectionCandidateSet<'tcx> {
 /// of the old return type, which was:
 /// ```ignore (not-rust)
 /// Result<
-///     Result<Option<Vec<PredicateObligation<'tcx>>>, InProgress>,
+///     Result<Option<PredicateObligations<'tcx>>, InProgress>,
 ///     MismatchedProjectionTypes<'tcx>,
 /// >
 /// ```
@@ -155,7 +155,7 @@ pub(super) enum ProjectAndUnifyResult<'tcx> {
     /// projection cannot be normalized because the required trait bound does
     /// not hold, this is returned, with `obligations` being a predicate that
     /// cannot be proven.
-    Holds(Vec<PredicateObligation<'tcx>>),
+    Holds(PredicateObligations<'tcx>),
     /// The projection cannot be normalized due to ambiguity. Resolving some
     /// inference variables in the projection may fix this.
     FailedNormalization,
@@ -231,7 +231,7 @@ fn project_and_unify_term<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionObligation<'tcx>,
 ) -> ProjectAndUnifyResult<'tcx> {
-    let mut obligations = vec![];
+    let mut obligations = PredicateObligations::new();
 
     let infcx = selcx.infcx;
     let normalized = match opt_normalize_projection_term(
@@ -289,7 +289,7 @@ pub fn normalize_projection_ty<'a, 'b, 'tcx>(
     projection_ty: ty::AliasTy<'tcx>,
     cause: ObligationCause<'tcx>,
     depth: usize,
-    obligations: &mut Vec<PredicateObligation<'tcx>>,
+    obligations: &mut PredicateObligations<'tcx>,
 ) -> Term<'tcx> {
     opt_normalize_projection_term(
         selcx,
@@ -330,7 +330,7 @@ pub(super) fn opt_normalize_projection_term<'a, 'b, 'tcx>(
     projection_term: ty::AliasTerm<'tcx>,
     cause: ObligationCause<'tcx>,
     depth: usize,
-    obligations: &mut Vec<PredicateObligation<'tcx>>,
+    obligations: &mut PredicateObligations<'tcx>,
 ) -> Result<Option<Term<'tcx>>, InProgress> {
     let infcx = selcx.infcx;
     debug_assert!(!selcx.infcx.next_trait_solver());
@@ -452,7 +452,8 @@ pub(super) fn opt_normalize_projection_term<'a, 'b, 'tcx>(
             Ok(Some(result.value))
         }
         Ok(Projected::NoProgress(projected_ty)) => {
-            let result = Normalized { value: projected_ty, obligations: vec![] };
+            let result =
+                Normalized { value: projected_ty, obligations: PredicateObligations::new() };
             if use_cache {
                 infcx.inner.borrow_mut().projection_cache().insert_term(cache_key, result.clone());
             }
@@ -519,13 +520,14 @@ fn normalize_to_error<'a, 'tcx>(
             selcx.infcx.next_const_var(cause.span).into()
         }
     };
-    let trait_obligation = Obligation {
+    let mut obligations = PredicateObligations::new();
+    obligations.push(Obligation {
         cause,
         recursion_depth: depth,
         param_env,
         predicate: trait_ref.upcast(selcx.tcx()),
-    };
-    Normalized { value: new_value, obligations: vec![trait_obligation] }
+    });
+    Normalized { value: new_value, obligations }
 }
 
 /// Confirm and normalize the given inherent projection.
@@ -536,7 +538,7 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
     alias_ty: ty::AliasTy<'tcx>,
     cause: ObligationCause<'tcx>,
     depth: usize,
-    obligations: &mut Vec<PredicateObligation<'tcx>>,
+    obligations: &mut PredicateObligations<'tcx>,
 ) -> Ty<'tcx> {
     let tcx = selcx.tcx();
 
@@ -604,7 +606,7 @@ pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>(
     alias_ty: ty::AliasTy<'tcx>,
     cause: ObligationCause<'tcx>,
     depth: usize,
-    obligations: &mut Vec<PredicateObligation<'tcx>>,
+    obligations: &mut PredicateObligations<'tcx>,
 ) -> ty::GenericArgsRef<'tcx> {
     let tcx = selcx.tcx();
 
@@ -657,15 +659,15 @@ enum Projected<'tcx> {
 
 struct Progress<'tcx> {
     term: ty::Term<'tcx>,
-    obligations: Vec<PredicateObligation<'tcx>>,
+    obligations: PredicateObligations<'tcx>,
 }
 
 impl<'tcx> Progress<'tcx> {
     fn error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Self {
-        Progress { term: Ty::new_error(tcx, guar).into(), obligations: vec![] }
+        Progress { term: Ty::new_error(tcx, guar).into(), obligations: PredicateObligations::new() }
     }
 
-    fn with_addl_obligations(mut self, mut obligations: Vec<PredicateObligation<'tcx>>) -> Self {
+    fn with_addl_obligations(mut self, mut obligations: PredicateObligations<'tcx>) -> Self {
         self.obligations.append(&mut obligations);
         self
     }
@@ -1351,7 +1353,7 @@ fn confirm_select_candidate<'cx, 'tcx>(
 fn confirm_coroutine_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTermObligation<'tcx>,
-    nested: Vec<PredicateObligation<'tcx>>,
+    nested: PredicateObligations<'tcx>,
 ) -> Progress<'tcx> {
     let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
     let ty::Coroutine(_, args) = self_ty.kind() else {
@@ -1410,7 +1412,7 @@ fn confirm_coroutine_candidate<'cx, 'tcx>(
 fn confirm_future_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTermObligation<'tcx>,
-    nested: Vec<PredicateObligation<'tcx>>,
+    nested: PredicateObligations<'tcx>,
 ) -> Progress<'tcx> {
     let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
     let ty::Coroutine(_, args) = self_ty.kind() else {
@@ -1458,7 +1460,7 @@ fn confirm_future_candidate<'cx, 'tcx>(
 fn confirm_iterator_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTermObligation<'tcx>,
-    nested: Vec<PredicateObligation<'tcx>>,
+    nested: PredicateObligations<'tcx>,
 ) -> Progress<'tcx> {
     let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
     let ty::Coroutine(_, args) = self_ty.kind() else {
@@ -1504,7 +1506,7 @@ fn confirm_iterator_candidate<'cx, 'tcx>(
 fn confirm_async_iterator_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTermObligation<'tcx>,
-    nested: Vec<PredicateObligation<'tcx>>,
+    nested: PredicateObligations<'tcx>,
 ) -> Progress<'tcx> {
     let ty::Coroutine(_, args) = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind()
     else {
@@ -1558,7 +1560,7 @@ fn confirm_async_iterator_candidate<'cx, 'tcx>(
 fn confirm_builtin_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTermObligation<'tcx>,
-    data: Vec<PredicateObligation<'tcx>>,
+    data: PredicateObligations<'tcx>,
 ) -> Progress<'tcx> {
     let tcx = selcx.tcx();
     let self_ty = obligation.predicate.self_ty();
@@ -1569,17 +1571,17 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
         let discriminant_def_id = tcx.require_lang_item(LangItem::Discriminant, None);
         assert_eq!(discriminant_def_id, item_def_id);
 
-        (self_ty.discriminant_ty(tcx).into(), Vec::new())
+        (self_ty.discriminant_ty(tcx).into(), PredicateObligations::new())
     } else if tcx.is_lang_item(trait_def_id, LangItem::AsyncDestruct) {
         let destructor_def_id = tcx.associated_item_def_ids(trait_def_id)[0];
         assert_eq!(destructor_def_id, item_def_id);
 
-        (self_ty.async_destructor_ty(tcx).into(), Vec::new())
+        (self_ty.async_destructor_ty(tcx).into(), PredicateObligations::new())
     } else if tcx.is_lang_item(trait_def_id, LangItem::PointeeTrait) {
         let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None);
         assert_eq!(metadata_def_id, item_def_id);
 
-        let mut obligations = Vec::new();
+        let mut obligations = PredicateObligations::new();
         let normalize = |ty| {
             normalize_with_depth_to(
                 selcx,
@@ -1627,7 +1629,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
 fn confirm_fn_pointer_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTermObligation<'tcx>,
-    nested: Vec<PredicateObligation<'tcx>>,
+    nested: PredicateObligations<'tcx>,
 ) -> Progress<'tcx> {
     let tcx = selcx.tcx();
     let fn_type = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
@@ -1663,7 +1665,7 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>(
 fn confirm_closure_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTermObligation<'tcx>,
-    nested: Vec<PredicateObligation<'tcx>>,
+    nested: PredicateObligations<'tcx>,
 ) -> Progress<'tcx> {
     let tcx = selcx.tcx();
     let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
@@ -1782,7 +1784,7 @@ fn confirm_callable_candidate<'cx, 'tcx>(
 fn confirm_async_closure_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTermObligation<'tcx>,
-    nested: Vec<PredicateObligation<'tcx>>,
+    nested: PredicateObligations<'tcx>,
 ) -> Progress<'tcx> {
     let tcx = selcx.tcx();
     let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
@@ -1934,7 +1936,7 @@ fn confirm_async_closure_candidate<'cx, 'tcx>(
 fn confirm_async_fn_kind_helper_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTermObligation<'tcx>,
-    nested: Vec<PredicateObligation<'tcx>>,
+    nested: PredicateObligations<'tcx>,
 ) -> Progress<'tcx> {
     let [
         // We already checked that the goal_kind >= closure_kind
@@ -1987,7 +1989,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>(
     );
 
     let cache_projection = cache_entry.projection_term;
-    let mut nested_obligations = Vec::new();
+    let mut nested_obligations = PredicateObligations::new();
     let obligation_projection = obligation.predicate;
     let obligation_projection = ensure_sufficient_stack(|| {
         normalize_with_depth_to(
@@ -2034,7 +2036,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>(
             );
             debug!("confirm_param_env_candidate: {}", msg);
             let err = Ty::new_error_with_message(infcx.tcx, obligation.cause.span, msg);
-            Progress { term: err.into(), obligations: vec![] }
+            Progress { term: err.into(), obligations: PredicateObligations::new() }
         }
     }
 }
@@ -2047,6 +2049,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
     let tcx = selcx.tcx();
 
     let ImplSourceUserDefinedData { impl_def_id, args, mut nested } = impl_impl_source;
+
     let assoc_item_id = obligation.predicate.def_id;
     let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap();
 
@@ -2102,7 +2105,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
 fn assoc_ty_own_obligations<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTermObligation<'tcx>,
-    nested: &mut Vec<PredicateObligation<'tcx>>,
+    nested: &mut PredicateObligations<'tcx>,
 ) {
     let tcx = selcx.tcx();
     let predicates = tcx
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index 36b24eac5c4..18412b844ff 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -4,6 +4,7 @@
 
 use rustc_data_structures::sso::SsoHashMap;
 use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_infer::traits::PredicateObligations;
 use rustc_macros::extension;
 pub use rustc_middle::traits::query::NormalizationResult;
 use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable};
@@ -20,8 +21,7 @@ use crate::infer::canonical::OriginalQueryValues;
 use crate::infer::{InferCtxt, InferOk};
 use crate::traits::normalize::needs_normalization;
 use crate::traits::{
-    BoundVarReplacer, Normalized, ObligationCause, PlaceholderReplacer, PredicateObligation,
-    Reveal, ScrubbedTraitError,
+    BoundVarReplacer, Normalized, ObligationCause, PlaceholderReplacer, Reveal, ScrubbedTraitError,
 };
 
 #[extension(pub trait QueryNormalizeExt<'tcx>)]
@@ -80,7 +80,9 @@ impl<'a, 'tcx> At<'a, 'tcx> {
             match crate::solve::deeply_normalize_with_skipped_universes::<_, ScrubbedTraitError<'tcx>>(
                 self, value, universes,
             ) {
-                Ok(value) => return Ok(Normalized { value, obligations: vec![] }),
+                Ok(value) => {
+                    return Ok(Normalized { value, obligations: PredicateObligations::new() });
+                }
                 Err(_errors) => {
                     return Err(NoSolution);
                 }
@@ -88,14 +90,14 @@ impl<'a, 'tcx> At<'a, 'tcx> {
         }
 
         if !needs_normalization(&value, self.param_env.reveal()) {
-            return Ok(Normalized { value, obligations: vec![] });
+            return Ok(Normalized { value, obligations: PredicateObligations::new() });
         }
 
         let mut normalizer = QueryNormalizer {
             infcx: self.infcx,
             cause: self.cause,
             param_env: self.param_env,
-            obligations: vec![],
+            obligations: PredicateObligations::new(),
             cache: SsoHashMap::new(),
             anon_depth: 0,
             universes,
@@ -164,7 +166,7 @@ struct QueryNormalizer<'a, 'tcx> {
     infcx: &'a InferCtxt<'tcx>,
     cause: &'a ObligationCause<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
-    obligations: Vec<PredicateObligation<'tcx>>,
+    obligations: PredicateObligations<'tcx>,
     cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>,
     anon_depth: usize,
     universes: Vec<Option<ty::UniverseIndex>>,
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
index a765de92afd..5ae8c87ec02 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
@@ -2,7 +2,7 @@ use std::fmt;
 
 use rustc_errors::ErrorGuaranteed;
 use rustc_infer::infer::canonical::Certainty;
-use rustc_infer::traits::PredicateObligation;
+use rustc_infer::traits::PredicateObligations;
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
@@ -103,13 +103,13 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<TyCtxt<'tcx>> + 't
         (
             Self::QueryResponse,
             Option<Canonical<'tcx, ParamEnvAnd<'tcx, Self>>>,
-            Vec<PredicateObligation<'tcx>>,
+            PredicateObligations<'tcx>,
             Certainty,
         ),
         NoSolution,
     > {
         if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) {
-            return Ok((result, None, vec![], Certainty::Proven));
+            return Ok((result, None, PredicateObligations::new(), Certainty::Proven));
         }
 
         let mut canonical_var_values = OriginalQueryValues::default();
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 084b61115db..52048ca79f9 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -12,7 +12,9 @@ use hir::LangItem;
 use hir::def_id::DefId;
 use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
 use rustc_hir as hir;
-use rustc_infer::traits::{Obligation, ObligationCause, PolyTraitObligation, SelectionError};
+use rustc_infer::traits::{
+    Obligation, ObligationCause, PolyTraitObligation, PredicateObligations, SelectionError,
+};
 use rustc_middle::ty::fast_reject::DeepRejectCtxt;
 use rustc_middle::ty::{self, ToPolyTraitRef, Ty, TypeVisitableExt};
 use rustc_middle::{bug, span_bug};
@@ -881,7 +883,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                         }
 
                         if let Some(principal) = data.principal() {
-                            if !self.infcx.tcx.features().object_safe_for_dispatch {
+                            if !self.infcx.tcx.features().dyn_compatible_for_dispatch {
                                 principal.with_self_ty(self.tcx(), self_ty)
                             } else if self.tcx().is_dyn_compatible(principal.def_id()) {
                                 principal.with_self_ty(self.tcx(), self_ty)
@@ -963,7 +965,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 0,
                 // We're *intentionally* throwing these away,
                 // since we don't actually use them.
-                &mut vec![],
+                &mut PredicateObligations::new(),
             )
             .as_type()
             .unwrap();
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 5141e969608..cc5c7532b50 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -24,7 +24,7 @@ use rustc_span::def_id::DefId;
 use tracing::{debug, instrument};
 
 use super::SelectionCandidate::{self, *};
-use super::{BuiltinImplConditions, SelectionContext};
+use super::{BuiltinImplConditions, PredicateObligations, SelectionContext};
 use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to};
 use crate::traits::util::{self, closure_trait_ref_and_return_type};
 use crate::traits::{
@@ -85,7 +85,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
             // No nested obligations or confirmation process. The checks that we do in
             // candidate assembly are sufficient.
-            AsyncFnKindHelperCandidate => ImplSource::Builtin(BuiltinImplSource::Misc, vec![]),
+            AsyncFnKindHelperCandidate => {
+                ImplSource::Builtin(BuiltinImplSource::Misc, PredicateObligations::new())
+            }
 
             CoroutineCandidate => {
                 let vtable_coroutine = self.confirm_coroutine_candidate(obligation)?;
@@ -121,7 +123,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 // This indicates something like `Trait + Send: Send`. In this case, we know that
                 // this holds because that's what the object type is telling us, and there's really
                 // no additional obligations to prove and no types in particular to unify, etc.
-                ImplSource::Builtin(BuiltinImplSource::Misc, Vec::new())
+                ImplSource::Builtin(BuiltinImplSource::Misc, PredicateObligations::new())
             }
 
             BuiltinUnsizeCandidate => self.confirm_builtin_unsize_candidate(obligation)?,
@@ -149,7 +151,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
         idx: usize,
-    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
         let tcx = self.tcx();
 
         let placeholder_trait_predicate =
@@ -179,7 +181,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             HigherRankedType,
             candidate,
         );
-        let mut obligations = Vec::new();
+        let mut obligations = PredicateObligations::new();
         let candidate = normalize_with_depth_to(
             self,
             obligation.param_env,
@@ -226,7 +228,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
         param: ty::PolyTraitRef<'tcx>,
-    ) -> Vec<PredicateObligation<'tcx>> {
+    ) -> PredicateObligations<'tcx> {
         debug!(?obligation, ?param, "confirm_param_candidate");
 
         // During evaluation, we already checked that this
@@ -249,7 +251,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
         has_nested: bool,
-    ) -> Vec<PredicateObligation<'tcx>> {
+    ) -> PredicateObligations<'tcx> {
         debug!(?obligation, ?has_nested, "confirm_builtin_candidate");
 
         let tcx = self.tcx();
@@ -279,7 +281,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 nested,
             )
         } else {
-            vec![]
+            PredicateObligations::new()
         };
 
         debug!(?obligations);
@@ -291,7 +293,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     fn confirm_transmutability_candidate(
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
-    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
         use rustc_transmute::{Answer, Assume, Condition};
 
         /// Generate sub-obligations for reference-to-reference transmutations.
@@ -301,7 +303,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             (src_lifetime, src_ty, src_mut): (ty::Region<'tcx>, Ty<'tcx>, Mutability),
             (dst_lifetime, dst_ty, dst_mut): (ty::Region<'tcx>, Ty<'tcx>, Mutability),
             assume: Assume,
-        ) -> Vec<PredicateObligation<'tcx>> {
+        ) -> PredicateObligations<'tcx> {
             let make_transmute_obl = |src, dst| {
                 let transmute_trait = obligation.predicate.def_id();
                 let assume = obligation.predicate.skip_binder().trait_ref.args.const_at(2);
@@ -347,7 +349,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             // Given a transmutation from `&'a (mut) Src` and `&'dst (mut) Dst`,
             // it is always the case that `Src` must be transmutable into `Dst`,
             // and that that `'src` must outlive `'dst`.
-            let mut obls = vec![make_transmute_obl(src_ty, dst_ty)];
+            let mut obls = PredicateObligations::with_capacity(1);
+            obls.push(make_transmute_obl(src_ty, dst_ty));
             if !assume.lifetimes {
                 obls.push(make_outlives_obl(src_lifetime, dst_lifetime));
             }
@@ -382,7 +385,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             obligation: &PolyTraitObligation<'tcx>,
             cond: Condition<rustc_transmute::layout::rustc::Ref<'tcx>>,
             assume: Assume,
-        ) -> Vec<PredicateObligation<'tcx>> {
+        ) -> PredicateObligations<'tcx> {
             match cond {
                 // FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll`
                 // Not possible until the trait solver supports disjunctions of obligations
@@ -424,7 +427,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let fully_flattened = match maybe_transmutable {
             Answer::No(_) => Err(Unimplemented)?,
             Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation, cond, assume),
-            Answer::Yes => vec![],
+            Answer::Yes => PredicateObligations::new(),
         };
 
         debug!(?fully_flattened);
@@ -439,7 +442,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     fn confirm_auto_impl_candidate(
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
-    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
         debug!(?obligation, "confirm_auto_impl_candidate");
 
         let self_ty = obligation.predicate.self_ty().map_bound(|ty| self.infcx.shallow_resolve(ty));
@@ -453,14 +456,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         obligation: &PolyTraitObligation<'tcx>,
         trait_def_id: DefId,
         nested: ty::Binder<'tcx, Vec<Ty<'tcx>>>,
-    ) -> Vec<PredicateObligation<'tcx>> {
+    ) -> PredicateObligations<'tcx> {
         debug!(?nested, "vtable_auto_impl");
         ensure_sufficient_stack(|| {
             let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived);
 
             let poly_trait_ref = obligation.predicate.to_poly_trait_ref();
             let trait_ref = self.infcx.enter_forall_and_leak_universe(poly_trait_ref);
-            let trait_obligations: Vec<PredicateObligation<'_>> = self.impl_or_trait_obligations(
+            let trait_obligations = self.impl_or_trait_obligations(
                 &cause,
                 obligation.recursion_depth + 1,
                 obligation.param_env,
@@ -566,7 +569,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         );
         let object_trait_ref = object_trait_ref.with_self_ty(self.tcx(), self_ty);
 
-        let mut nested = vec![];
+        let mut nested = PredicateObligations::new();
 
         let mut supertraits = util::supertraits(tcx, ty::Binder::dummy(object_trait_ref));
         let unnormalized_upcast_trait_ref =
@@ -706,7 +709,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
         fn_host_effect: ty::Const<'tcx>,
-    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
         debug!(?obligation, "confirm_fn_pointer_candidate");
         let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
         let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
@@ -750,7 +753,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     fn confirm_trait_alias_candidate(
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
-    ) -> Vec<PredicateObligation<'tcx>> {
+    ) -> PredicateObligations<'tcx> {
         debug!(?obligation, "confirm_trait_alias_candidate");
 
         let predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
@@ -775,7 +778,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     fn confirm_coroutine_candidate(
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
-    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
         let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
         let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
         let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
@@ -805,7 +808,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     fn confirm_future_candidate(
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
-    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
         let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
         let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
         let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
@@ -835,7 +838,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     fn confirm_iterator_candidate(
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
-    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
         let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
         let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
         let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
@@ -865,7 +868,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     fn confirm_async_iterator_candidate(
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
-    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
         let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
         let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
         let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
@@ -896,7 +899,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     fn confirm_closure_candidate(
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
-    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
         let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
         let self_ty: Ty<'_> = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
 
@@ -926,13 +929,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     fn confirm_async_closure_candidate(
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
-    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
         let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
         let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
 
         let tcx = self.tcx();
 
-        let mut nested = vec![];
+        let mut nested = PredicateObligations::new();
         let (trait_ref, kind_ty) = match *self_ty.kind() {
             ty::CoroutineClosure(_, args) => {
                 let args = args.as_coroutine_closure();
@@ -1055,7 +1058,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         obligation: TraitObligation<'tcx>,
         found_trait_ref: ty::PolyTraitRef<'tcx>,
-    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
         let found_trait_ref = self.infcx.instantiate_binder_with_fresh_vars(
             obligation.cause.span,
             HigherRankedType,
@@ -1210,7 +1213,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 //  words, if the object type is `Foo + Send`, this would create an obligation for
                 //  the `Send` check.)
                 //  - Projection predicates
-                let mut nested: Vec<_> = data
+                let mut nested: PredicateObligations<'_> = data
                     .iter()
                     .map(|predicate| predicate_to_obligation(predicate.with_self_ty(tcx, source)))
                     .collect();
@@ -1254,7 +1257,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 let tail_field = def.non_enum_variant().tail();
                 let tail_field_ty = tcx.type_of(tail_field.did);
 
-                let mut nested = vec![];
+                let mut nested = PredicateObligations::new();
 
                 // Extract `TailField<T>` and `TailField<U>` from `Struct<T>` and `Struct<U>`,
                 // normalizing in the process, since `type_of` returns something directly from
@@ -1339,7 +1342,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
         impl_def_id: Option<DefId>,
-    ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
         let Some(host_effect_index) =
             self.tcx().generics_of(obligation.predicate.def_id()).host_effect_index
         else {
@@ -1353,7 +1356,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let tcx = self.tcx();
         let self_ty = obligation.self_ty().map_bound(|ty| self.infcx.shallow_resolve(ty));
 
-        let mut nested = vec![];
+        let mut nested = PredicateObligations::new();
         let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived);
 
         // If we have a custom `impl const Drop`, then
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index fba1d1025ca..621babe9104 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -18,7 +18,7 @@ use rustc_infer::infer::BoundRegionConversionTime::{self, HigherRankedType};
 use rustc_infer::infer::DefineOpaqueTypes;
 use rustc_infer::infer::at::ToTrace;
 use rustc_infer::infer::relate::TypeRelation;
-use rustc_infer::traits::TraitObligation;
+use rustc_infer::traits::{PredicateObligations, TraitObligation};
 use rustc_middle::bug;
 use rustc_middle::dep_graph::{DepNodeIndex, dep_kinds};
 use rustc_middle::mir::interpret::ErrorHandled;
@@ -1067,7 +1067,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 && fresh_trait_pred.has_aliases()
                 && fresh_trait_pred.is_global()
             {
-                let mut nested_obligations = Vec::new();
+                let mut nested_obligations = PredicateObligations::new();
                 let predicate = normalize_with_depth_to(
                     this,
                     param_env,
@@ -1715,7 +1715,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     ) -> ProjectionMatchesProjection {
         debug_assert_eq!(obligation.predicate.def_id, env_predicate.projection_def_id());
 
-        let mut nested_obligations = Vec::new();
+        let mut nested_obligations = PredicateObligations::new();
         let infer_predicate = self.infcx.instantiate_binder_with_fresh_vars(
             obligation.cause.span,
             BoundRegionConversionTime::HigherRankedType,
@@ -2410,7 +2410,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
         recursion_depth: usize,
         trait_def_id: DefId,
         types: ty::Binder<'tcx, Vec<Ty<'tcx>>>,
-    ) -> Vec<PredicateObligation<'tcx>> {
+    ) -> PredicateObligations<'tcx> {
         // Because the types were potentially derived from
         // higher-ranked obligations they may reference late-bound
         // regions. For example, `for<'a> Foo<&'a i32> : Copy` would
@@ -2552,9 +2552,9 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
         b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
         a_region: ty::Region<'tcx>,
         b_region: ty::Region<'tcx>,
-    ) -> SelectionResult<'tcx, Vec<PredicateObligation<'tcx>>> {
+    ) -> SelectionResult<'tcx, PredicateObligations<'tcx>> {
         let tcx = self.tcx();
-        let mut nested = vec![];
+        let mut nested = PredicateObligations::new();
 
         // We may upcast to auto traits that are either explicitly listed in
         // the object type's bounds, or implied by the principal trait ref's
@@ -2705,7 +2705,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
         where_clause_trait_ref: ty::PolyTraitRef<'tcx>,
-    ) -> Result<Vec<PredicateObligation<'tcx>>, ()> {
+    ) -> Result<PredicateObligations<'tcx>, ()> {
         self.match_poly_trait_ref(obligation, where_clause_trait_ref)
     }
 
@@ -2716,7 +2716,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
         poly_trait_ref: ty::PolyTraitRef<'tcx>,
-    ) -> Result<Vec<PredicateObligation<'tcx>>, ()> {
+    ) -> Result<PredicateObligations<'tcx>, ()> {
         let predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
         let trait_ref = self.infcx.instantiate_binder_with_fresh_vars(
             obligation.cause.span,
@@ -2797,7 +2797,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
         def_id: DefId,              // of impl or trait
         args: GenericArgsRef<'tcx>, // for impl or trait
         parent_trait_pred: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
-    ) -> Vec<PredicateObligation<'tcx>> {
+    ) -> PredicateObligations<'tcx> {
         let tcx = self.tcx();
 
         // To allow for one-pass evaluation of the nested obligation,
@@ -2817,7 +2817,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
         let predicates = tcx.predicates_of(def_id);
         assert_eq!(predicates.parent, None);
         let predicates = predicates.instantiate_own(tcx, args);
-        let mut obligations = Vec::with_capacity(predicates.len());
+        let mut obligations = PredicateObligations::with_capacity(predicates.len());
         for (index, (predicate, span)) in predicates.into_iter().enumerate() {
             let cause = if tcx.is_lang_item(parent_trait_pred.def_id(), LangItem::CoerceUnsized) {
                 cause.clone()
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 7e140ecfee0..07e68e5a3e8 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -2,7 +2,7 @@ use std::iter;
 
 use rustc_hir as hir;
 use rustc_hir::lang_items::LangItem;
-use rustc_infer::traits::ObligationCauseCode;
+use rustc_infer::traits::{ObligationCauseCode, PredicateObligations};
 use rustc_middle::bug;
 use rustc_middle::ty::{
     self, GenericArg, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable,
@@ -27,7 +27,7 @@ pub fn obligations<'tcx>(
     recursion_depth: usize,
     arg: GenericArg<'tcx>,
     span: Span,
-) -> Option<Vec<traits::PredicateObligation<'tcx>>> {
+) -> Option<PredicateObligations<'tcx>> {
     // Handle the "livelock" case (see comment above) by bailing out if necessary.
     let arg = match arg.unpack() {
         GenericArgKind::Type(ty) => {
@@ -61,11 +61,18 @@ pub fn obligations<'tcx>(
             .into()
         }
         // There is nothing we have to do for lifetimes.
-        GenericArgKind::Lifetime(..) => return Some(Vec::new()),
+        GenericArgKind::Lifetime(..) => return Some(PredicateObligations::new()),
     };
 
-    let mut wf =
-        WfPredicates { infcx, param_env, body_id, span, out: vec![], recursion_depth, item: None };
+    let mut wf = WfPredicates {
+        infcx,
+        param_env,
+        body_id,
+        span,
+        out: PredicateObligations::new(),
+        recursion_depth,
+        item: None,
+    };
     wf.compute(arg);
     debug!("wf::obligations({:?}, body_id={:?}) = {:?}", arg, body_id, wf.out);
 
@@ -82,7 +89,7 @@ pub fn unnormalized_obligations<'tcx>(
     infcx: &InferCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     arg: GenericArg<'tcx>,
-) -> Option<Vec<traits::PredicateObligation<'tcx>>> {
+) -> Option<PredicateObligations<'tcx>> {
     debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg));
 
     // However, if `arg` IS an unresolved inference variable, returns `None`,
@@ -93,7 +100,7 @@ pub fn unnormalized_obligations<'tcx>(
     }
 
     if let ty::GenericArgKind::Lifetime(..) = arg.unpack() {
-        return Some(vec![]);
+        return Some(PredicateObligations::new());
     }
 
     let mut wf = WfPredicates {
@@ -101,7 +108,7 @@ pub fn unnormalized_obligations<'tcx>(
         param_env,
         body_id: CRATE_DEF_ID,
         span: DUMMY_SP,
-        out: vec![],
+        out: PredicateObligations::new(),
         recursion_depth: 0,
         item: None,
     };
@@ -120,13 +127,13 @@ pub fn trait_obligations<'tcx>(
     trait_pred: ty::TraitPredicate<'tcx>,
     span: Span,
     item: &'tcx hir::Item<'tcx>,
-) -> Vec<traits::PredicateObligation<'tcx>> {
+) -> PredicateObligations<'tcx> {
     let mut wf = WfPredicates {
         infcx,
         param_env,
         body_id,
         span,
-        out: vec![],
+        out: PredicateObligations::new(),
         recursion_depth: 0,
         item: Some(item),
     };
@@ -147,13 +154,13 @@ pub fn clause_obligations<'tcx>(
     body_id: LocalDefId,
     clause: ty::Clause<'tcx>,
     span: Span,
-) -> Vec<traits::PredicateObligation<'tcx>> {
+) -> PredicateObligations<'tcx> {
     let mut wf = WfPredicates {
         infcx,
         param_env,
         body_id,
         span,
-        out: vec![],
+        out: PredicateObligations::new(),
         recursion_depth: 0,
         item: None,
     };
@@ -192,7 +199,7 @@ struct WfPredicates<'a, 'tcx> {
     param_env: ty::ParamEnv<'tcx>,
     body_id: LocalDefId,
     span: Span,
-    out: Vec<traits::PredicateObligation<'tcx>>,
+    out: PredicateObligations<'tcx>,
     recursion_depth: usize,
     item: Option<&'tcx hir::Item<'tcx>>,
 }
@@ -323,7 +330,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
         traits::ObligationCause::new(self.span, self.body_id, code)
     }
 
-    fn normalize(self, infcx: &InferCtxt<'tcx>) -> Vec<traits::PredicateObligation<'tcx>> {
+    fn normalize(self, infcx: &InferCtxt<'tcx>) -> PredicateObligations<'tcx> {
         // Do not normalize `wf` obligations with the new solver.
         //
         // The current deep normalization routine with the new solver does not
@@ -336,7 +343,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
 
         let cause = self.cause(ObligationCauseCode::WellFormed(None));
         let param_env = self.param_env;
-        let mut obligations = Vec::with_capacity(self.out.len());
+        let mut obligations = PredicateObligations::with_capacity(self.out.len());
         for mut obligation in self.out {
             assert!(!obligation.has_escaping_bound_vars());
             let mut selcx = traits::SelectionContext::new(infcx);
@@ -553,7 +560,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
         &mut self,
         def_id: DefId,
         args: GenericArgsRef<'tcx>,
-    ) -> Vec<traits::PredicateObligation<'tcx>> {
+    ) -> PredicateObligations<'tcx> {
         let predicates = self.tcx().predicates_of(def_id);
         let mut origins = vec![def_id; predicates.predicates.len()];
         let mut head = predicates;
@@ -829,7 +836,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
                 // obligations that don't refer to Self and
                 // checking those
 
-                let defer_to_coercion = tcx.features().object_safe_for_dispatch;
+                let defer_to_coercion = tcx.features().dyn_compatible_for_dispatch;
 
                 if !defer_to_coercion {
                     if let Some(principal) = data.principal_def_id() {
diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs
index 3102da218db..4c2b7e4769a 100644
--- a/compiler/rustc_traits/src/normalize_projection_ty.rs
+++ b/compiler/rustc_traits/src/normalize_projection_ty.rs
@@ -1,5 +1,6 @@
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_infer::infer::canonical::{Canonical, QueryResponse};
+use rustc_infer::traits::PredicateObligations;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
@@ -30,7 +31,7 @@ fn normalize_canonicalized_projection_ty<'tcx>(
             debug_assert!(!ocx.infcx.next_trait_solver());
             let selcx = &mut SelectionContext::new(ocx.infcx);
             let cause = ObligationCause::dummy();
-            let mut obligations = vec![];
+            let mut obligations = PredicateObligations::new();
             let answer =
                 traits::normalize_projection_ty(selcx, param_env, goal, cause, 0, &mut obligations);
             ocx.register_obligations(obligations);
@@ -99,7 +100,7 @@ fn normalize_canonicalized_inherent_projection_ty<'tcx>(
         |ocx, ParamEnvAnd { param_env, value: goal }| {
             let selcx = &mut SelectionContext::new(ocx.infcx);
             let cause = ObligationCause::dummy();
-            let mut obligations = vec![];
+            let mut obligations = PredicateObligations::new();
             let answer = traits::normalize_inherent_projection(
                 selcx,
                 param_env,
diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml
index 01d5251bfa0..40356e0c978 100644
--- a/compiler/rustc_ty_utils/Cargo.toml
+++ b/compiler/rustc_ty_utils/Cargo.toml
@@ -6,6 +6,7 @@ edition = "2021"
 [dependencies]
 # tidy-alphabetical-start
 itertools = "0.12"
+rustc_abi = { path = "../rustc_abi" }
 rustc_ast_ir = { path = "../rustc_ast_ir" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs
index 3d6c09bf89c..7354ea5fb6a 100644
--- a/compiler/rustc_ty_utils/src/abi.rs
+++ b/compiler/rustc_ty_utils/src/abi.rs
@@ -1,5 +1,8 @@
 use std::iter;
 
+use rustc_abi::Float::*;
+use rustc_abi::Primitive::{Float, Pointer};
+use rustc_abi::{Abi, AddressSpace, PointerKind, Scalar, Size};
 use rustc_hir as hir;
 use rustc_hir::lang_items::LangItem;
 use rustc_middle::bug;
@@ -14,7 +17,6 @@ use rustc_target::abi::call::{
     ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, Conv, FnAbi, PassMode, Reg, RegKind,
     RiscvInterruptKind,
 };
-use rustc_target::abi::*;
 use rustc_target::spec::abi::Abi as SpecAbi;
 use tracing::debug;
 
@@ -820,10 +822,10 @@ fn make_thin_self_ptr<'tcx>(
             _ => bug!("receiver type has unsupported layout: {:?}", layout),
         }
 
-        // In the case of Rc<Self>, we need to explicitly pass a *mut RcBox<Self>
+        // In the case of Rc<Self>, we need to explicitly pass a *mut RcInner<Self>
         // with a Scalar (not ScalarPair) ABI. This is a hack that is understood
         // elsewhere in the compiler as a method on a `dyn Trait`.
-        // To get the type `*mut RcBox<Self>`, we just keep unwrapping newtypes until we
+        // To get the type `*mut RcInner<Self>`, we just keep unwrapping newtypes until we
         // get a built-in pointer type
         let mut wide_pointer_layout = layout;
         while !wide_pointer_layout.ty.is_unsafe_ptr() && !wide_pointer_layout.ty.is_ref() {
@@ -836,7 +838,7 @@ fn make_thin_self_ptr<'tcx>(
         wide_pointer_layout.ty
     };
 
-    // we now have a type like `*mut RcBox<dyn Trait>`
+    // we now have a type like `*mut RcInner<dyn Trait>`
     // change its layout to that of `*mut ()`, a thin pointer, but keep the same type
     // this is understood as a special case elsewhere in the compiler
     let unit_ptr_ty = Ty::new_mut_ptr(tcx, tcx.types.unit);
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index 34c9f1b63c0..afdfa2e80c1 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -2,7 +2,12 @@ use std::fmt::Debug;
 use std::iter;
 
 use hir::def_id::DefId;
-use rustc_hir as hir;
+use rustc_abi::Integer::{I8, I32};
+use rustc_abi::Primitive::{self, Float, Int, Pointer};
+use rustc_abi::{
+    Abi, AbiAndPrefAlign, AddressSpace, Align, FieldsShape, HasDataLayout, LayoutCalculatorError,
+    LayoutS, Niche, ReprOptions, Scalar, Size, StructKind, TagEncoding, Variants, WrappingRange,
+};
 use rustc_index::bit_set::BitSet;
 use rustc_index::{IndexSlice, IndexVec};
 use rustc_middle::bug;
@@ -18,8 +23,9 @@ use rustc_middle::ty::{
 use rustc_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo};
 use rustc_span::sym;
 use rustc_span::symbol::Symbol;
-use rustc_target::abi::*;
+use rustc_target::abi::{FIRST_VARIANT, FieldIdx, Layout, VariantIdx};
 use tracing::{debug, instrument, trace};
+use {rustc_abi as abi, rustc_hir as hir};
 
 use crate::errors::{
     MultipleArrayFieldsSimdType, NonPrimitiveSimdType, OversizedSimdType, ZeroLengthSimdType,
@@ -202,9 +208,9 @@ fn layout_of_uncached<'tcx>(
             value: Int(I32, false),
             valid_range: WrappingRange { start: 0, end: 0x10FFFF },
         })),
-        ty::Int(ity) => scalar(Int(Integer::from_int_ty(dl, ity), true)),
-        ty::Uint(ity) => scalar(Int(Integer::from_uint_ty(dl, ity), false)),
-        ty::Float(fty) => scalar(Float(Float::from_float_ty(fty))),
+        ty::Int(ity) => scalar(Int(abi::Integer::from_int_ty(dl, ity), true)),
+        ty::Uint(ity) => scalar(Int(abi::Integer::from_uint_ty(dl, ity), false)),
+        ty::Float(fty) => scalar(Float(abi::Float::from_float_ty(fty))),
         ty::FnPtr(..) => {
             let mut ptr = scalar_unit(Pointer(dl.instruction_address_space));
             ptr.valid_range_mut().start = 1;
@@ -563,7 +569,7 @@ fn layout_of_uncached<'tcx>(
             }
 
             let get_discriminant_type =
-                |min, max| Integer::repr_discr(tcx, ty, &def.repr(), min, max);
+                |min, max| abi::Integer::repr_discr(tcx, ty, &def.repr(), min, max);
 
             let discriminants_iter = || {
                 def.is_enum()
@@ -816,7 +822,7 @@ fn coroutine_layout<'tcx>(
 
     // `info.variant_fields` already accounts for the reserved variants, so no need to add them.
     let max_discr = (info.variant_fields.len() - 1) as u128;
-    let discr_int = Integer::fit_unsigned(max_discr);
+    let discr_int = abi::Integer::fit_unsigned(max_discr);
     let tag = Scalar::Initialized {
         value: Primitive::Int(discr_int, /* signed = */ false),
         valid_range: WrappingRange { start: 0, end: max_discr },
diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml
index 98cc116bd00..8d97ec72830 100644
--- a/compiler/rustc_type_ir/Cargo.toml
+++ b/compiler/rustc_type_ir/Cargo.toml
@@ -17,6 +17,7 @@ rustc_serialize = { path = "../rustc_serialize", optional = true }
 rustc_span = { path = "../rustc_span", optional = true }
 rustc_type_ir_macros = { path = "../rustc_type_ir_macros" }
 smallvec = { version = "1.8.1", default-features = false }
+thin-vec = "0.2.12"
 tracing = "0.1"
 # tidy-alphabetical-end
 
@@ -30,7 +31,7 @@ nightly = [
     "smallvec/may_dangle",
     "smallvec/union",
     "rustc_index/nightly",
-    "rustc_ast_ir/nightly"
+    "rustc_ast_ir/nightly",
 ]
 
 [lints.rust]
diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs
index 64ce6fd69e6..8209d6f5fe3 100644
--- a/compiler/rustc_type_ir/src/fold.rs
+++ b/compiler/rustc_type_ir/src/fold.rs
@@ -48,6 +48,7 @@
 use std::mem;
 
 use rustc_index::{Idx, IndexVec};
+use thin_vec::ThinVec;
 use tracing::instrument;
 
 use crate::data_structures::Lrc;
@@ -322,6 +323,12 @@ impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Vec<T> {
     }
 }
 
+impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for ThinVec<T> {
+    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
+        self.into_iter().map(|t| t.try_fold_with(folder)).collect()
+    }
+}
+
 impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Box<[T]> {
     fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
         Vec::from(self).try_fold_with(folder).map(Vec::into_boxed_slice)
diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs
index 1d0b2345b80..b9f5cde653e 100644
--- a/compiler/rustc_type_ir/src/infer_ctxt.rs
+++ b/compiler/rustc_type_ir/src/infer_ctxt.rs
@@ -1,12 +1,21 @@
 use crate::fold::TypeFoldable;
-use crate::relate::Relate;
-use crate::solve::{Goal, NoSolution, SolverMode};
+use crate::relate::RelateResult;
+use crate::relate::combine::PredicateEmittingRelation;
+use crate::solve::SolverMode;
 use crate::{self as ty, Interner};
 
 pub trait InferCtxtLike: Sized {
     type Interner: Interner;
     fn cx(&self) -> Self::Interner;
 
+    /// Whether the new trait solver is enabled. This only exists because rustc
+    /// shares code between the new and old trait solvers; for all other users,
+    /// this should always be true. If this is unknowingly false and you try to
+    /// use the new trait solver, things will break badly.
+    fn next_trait_solver(&self) -> bool {
+        true
+    }
+
     fn solver_mode(&self) -> SolverMode;
 
     fn universe(&self) -> ty::UniverseIndex;
@@ -58,25 +67,45 @@ pub trait InferCtxtLike: Sized {
         f: impl FnOnce(T) -> U,
     ) -> U;
 
-    fn relate<T: Relate<Self::Interner>>(
-        &self,
-        param_env: <Self::Interner as Interner>::ParamEnv,
-        lhs: T,
-        variance: ty::Variance,
-        rhs: T,
-    ) -> Result<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>, NoSolution>;
+    fn equate_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid);
+    fn equate_int_vids_raw(&self, a: ty::IntVid, b: ty::IntVid);
+    fn equate_float_vids_raw(&self, a: ty::FloatVid, b: ty::FloatVid);
+    fn equate_const_vids_raw(&self, a: ty::ConstVid, b: ty::ConstVid);
+    fn equate_effect_vids_raw(&self, a: ty::EffectVid, b: ty::EffectVid);
 
-    fn eq_structurally_relating_aliases<T: Relate<Self::Interner>>(
+    fn instantiate_ty_var_raw<R: PredicateEmittingRelation<Self>>(
+        &self,
+        relation: &mut R,
+        target_is_expected: bool,
+        target_vid: ty::TyVid,
+        instantiation_variance: ty::Variance,
+        source_ty: <Self::Interner as Interner>::Ty,
+    ) -> RelateResult<Self::Interner, ()>;
+    fn instantiate_int_var_raw(&self, vid: ty::IntVid, value: ty::IntVarValue);
+    fn instantiate_float_var_raw(&self, vid: ty::FloatVid, value: ty::FloatVarValue);
+    fn instantiate_effect_var_raw(
+        &self,
+        vid: ty::EffectVid,
+        value: <Self::Interner as Interner>::Const,
+    );
+    fn instantiate_const_var_raw<R: PredicateEmittingRelation<Self>>(
         &self,
-        param_env: <Self::Interner as Interner>::ParamEnv,
-        lhs: T,
-        rhs: T,
-    ) -> Result<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>, NoSolution>;
+        relation: &mut R,
+        target_is_expected: bool,
+        target_vid: ty::ConstVid,
+        source_ct: <Self::Interner as Interner>::Const,
+    ) -> RelateResult<Self::Interner, ()>;
+
+    fn set_tainted_by_errors(&self, e: <Self::Interner as Interner>::ErrorGuaranteed);
 
     fn shallow_resolve(
         &self,
         ty: <Self::Interner as Interner>::Ty,
     ) -> <Self::Interner as Interner>::Ty;
+    fn shallow_resolve_const(
+        &self,
+        ty: <Self::Interner as Interner>::Const,
+    ) -> <Self::Interner as Interner>::Const;
 
     fn resolve_vars_if_possible<T>(&self, value: T) -> T
     where
@@ -90,6 +119,12 @@ pub trait InferCtxtLike: Sized {
         sup: <Self::Interner as Interner>::Region,
     );
 
+    fn equate_regions(
+        &self,
+        a: <Self::Interner as Interner>::Region,
+        b: <Self::Interner as Interner>::Region,
+    );
+
     fn register_ty_outlives(
         &self,
         ty: <Self::Interner as Interner>::Ty,
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index 69665df4bfc..f7875bb5152 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -565,6 +565,10 @@ pub trait BoundExistentialPredicates<I: Interner>:
     ) -> impl IntoIterator<Item = ty::Binder<I, ty::ExistentialProjection<I>>>;
 }
 
+pub trait Span<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> {
+    fn dummy() -> Self;
+}
+
 pub trait SliceLike: Sized + Copy {
     type Item: Copy;
     type IntoIter: Iterator<Item = Self::Item>;
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index b2ac67efef6..f06017d7e5c 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -36,7 +36,7 @@ pub trait Interner:
 {
     type DefId: DefId<Self>;
     type LocalDefId: Copy + Debug + Hash + Eq + Into<Self::DefId> + TypeFoldable<Self>;
-    type Span: Copy + Debug + Hash + Eq + TypeFoldable<Self>;
+    type Span: Span<Self>;
 
     type GenericArgs: GenericArgs<Self>;
     type GenericArgsSlice: Copy + Debug + Hash + Eq + SliceLike<Item = Self::GenericArg>;
@@ -261,6 +261,8 @@ pub trait Interner:
 
     fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool;
 
+    fn is_impl_trait_in_trait(self, def_id: Self::DefId) -> bool;
+
     fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed;
 
     fn is_general_coroutine(self, coroutine_def_id: Self::DefId) -> bool;
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index 02a9ad1e35f..9e6d1f424ba 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -1,11 +1,11 @@
 // tidy-alphabetical-start
 #![allow(rustc::usage_of_ty_tykind)]
+#![allow(rustc::usage_of_type_ir_inherent)]
 #![cfg_attr(
     feature = "nightly",
     feature(associated_type_defaults, never_type, rustc_attrs, negative_impls)
 )]
 #![cfg_attr(feature = "nightly", allow(internal_features))]
-#![cfg_attr(not(bootstrap), allow(rustc::usage_of_type_ir_inherent))]
 #![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
@@ -206,8 +206,8 @@ pub fn debug_bound_var<T: std::fmt::Write>(
     }
 }
 
-#[derive(Copy, Clone, PartialEq, Eq)]
-#[cfg_attr(feature = "nightly", derive(Decodable, Encodable, Hash, HashStable_NoContext))]
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+#[cfg_attr(feature = "nightly", derive(Decodable, Encodable, HashStable_NoContext))]
 #[cfg_attr(feature = "nightly", rustc_pass_by_value)]
 pub enum Variance {
     Covariant,     // T<A> <: T<B> iff A <: B -- e.g., function return type
diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs
index 9c725f34d8e..e1f3e493e36 100644
--- a/compiler/rustc_type_ir/src/relate.rs
+++ b/compiler/rustc_type_ir/src/relate.rs
@@ -9,8 +9,23 @@ use crate::fold::TypeFoldable;
 use crate::inherent::*;
 use crate::{self as ty, Interner};
 
+pub mod combine;
+pub mod solver_relating;
+
 pub type RelateResult<I, T> = Result<T, TypeError<I>>;
 
+/// Whether aliases should be related structurally or not. Used
+/// to adjust the behavior of generalization and combine.
+///
+/// This should always be `No` unless in a few special-cases when
+/// instantiating canonical responses and in the new solver. Each
+/// such case should have a comment explaining why it is used.
+#[derive(Debug, Copy, Clone)]
+pub enum StructurallyRelateAliases {
+    Yes,
+    No,
+}
+
 /// Extra information about why we ended up with a particular variance.
 /// This is only used to add more information to error messages, and
 /// has no effect on soundness. While choosing the 'wrong' `VarianceDiagInfo`
@@ -239,6 +254,16 @@ impl<I: Interner> Relate<I> for ty::AliasTy<I> {
                     b.args,
                     false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
                 )?,
+                ty::Projection if relation.cx().is_impl_trait_in_trait(a.def_id) => {
+                    relate_args_with_variances(
+                        relation,
+                        a.def_id,
+                        relation.cx().variances_of(a.def_id),
+                        a.args,
+                        b.args,
+                        false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
+                    )?
+                }
                 ty::Projection | ty::Weak | ty::Inherent => {
                     relate_args_invariantly(relation, a.args, b.args)?
                 }
diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs
new file mode 100644
index 00000000000..60a953801a4
--- /dev/null
+++ b/compiler/rustc_type_ir/src/relate/combine.rs
@@ -0,0 +1,246 @@
+use tracing::debug;
+
+use super::{
+    ExpectedFound, RelateResult, StructurallyRelateAliases, TypeRelation,
+    structurally_relate_consts, structurally_relate_tys,
+};
+use crate::error::TypeError;
+use crate::inherent::*;
+use crate::solve::{Goal, SolverMode};
+use crate::visit::TypeVisitableExt as _;
+use crate::{self as ty, InferCtxtLike, Interner, Upcast};
+
+pub trait PredicateEmittingRelation<Infcx, I = <Infcx as InferCtxtLike>::Interner>:
+    TypeRelation<I>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
+    fn span(&self) -> I::Span;
+
+    fn param_env(&self) -> I::ParamEnv;
+
+    /// Whether aliases should be related structurally. This is pretty much
+    /// always `No` unless you're equating in some specific locations of the
+    /// new solver. See the comments in these use-cases for more details.
+    fn structurally_relate_aliases(&self) -> StructurallyRelateAliases;
+
+    /// Register obligations that must hold in order for this relation to hold
+    fn register_goals(&mut self, obligations: impl IntoIterator<Item = Goal<I, I::Predicate>>);
+
+    /// Register predicates that must hold in order for this relation to hold.
+    /// This uses the default `param_env` of the obligation.
+    fn register_predicates(
+        &mut self,
+        obligations: impl IntoIterator<Item: Upcast<I, I::Predicate>>,
+    );
+
+    /// Register `AliasRelate` obligation(s) that both types must be related to each other.
+    fn register_alias_relate_predicate(&mut self, a: I::Ty, b: I::Ty);
+}
+
+pub fn super_combine_tys<Infcx, I, R>(
+    infcx: &Infcx,
+    relation: &mut R,
+    a: I::Ty,
+    b: I::Ty,
+) -> RelateResult<I, I::Ty>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+    R: PredicateEmittingRelation<Infcx>,
+{
+    debug!("super_combine_tys::<{}>({:?}, {:?})", std::any::type_name::<R>(), a, b);
+    debug_assert!(!a.has_escaping_bound_vars());
+    debug_assert!(!b.has_escaping_bound_vars());
+
+    match (a.kind(), b.kind()) {
+        (ty::Error(e), _) | (_, ty::Error(e)) => {
+            infcx.set_tainted_by_errors(e);
+            return Ok(Ty::new_error(infcx.cx(), e));
+        }
+
+        // Relate integral variables to other types
+        (ty::Infer(ty::IntVar(a_id)), ty::Infer(ty::IntVar(b_id))) => {
+            infcx.equate_int_vids_raw(a_id, b_id);
+            Ok(a)
+        }
+        (ty::Infer(ty::IntVar(v_id)), ty::Int(v)) => {
+            infcx.instantiate_int_var_raw(v_id, ty::IntVarValue::IntType(v));
+            Ok(b)
+        }
+        (ty::Int(v), ty::Infer(ty::IntVar(v_id))) => {
+            infcx.instantiate_int_var_raw(v_id, ty::IntVarValue::IntType(v));
+            Ok(a)
+        }
+        (ty::Infer(ty::IntVar(v_id)), ty::Uint(v)) => {
+            infcx.instantiate_int_var_raw(v_id, ty::IntVarValue::UintType(v));
+            Ok(b)
+        }
+        (ty::Uint(v), ty::Infer(ty::IntVar(v_id))) => {
+            infcx.instantiate_int_var_raw(v_id, ty::IntVarValue::UintType(v));
+            Ok(a)
+        }
+
+        // Relate floating-point variables to other types
+        (ty::Infer(ty::FloatVar(a_id)), ty::Infer(ty::FloatVar(b_id))) => {
+            infcx.equate_float_vids_raw(a_id, b_id);
+            Ok(a)
+        }
+        (ty::Infer(ty::FloatVar(v_id)), ty::Float(v)) => {
+            infcx.instantiate_float_var_raw(v_id, ty::FloatVarValue::Known(v));
+            Ok(b)
+        }
+        (ty::Float(v), ty::Infer(ty::FloatVar(v_id))) => {
+            infcx.instantiate_float_var_raw(v_id, ty::FloatVarValue::Known(v));
+            Ok(a)
+        }
+
+        // We don't expect `TyVar` or `Fresh*` vars at this point with lazy norm.
+        (ty::Alias(..), ty::Infer(ty::TyVar(_))) | (ty::Infer(ty::TyVar(_)), ty::Alias(..))
+            if infcx.next_trait_solver() =>
+        {
+            panic!(
+                "We do not expect to encounter `TyVar` this late in combine \
+                    -- they should have been handled earlier"
+            )
+        }
+        (_, ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)))
+        | (ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)), _)
+            if infcx.next_trait_solver() =>
+        {
+            panic!("We do not expect to encounter `Fresh` variables in the new solver")
+        }
+
+        (_, ty::Alias(..)) | (ty::Alias(..), _) if infcx.next_trait_solver() => {
+            match relation.structurally_relate_aliases() {
+                StructurallyRelateAliases::Yes => structurally_relate_tys(relation, a, b),
+                StructurallyRelateAliases::No => {
+                    relation.register_alias_relate_predicate(a, b);
+                    Ok(a)
+                }
+            }
+        }
+
+        // All other cases of inference are errors
+        (ty::Infer(_), _) | (_, ty::Infer(_)) => {
+            Err(TypeError::Sorts(ExpectedFound::new(true, a, b)))
+        }
+
+        (ty::Alias(ty::Opaque, _), _) | (_, ty::Alias(ty::Opaque, _)) => {
+            match infcx.solver_mode() {
+                SolverMode::Normal => {
+                    assert!(!infcx.next_trait_solver());
+                    structurally_relate_tys(relation, a, b)
+                }
+                // During coherence, opaque types should be treated as *possibly*
+                // equal to any other type (except for possibly itinfcx). This is an
+                // extremely heavy hammer, but can be relaxed in a forwards-compatible
+                // way later.
+                SolverMode::Coherence => {
+                    relation.register_predicates([ty::Binder::dummy(ty::PredicateKind::Ambiguous)]);
+                    Ok(a)
+                }
+            }
+        }
+
+        _ => structurally_relate_tys(relation, a, b),
+    }
+}
+
+pub fn super_combine_consts<Infcx, I, R>(
+    infcx: &Infcx,
+    relation: &mut R,
+    a: I::Const,
+    b: I::Const,
+) -> RelateResult<I, I::Const>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+    R: PredicateEmittingRelation<Infcx>,
+{
+    debug!("super_combine_consts::<{}>({:?}, {:?})", std::any::type_name::<R>(), a, b);
+    debug_assert!(!a.has_escaping_bound_vars());
+    debug_assert!(!b.has_escaping_bound_vars());
+
+    if a == b {
+        return Ok(a);
+    }
+
+    let a = infcx.shallow_resolve_const(a);
+    let b = infcx.shallow_resolve_const(b);
+
+    match (a.kind(), b.kind()) {
+        (
+            ty::ConstKind::Infer(ty::InferConst::Var(a_vid)),
+            ty::ConstKind::Infer(ty::InferConst::Var(b_vid)),
+        ) => {
+            infcx.equate_const_vids_raw(a_vid, b_vid);
+            Ok(a)
+        }
+
+        (
+            ty::ConstKind::Infer(ty::InferConst::EffectVar(a_vid)),
+            ty::ConstKind::Infer(ty::InferConst::EffectVar(b_vid)),
+        ) => {
+            infcx.equate_effect_vids_raw(a_vid, b_vid);
+            Ok(a)
+        }
+
+        // All other cases of inference with other variables are errors.
+        (
+            ty::ConstKind::Infer(ty::InferConst::Var(_) | ty::InferConst::EffectVar(_)),
+            ty::ConstKind::Infer(_),
+        )
+        | (
+            ty::ConstKind::Infer(_),
+            ty::ConstKind::Infer(ty::InferConst::Var(_) | ty::InferConst::EffectVar(_)),
+        ) => {
+            panic!(
+                "tried to combine ConstKind::Infer/ConstKind::Infer(InferConst::Var): {a:?} and {b:?}"
+            )
+        }
+
+        (ty::ConstKind::Infer(ty::InferConst::Var(vid)), _) => {
+            infcx.instantiate_const_var_raw(relation, true, vid, b)?;
+            Ok(b)
+        }
+
+        (_, ty::ConstKind::Infer(ty::InferConst::Var(vid))) => {
+            infcx.instantiate_const_var_raw(relation, false, vid, a)?;
+            Ok(a)
+        }
+
+        (ty::ConstKind::Infer(ty::InferConst::EffectVar(vid)), _) => {
+            infcx.instantiate_effect_var_raw(vid, b);
+            Ok(b)
+        }
+
+        (_, ty::ConstKind::Infer(ty::InferConst::EffectVar(vid))) => {
+            infcx.instantiate_effect_var_raw(vid, a);
+            Ok(a)
+        }
+
+        (ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..))
+            if infcx.cx().features().generic_const_exprs() || infcx.next_trait_solver() =>
+        {
+            match relation.structurally_relate_aliases() {
+                StructurallyRelateAliases::No => {
+                    relation.register_predicates([if infcx.next_trait_solver() {
+                        ty::PredicateKind::AliasRelate(
+                            a.into(),
+                            b.into(),
+                            ty::AliasRelationDirection::Equate,
+                        )
+                    } else {
+                        ty::PredicateKind::ConstEquate(a, b)
+                    }]);
+
+                    Ok(b)
+                }
+                StructurallyRelateAliases::Yes => structurally_relate_consts(relation, a, b),
+            }
+        }
+        _ => structurally_relate_consts(relation, a, b),
+    }
+}
diff --git a/compiler/rustc_type_ir/src/relate/solver_relating.rs b/compiler/rustc_type_ir/src/relate/solver_relating.rs
new file mode 100644
index 00000000000..ad10ad5af9c
--- /dev/null
+++ b/compiler/rustc_type_ir/src/relate/solver_relating.rs
@@ -0,0 +1,394 @@
+pub use rustc_type_ir::relate::*;
+use rustc_type_ir::solve::Goal;
+use rustc_type_ir::{self as ty, InferCtxtLike, Interner};
+use tracing::{debug, instrument};
+
+use self::combine::{PredicateEmittingRelation, super_combine_consts, super_combine_tys};
+use crate::data_structures::DelayedSet;
+
+pub trait RelateExt: InferCtxtLike {
+    fn relate<T: Relate<Self::Interner>>(
+        &self,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        lhs: T,
+        variance: ty::Variance,
+        rhs: T,
+    ) -> Result<
+        Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
+        TypeError<Self::Interner>,
+    >;
+
+    fn eq_structurally_relating_aliases<T: Relate<Self::Interner>>(
+        &self,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        lhs: T,
+        rhs: T,
+    ) -> Result<
+        Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
+        TypeError<Self::Interner>,
+    >;
+}
+
+impl<Infcx: InferCtxtLike> RelateExt for Infcx {
+    fn relate<T: Relate<Self::Interner>>(
+        &self,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        lhs: T,
+        variance: ty::Variance,
+        rhs: T,
+    ) -> Result<
+        Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
+        TypeError<Self::Interner>,
+    > {
+        let mut relate =
+            SolverRelating::new(self, StructurallyRelateAliases::No, variance, param_env);
+        relate.relate(lhs, rhs)?;
+        Ok(relate.goals)
+    }
+
+    fn eq_structurally_relating_aliases<T: Relate<Self::Interner>>(
+        &self,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        lhs: T,
+        rhs: T,
+    ) -> Result<
+        Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
+        TypeError<Self::Interner>,
+    > {
+        let mut relate =
+            SolverRelating::new(self, StructurallyRelateAliases::Yes, ty::Invariant, param_env);
+        relate.relate(lhs, rhs)?;
+        Ok(relate.goals)
+    }
+}
+
+/// Enforce that `a` is equal to or a subtype of `b`.
+pub struct SolverRelating<'infcx, Infcx, I: Interner> {
+    infcx: &'infcx Infcx,
+    // Immutable fields.
+    structurally_relate_aliases: StructurallyRelateAliases,
+    param_env: I::ParamEnv,
+    // Mutable fields.
+    ambient_variance: ty::Variance,
+    goals: Vec<Goal<I, I::Predicate>>,
+    /// The cache only tracks the `ambient_variance` as it's the
+    /// only field which is mutable and which meaningfully changes
+    /// the result when relating types.
+    ///
+    /// The cache does not track whether the state of the
+    /// `Infcx` has been changed or whether we've added any
+    /// goals to `self.goals`. Whether a goal is added once or multiple
+    /// times is not really meaningful.
+    ///
+    /// Changes in the inference state may delay some type inference to
+    /// the next fulfillment loop. Given that this loop is already
+    /// necessary, this is also not a meaningful change. Consider
+    /// the following three relations:
+    /// ```text
+    /// Vec<?0> sub Vec<?1>
+    /// ?0 eq u32
+    /// Vec<?0> sub Vec<?1>
+    /// ```
+    /// Without a cache, the second `Vec<?0> sub Vec<?1>` would eagerly
+    /// constrain `?1` to `u32`. When using the cache entry from the
+    /// first time we've related these types, this only happens when
+    /// later proving the `Subtype(?0, ?1)` goal from the first relation.
+    cache: DelayedSet<(ty::Variance, I::Ty, I::Ty)>,
+}
+
+impl<'infcx, Infcx, I> SolverRelating<'infcx, Infcx, I>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
+    pub fn new(
+        infcx: &'infcx Infcx,
+        structurally_relate_aliases: StructurallyRelateAliases,
+        ambient_variance: ty::Variance,
+        param_env: I::ParamEnv,
+    ) -> Self {
+        SolverRelating {
+            infcx,
+            structurally_relate_aliases,
+            ambient_variance,
+            param_env,
+            goals: vec![],
+            cache: Default::default(),
+        }
+    }
+}
+
+impl<Infcx, I> TypeRelation<I> for SolverRelating<'_, Infcx, I>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
+    fn cx(&self) -> I {
+        self.infcx.cx()
+    }
+
+    fn relate_item_args(
+        &mut self,
+        item_def_id: I::DefId,
+        a_arg: I::GenericArgs,
+        b_arg: I::GenericArgs,
+    ) -> RelateResult<I, I::GenericArgs> {
+        if self.ambient_variance == ty::Invariant {
+            // Avoid fetching the variance if we are in an invariant
+            // context; no need, and it can induce dependency cycles
+            // (e.g., #41849).
+            relate_args_invariantly(self, a_arg, b_arg)
+        } else {
+            let tcx = self.cx();
+            let opt_variances = tcx.variances_of(item_def_id);
+            relate_args_with_variances(self, item_def_id, opt_variances, a_arg, b_arg, false)
+        }
+    }
+
+    fn relate_with_variance<T: Relate<I>>(
+        &mut self,
+        variance: ty::Variance,
+        _info: VarianceDiagInfo<I>,
+        a: T,
+        b: T,
+    ) -> RelateResult<I, T> {
+        let old_ambient_variance = self.ambient_variance;
+        self.ambient_variance = self.ambient_variance.xform(variance);
+        debug!(?self.ambient_variance, "new ambient variance");
+
+        let r = if self.ambient_variance == ty::Bivariant { Ok(a) } else { self.relate(a, b) };
+
+        self.ambient_variance = old_ambient_variance;
+        r
+    }
+
+    #[instrument(skip(self), level = "trace")]
+    fn tys(&mut self, a: I::Ty, b: I::Ty) -> RelateResult<I, I::Ty> {
+        if a == b {
+            return Ok(a);
+        }
+
+        let infcx = self.infcx;
+        let a = infcx.shallow_resolve(a);
+        let b = infcx.shallow_resolve(b);
+
+        if self.cache.contains(&(self.ambient_variance, a, b)) {
+            return Ok(a);
+        }
+
+        match (a.kind(), b.kind()) {
+            (ty::Infer(ty::TyVar(a_id)), ty::Infer(ty::TyVar(b_id))) => {
+                match self.ambient_variance {
+                    ty::Covariant => {
+                        // can't make progress on `A <: B` if both A and B are
+                        // type variables, so record an obligation.
+                        self.goals.push(Goal::new(
+                            self.cx(),
+                            self.param_env,
+                            ty::Binder::dummy(ty::PredicateKind::Subtype(ty::SubtypePredicate {
+                                a_is_expected: true,
+                                a,
+                                b,
+                            })),
+                        ));
+                    }
+                    ty::Contravariant => {
+                        // can't make progress on `B <: A` if both A and B are
+                        // type variables, so record an obligation.
+                        self.goals.push(Goal::new(
+                            self.cx(),
+                            self.param_env,
+                            ty::Binder::dummy(ty::PredicateKind::Subtype(ty::SubtypePredicate {
+                                a_is_expected: false,
+                                a: b,
+                                b: a,
+                            })),
+                        ));
+                    }
+                    ty::Invariant => {
+                        infcx.equate_ty_vids_raw(a_id, b_id);
+                    }
+                    ty::Bivariant => {
+                        unreachable!("Expected bivariance to be handled in relate_with_variance")
+                    }
+                }
+            }
+
+            (ty::Infer(ty::TyVar(a_vid)), _) => {
+                infcx.instantiate_ty_var_raw(self, true, a_vid, self.ambient_variance, b)?;
+            }
+            (_, ty::Infer(ty::TyVar(b_vid))) => {
+                infcx.instantiate_ty_var_raw(
+                    self,
+                    false,
+                    b_vid,
+                    self.ambient_variance.xform(ty::Contravariant),
+                    a,
+                )?;
+            }
+
+            _ => {
+                super_combine_tys(self.infcx, self, a, b)?;
+            }
+        }
+
+        assert!(self.cache.insert((self.ambient_variance, a, b)));
+
+        Ok(a)
+    }
+
+    #[instrument(skip(self), level = "trace")]
+    fn regions(&mut self, a: I::Region, b: I::Region) -> RelateResult<I, I::Region> {
+        match self.ambient_variance {
+            // Subtype(&'a u8, &'b u8) => Outlives('a: 'b) => SubRegion('b, 'a)
+            ty::Covariant => self.infcx.sub_regions(b, a),
+            // Suptype(&'a u8, &'b u8) => Outlives('b: 'a) => SubRegion('a, 'b)
+            ty::Contravariant => self.infcx.sub_regions(a, b),
+            ty::Invariant => self.infcx.equate_regions(a, b),
+            ty::Bivariant => {
+                unreachable!("Expected bivariance to be handled in relate_with_variance")
+            }
+        }
+
+        Ok(a)
+    }
+
+    #[instrument(skip(self), level = "trace")]
+    fn consts(&mut self, a: I::Const, b: I::Const) -> RelateResult<I, I::Const> {
+        super_combine_consts(self.infcx, self, a, b)
+    }
+
+    fn binders<T>(
+        &mut self,
+        a: ty::Binder<I, T>,
+        b: ty::Binder<I, T>,
+    ) -> RelateResult<I, ty::Binder<I, T>>
+    where
+        T: Relate<I>,
+    {
+        // If they're equal, then short-circuit.
+        if a == b {
+            return Ok(a);
+        }
+
+        // If they have no bound vars, relate normally.
+        if let Some(a_inner) = a.no_bound_vars() {
+            if let Some(b_inner) = b.no_bound_vars() {
+                self.relate(a_inner, b_inner)?;
+                return Ok(a);
+            }
+        };
+
+        match self.ambient_variance {
+            // Checks whether `for<..> sub <: for<..> sup` holds.
+            //
+            // For this to hold, **all** instantiations of the super type
+            // have to be a super type of **at least one** instantiation of
+            // the subtype.
+            //
+            // This is implemented by first entering a new universe.
+            // We then replace all bound variables in `sup` with placeholders,
+            // and all bound variables in `sub` with inference vars.
+            // We can then just relate the two resulting types as normal.
+            //
+            // Note: this is a subtle algorithm. For a full explanation, please see
+            // the [rustc dev guide][rd]
+            //
+            // [rd]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/placeholders_and_universes.html
+            ty::Covariant => {
+                self.infcx.enter_forall(b, |b| {
+                    let a = self.infcx.instantiate_binder_with_infer(a);
+                    self.relate(a, b)
+                })?;
+            }
+            ty::Contravariant => {
+                self.infcx.enter_forall(a, |a| {
+                    let b = self.infcx.instantiate_binder_with_infer(b);
+                    self.relate(a, b)
+                })?;
+            }
+
+            // When **equating** binders, we check that there is a 1-to-1
+            // correspondence between the bound vars in both types.
+            //
+            // We do so by separately instantiating one of the binders with
+            // placeholders and the other with inference variables and then
+            // equating the instantiated types.
+            //
+            // We want `for<..> A == for<..> B` -- therefore we want
+            // `exists<..> A == for<..> B` and `exists<..> B == for<..> A`.
+            // Check if `exists<..> A == for<..> B`
+            ty::Invariant => {
+                self.infcx.enter_forall(b, |b| {
+                    let a = self.infcx.instantiate_binder_with_infer(a);
+                    self.relate(a, b)
+                })?;
+
+                // Check if `exists<..> B == for<..> A`.
+                self.infcx.enter_forall(a, |a| {
+                    let b = self.infcx.instantiate_binder_with_infer(b);
+                    self.relate(a, b)
+                })?;
+            }
+            ty::Bivariant => {
+                unreachable!("Expected bivariance to be handled in relate_with_variance")
+            }
+        }
+        Ok(a)
+    }
+}
+
+impl<Infcx, I> PredicateEmittingRelation<Infcx> for SolverRelating<'_, Infcx, I>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
+    fn span(&self) -> I::Span {
+        Span::dummy()
+    }
+
+    fn param_env(&self) -> I::ParamEnv {
+        self.param_env
+    }
+
+    fn structurally_relate_aliases(&self) -> StructurallyRelateAliases {
+        self.structurally_relate_aliases
+    }
+
+    fn register_predicates(
+        &mut self,
+        obligations: impl IntoIterator<Item: ty::Upcast<I, I::Predicate>>,
+    ) {
+        self.goals.extend(
+            obligations.into_iter().map(|pred| Goal::new(self.infcx.cx(), self.param_env, pred)),
+        );
+    }
+
+    fn register_goals(&mut self, obligations: impl IntoIterator<Item = Goal<I, I::Predicate>>) {
+        self.goals.extend(obligations);
+    }
+
+    fn register_alias_relate_predicate(&mut self, a: I::Ty, b: I::Ty) {
+        self.register_predicates([ty::Binder::dummy(match self.ambient_variance {
+            ty::Covariant => ty::PredicateKind::AliasRelate(
+                a.into(),
+                b.into(),
+                ty::AliasRelationDirection::Subtype,
+            ),
+            // a :> b is b <: a
+            ty::Contravariant => ty::PredicateKind::AliasRelate(
+                b.into(),
+                a.into(),
+                ty::AliasRelationDirection::Subtype,
+            ),
+            ty::Invariant => ty::PredicateKind::AliasRelate(
+                a.into(),
+                b.into(),
+                ty::AliasRelationDirection::Equate,
+            ),
+            ty::Bivariant => {
+                unreachable!("Expected bivariance to be handled in relate_with_variance")
+            }
+        })]);
+    }
+}
diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs
index ac4d0795a92..f4fb03562de 100644
--- a/compiler/rustc_type_ir/src/search_graph/mod.rs
+++ b/compiler/rustc_type_ir/src/search_graph/mod.rs
@@ -81,7 +81,6 @@ pub trait Delegate {
     fn inspect_is_noop(inspect: &mut Self::ProofTreeBuilder) -> bool;
 
     const DIVIDE_AVAILABLE_DEPTH_ON_OVERFLOW: usize;
-    fn recursion_limit(cx: Self::Cx) -> usize;
 
     fn initial_provisional_result(
         cx: Self::Cx,
@@ -156,7 +155,7 @@ impl AvailableDepth {
     /// the remaining depth of all nested goals to prevent hangs
     /// in case there is exponential blowup.
     fn allowed_depth_for_nested<D: Delegate>(
-        cx: D::Cx,
+        root_depth: AvailableDepth,
         stack: &IndexVec<StackDepth, StackEntry<D::Cx>>,
     ) -> Option<AvailableDepth> {
         if let Some(last) = stack.raw.last() {
@@ -170,7 +169,7 @@ impl AvailableDepth {
                 AvailableDepth(last.available_depth.0 - 1)
             })
         } else {
-            Some(AvailableDepth(D::recursion_limit(cx)))
+            Some(root_depth)
         }
     }
 
@@ -360,6 +359,7 @@ struct ProvisionalCacheEntry<X: Cx> {
 
 pub struct SearchGraph<D: Delegate<Cx = X>, X: Cx = <D as Delegate>::Cx> {
     mode: SolverMode,
+    root_depth: AvailableDepth,
     /// The stack of goals currently being computed.
     ///
     /// An element is *deeper* in the stack if its index is *lower*.
@@ -374,9 +374,10 @@ pub struct SearchGraph<D: Delegate<Cx = X>, X: Cx = <D as Delegate>::Cx> {
 }
 
 impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
-    pub fn new(mode: SolverMode) -> SearchGraph<D> {
+    pub fn new(mode: SolverMode, root_depth: usize) -> SearchGraph<D> {
         Self {
             mode,
+            root_depth: AvailableDepth(root_depth),
             stack: Default::default(),
             provisional_cache: Default::default(),
             _marker: PhantomData,
@@ -460,7 +461,8 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
         inspect: &mut D::ProofTreeBuilder,
         mut evaluate_goal: impl FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result,
     ) -> X::Result {
-        let Some(available_depth) = AvailableDepth::allowed_depth_for_nested::<D>(cx, &self.stack)
+        let Some(available_depth) =
+            AvailableDepth::allowed_depth_for_nested::<D>(self.root_depth, &self.stack)
         else {
             return self.handle_overflow(cx, input, inspect);
         };
diff --git a/compiler/rustc_type_ir/src/solve/inspect.rs b/compiler/rustc_type_ir/src/solve/inspect.rs
index 099c66f6bdc..138ba8bac88 100644
--- a/compiler/rustc_type_ir/src/solve/inspect.rs
+++ b/compiler/rustc_type_ir/src/solve/inspect.rs
@@ -135,4 +135,6 @@ pub enum ProbeKind<I: Interner> {
     ShadowedEnvProbing,
     /// Try to unify an opaque type with an existing key in the storage.
     OpaqueTypeStorageLookup { result: QueryResult<I> },
+    /// Checking that a rigid alias is well-formed.
+    RigidAlias { result: QueryResult<I> },
 }
diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs
index a0f7658212f..f02c7a32071 100644
--- a/compiler/rustc_type_ir/src/solve/mod.rs
+++ b/compiler/rustc_type_ir/src/solve/mod.rs
@@ -130,6 +130,10 @@ pub enum GoalSource {
     ImplWhereBound,
     /// Instantiating a higher-ranked goal and re-proving it.
     InstantiateHigherRanked,
+    /// Predicate required for an alias projection to be well-formed.
+    /// This is used in two places: projecting to an opaque whose hidden type
+    /// is already registered in the opaque type storage, and for rigid projections.
+    AliasWellFormed,
 }
 
 #[derive_where(Clone; I: Interner, Goal<I, P>: Clone)]
diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs
index 0ba7e2bd552..71c3646498b 100644
--- a/compiler/rustc_type_ir/src/visit.rs
+++ b/compiler/rustc_type_ir/src/visit.rs
@@ -47,6 +47,7 @@ use std::ops::ControlFlow;
 use rustc_ast_ir::visit::VisitorResult;
 use rustc_ast_ir::{try_visit, walk_visitable_list};
 use rustc_index::{Idx, IndexVec};
+use thin_vec::ThinVec;
 
 use crate::data_structures::Lrc;
 use crate::inherent::*;
@@ -184,6 +185,13 @@ impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Vec<T> {
     }
 }
 
+impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for ThinVec<T> {
+    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
+        walk_visitable_list!(visitor, self.iter());
+        V::Result::output()
+    }
+}
+
 // `TypeFoldable` isn't impl'd for `&[T]`. It doesn't make sense in the general
 // case, because we can't return a new slice. But note that there are a couple
 // of trivial impls of `TypeFoldable` for specific slice types elsewhere.
diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs
index 742469a1c93..f96487cc53c 100644
--- a/compiler/stable_mir/src/mir/body.rs
+++ b/compiler/stable_mir/src/mir/body.rs
@@ -1028,7 +1028,7 @@ impl ProjectionElem {
             ProjectionElem::Field(_idx, fty) => Ok(*fty),
             ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => Self::index_ty(ty),
             ProjectionElem::Subslice { from, to, from_end } => {
-                Self::subslice_ty(ty, from, to, from_end)
+                Self::subslice_ty(ty, *from, *to, *from_end)
             }
             ProjectionElem::Downcast(_) => Ok(ty),
             ProjectionElem::OpaqueCast(ty) | ProjectionElem::Subtype(ty) => Ok(*ty),
@@ -1039,13 +1039,13 @@ impl ProjectionElem {
         ty.kind().builtin_index().ok_or_else(|| error!("Cannot index non-array type: {ty:?}"))
     }
 
-    fn subslice_ty(ty: Ty, from: &u64, to: &u64, from_end: &bool) -> Result<Ty, Error> {
+    fn subslice_ty(ty: Ty, from: u64, to: u64, from_end: bool) -> Result<Ty, Error> {
         let ty_kind = ty.kind();
         match ty_kind {
             TyKind::RigidTy(RigidTy::Slice(..)) => Ok(ty),
             TyKind::RigidTy(RigidTy::Array(inner, _)) if !from_end => Ty::try_new_array(
                 inner,
-                to.checked_sub(*from).ok_or_else(|| error!("Subslice overflow: {from}..{to}"))?,
+                to.checked_sub(from).ok_or_else(|| error!("Subslice overflow: {from}..{to}"))?,
             ),
             TyKind::RigidTy(RigidTy::Array(inner, size)) => {
                 let size = size.eval_target_usize()?;
diff --git a/compiler/stable_mir/src/mir/pretty.rs b/compiler/stable_mir/src/mir/pretty.rs
index 74081af1d86..13e3d229d06 100644
--- a/compiler/stable_mir/src/mir/pretty.rs
+++ b/compiler/stable_mir/src/mir/pretty.rs
@@ -22,7 +22,7 @@ impl Debug for Place {
 }
 
 pub(crate) fn function_body<W: Write>(writer: &mut W, body: &Body, name: &str) -> io::Result<()> {
-    write!(writer, "fn {}(", name)?;
+    write!(writer, "fn {name}(")?;
     body.arg_locals()
         .iter()
         .enumerate()
@@ -54,7 +54,7 @@ pub(crate) fn function_body<W: Write>(writer: &mut W, body: &Body, name: &str) -
         .iter()
         .enumerate()
         .map(|(index, block)| -> io::Result<()> {
-            writeln!(writer, "    bb{}: {{", index)?;
+            writeln!(writer, "    bb{index}: {{")?;
             let _ = block
                 .statements
                 .iter()
@@ -75,7 +75,7 @@ pub(crate) fn function_body<W: Write>(writer: &mut W, body: &Body, name: &str) -
 fn pretty_statement<W: Write>(writer: &mut W, statement: &StatementKind) -> io::Result<()> {
     match statement {
         StatementKind::Assign(place, rval) => {
-            write!(writer, "        {:?} = ", place)?;
+            write!(writer, "        {place:?} = ")?;
             pretty_rvalue(writer, rval)?;
             writeln!(writer, ";")
         }
@@ -165,7 +165,7 @@ fn pretty_terminator_head<W: Write>(writer: &mut W, terminator: &TerminatorKind)
         Abort => write!(writer, "{INDENT}abort"),
         Return => write!(writer, "{INDENT}return"),
         Unreachable => write!(writer, "{INDENT}unreachable"),
-        Drop { place, .. } => write!(writer, "{INDENT}drop({:?})", place),
+        Drop { place, .. } => write!(writer, "{INDENT}drop({place:?})"),
         Call { func, args, destination, .. } => {
             write!(writer, "{INDENT}{:?} = {}(", destination, pretty_operand(func))?;
             let mut args_iter = args.iter();
@@ -189,7 +189,9 @@ fn pretty_terminator_head<W: Write>(writer: &mut W, terminator: &TerminatorKind)
 fn pretty_successor_labels(terminator: &TerminatorKind) -> Vec<String> {
     use self::TerminatorKind::*;
     match terminator {
-        Resume | Abort | Return | Unreachable => vec![],
+        Call { target: None, unwind: UnwindAction::Cleanup(_), .. }
+        | InlineAsm { destination: None, .. } => vec!["unwind".into()],
+        Resume | Abort | Return | Unreachable | Call { target: None, unwind: _, .. } => vec![],
         Goto { .. } => vec!["".to_string()],
         SwitchInt { targets, .. } => targets
             .branches()
@@ -197,19 +199,15 @@ fn pretty_successor_labels(terminator: &TerminatorKind) -> Vec<String> {
             .chain(iter::once("otherwise".into()))
             .collect(),
         Drop { unwind: UnwindAction::Cleanup(_), .. } => vec!["return".into(), "unwind".into()],
-        Drop { unwind: _, .. } => vec!["return".into()],
         Call { target: Some(_), unwind: UnwindAction::Cleanup(_), .. } => {
             vec!["return".into(), "unwind".into()]
         }
-        Call { target: Some(_), unwind: _, .. } => vec!["return".into()],
-        Call { target: None, unwind: UnwindAction::Cleanup(_), .. } => vec!["unwind".into()],
-        Call { target: None, unwind: _, .. } => vec![],
+        Drop { unwind: _, .. } | Call { target: Some(_), unwind: _, .. } => vec!["return".into()],
         Assert { unwind: UnwindAction::Cleanup(_), .. } => {
             vec!["success".into(), "unwind".into()]
         }
         Assert { unwind: _, .. } => vec!["success".into()],
         InlineAsm { destination: Some(_), .. } => vec!["goto".into(), "unwind".into()],
-        InlineAsm { destination: None, .. } => vec!["unwind".into()],
     }
 }
 
@@ -304,10 +302,10 @@ fn pretty_assert_message<W: Write>(writer: &mut W, msg: &AssertMessage) -> io::R
 fn pretty_operand(operand: &Operand) -> String {
     match operand {
         Operand::Copy(copy) => {
-            format!("{:?}", copy)
+            format!("{copy:?}")
         }
         Operand::Move(mv) => {
-            format!("move {:?}", mv)
+            format!("move {mv:?}")
         }
         Operand::Constant(cnst) => pretty_mir_const(&cnst.const_),
     }
@@ -344,13 +342,13 @@ fn pretty_rvalue<W: Write>(writer: &mut W, rval: &Rvalue) -> io::Result<()> {
             write!(writer, "Checked{:?}({}, {})", bin, pretty_operand(op1), pretty_operand(op2))
         }
         Rvalue::CopyForDeref(deref) => {
-            write!(writer, "CopyForDeref({:?})", deref)
+            write!(writer, "CopyForDeref({deref:?})")
         }
         Rvalue::Discriminant(place) => {
-            write!(writer, "discriminant({:?})", place)
+            write!(writer, "discriminant({place:?})")
         }
         Rvalue::Len(len) => {
-            write!(writer, "len({:?})", len)
+            write!(writer, "len({len:?})")
         }
         Rvalue::Ref(_, borrowkind, place) => {
             let kind = match borrowkind {
@@ -359,17 +357,17 @@ fn pretty_rvalue<W: Write>(writer: &mut W, rval: &Rvalue) -> io::Result<()> {
                 BorrowKind::Fake(FakeBorrowKind::Shallow) => "&fake shallow ",
                 BorrowKind::Mut { .. } => "&mut ",
             };
-            write!(writer, "{kind}{:?}", place)
+            write!(writer, "{kind}{place:?}")
         }
         Rvalue::Repeat(op, cnst) => {
             write!(writer, "{} \" \" {}", pretty_operand(op), pretty_ty_const(cnst))
         }
         Rvalue::ShallowInitBox(_, _) => Ok(()),
         Rvalue::ThreadLocalRef(item) => {
-            write!(writer, "thread_local_ref{:?}", item)
+            write!(writer, "thread_local_ref{item:?}")
         }
         Rvalue::NullaryOp(nul, ty) => {
-            write!(writer, "{:?} {} \" \"", nul, ty)
+            write!(writer, "{nul:?} {ty} \" \"")
         }
         Rvalue::UnaryOp(un, op) => {
             write!(writer, "{} \" \" {:?}", pretty_operand(op), un)
diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs
index e2d1ff7fdd3..d73e79c4893 100644
--- a/compiler/stable_mir/src/mir/visit.rs
+++ b/compiler/stable_mir/src/mir/visit.rs
@@ -194,27 +194,17 @@ pub trait MirVisitor {
                 self.visit_place(place, PlaceContext::MUTATING, location);
                 self.visit_rvalue(rvalue, location);
             }
-            StatementKind::FakeRead(_, place) => {
+            StatementKind::FakeRead(_, place) | StatementKind::PlaceMention(place) => {
                 self.visit_place(place, PlaceContext::NON_MUTATING, location);
             }
-            StatementKind::SetDiscriminant { place, .. } => {
+            StatementKind::SetDiscriminant { place, .. }
+            | StatementKind::Deinit(place)
+            | StatementKind::Retag(_, place) => {
                 self.visit_place(place, PlaceContext::MUTATING, location);
             }
-            StatementKind::Deinit(place) => {
-                self.visit_place(place, PlaceContext::MUTATING, location);
-            }
-            StatementKind::StorageLive(local) => {
-                self.visit_local(local, PlaceContext::NON_USE, location);
-            }
-            StatementKind::StorageDead(local) => {
+            StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
                 self.visit_local(local, PlaceContext::NON_USE, location);
             }
-            StatementKind::Retag(_, place) => {
-                self.visit_place(place, PlaceContext::MUTATING, location);
-            }
-            StatementKind::PlaceMention(place) => {
-                self.visit_place(place, PlaceContext::NON_MUTATING, location);
-            }
             StatementKind::AscribeUserType { place, projections, variance: _ } => {
                 self.visit_place(place, PlaceContext::NON_USE, location);
                 self.visit_user_type_projection(projections);
@@ -234,8 +224,7 @@ pub trait MirVisitor {
                     self.visit_operand(count, location);
                 }
             },
-            StatementKind::ConstEvalCounter => {}
-            StatementKind::Nop => {}
+            StatementKind::ConstEvalCounter | StatementKind::Nop => {}
         }
     }
 
@@ -304,14 +293,15 @@ pub trait MirVisitor {
         location: Location,
     ) {
         match elem {
-            ProjectionElem::Deref => {}
+            ProjectionElem::Downcast(_idx) => {}
+            ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ }
+            | ProjectionElem::Deref
+            | ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {}
             ProjectionElem::Field(_idx, ty) => self.visit_ty(ty, location),
             ProjectionElem::Index(local) => self.visit_local(local, ptx, location),
-            ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ } => {}
-            ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {}
-            ProjectionElem::Downcast(_idx) => {}
-            ProjectionElem::OpaqueCast(ty) => self.visit_ty(ty, location),
-            ProjectionElem::Subtype(ty) => self.visit_ty(ty, location),
+            ProjectionElem::OpaqueCast(ty) | ProjectionElem::Subtype(ty) => {
+                self.visit_ty(ty, location)
+            }
         }
     }
 
@@ -487,7 +477,7 @@ pub struct PlaceRef<'a> {
     pub projection: &'a [ProjectionElem],
 }
 
-impl<'a> 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?))
diff --git a/compiler/stable_mir/src/visitor.rs b/compiler/stable_mir/src/visitor.rs
index 3c769b2d37c..48260285408 100644
--- a/compiler/stable_mir/src/visitor.rs
+++ b/compiler/stable_mir/src/visitor.rs
@@ -35,8 +35,7 @@ impl Visitable for Ty {
         match self.kind() {
             super::ty::TyKind::RigidTy(ty) => ty.visit(visitor)?,
             super::ty::TyKind::Alias(_, alias) => alias.args.visit(visitor)?,
-            super::ty::TyKind::Param(_) => {}
-            super::ty::TyKind::Bound(_, _) => {}
+            super::ty::TyKind::Param(_) | super::ty::TyKind::Bound(_, _) => {}
         }
         ControlFlow::Continue(())
     }
@@ -48,8 +47,7 @@ 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::Param(_) | crate::ty::TyConstKind::Bound(_, _) => {}
             crate::ty::TyConstKind::Unevaluated(_, args) => args.visit(visitor)?,
             crate::ty::TyConstKind::Value(ty, alloc) => {
                 alloc.visit(visitor)?;
@@ -166,17 +164,17 @@ impl Visitable for RigidTy {
                 reg.visit(visitor);
                 ty.visit(visitor)
             }
-            RigidTy::FnDef(_, args) => args.visit(visitor),
+            RigidTy::Adt(_, args)
+            | RigidTy::Closure(_, args)
+            | RigidTy::Coroutine(_, args, _)
+            | RigidTy::CoroutineWitness(_, args)
+            | RigidTy::FnDef(_, args) => args.visit(visitor),
             RigidTy::FnPtr(sig) => sig.visit(visitor),
-            RigidTy::Closure(_, args) => args.visit(visitor),
-            RigidTy::Coroutine(_, args, _) => args.visit(visitor),
-            RigidTy::CoroutineWitness(_, args) => args.visit(visitor),
             RigidTy::Dynamic(pred, r, _) => {
                 pred.visit(visitor)?;
                 r.visit(visitor)
             }
             RigidTy::Tuple(fields) => fields.visit(visitor),
-            RigidTy::Adt(_, args) => args.visit(visitor),
         }
     }
 }