about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.mailmap4
-rw-r--r--Cargo.lock9
-rw-r--r--compiler/rustc_abi/src/lib.rs41
-rw-r--r--compiler/rustc_ast/src/ast.rs4
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs2
-rw-r--r--compiler/rustc_ast/src/util/classify.rs2
-rw-r--r--compiler/rustc_ast/src/visit.rs2
-rw-r--r--compiler/rustc_ast_lowering/messages.ftl4
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs6
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs2
-rw-r--r--compiler/rustc_ast_passes/messages.ftl10
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs103
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs19
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs2
-rw-r--r--compiler/rustc_borrowck/messages.ftl11
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs50
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs126
-rw-r--r--compiler/rustc_borrowck/src/nll.rs6
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types.rs33
-rw-r--r--compiler/rustc_borrowck/src/session_diagnostics.rs7
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs29
-rw-r--r--compiler/rustc_borrowck/src/universal_regions.rs21
-rw-r--r--compiler/rustc_builtin_macros/src/assert/context.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs12
-rw-r--r--compiler/rustc_builtin_macros/src/test_harness.rs8
-rw-r--r--compiler/rustc_codegen_cranelift/docs/usage.md2
-rw-r--r--compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml12
-rw-r--r--compiler/rustc_codegen_cranelift/rust-toolchain2
-rwxr-xr-xcompiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh2
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs7
-rw-r--r--compiler/rustc_codegen_cranelift/src/constant.rs46
-rw-r--r--compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/driver/aot.rs41
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs248
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs3
-rw-r--r--compiler/rustc_codegen_cranelift/src/vtable.rs19
-rw-r--r--compiler/rustc_codegen_gcc/src/callee.rs2
-rw-r--r--compiler/rustc_codegen_gcc/src/debuginfo.rs2
-rw-r--r--compiler/rustc_codegen_llvm/messages.ftl2
-rw-r--r--compiler/rustc_codegen_llvm/src/abi.rs39
-rw-r--r--compiler/rustc_codegen_llvm/src/back/lto.rs32
-rw-r--r--compiler/rustc_codegen_llvm/src/back/write.rs79
-rw-r--r--compiler/rustc_codegen_llvm/src/callee.rs3
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs195
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs32
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/mod.rs11
-rw-r--r--compiler/rustc_codegen_llvm/src/errors.rs6
-rw-r--r--compiler/rustc_codegen_llvm/src/lib.rs1
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs14
-rw-r--r--compiler/rustc_codegen_ssa/messages.ftl2
-rw-r--r--compiler/rustc_codegen_ssa/src/back/symbol_export.rs6
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs7
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs21
-rw-r--r--compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs42
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs7
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs41
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/constant.rs52
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/debuginfo.rs164
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/locals.rs8
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/mod.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/operand.rs10
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/debuginfo.rs2
-rw-r--r--compiler/rustc_const_eval/src/const_eval/error.rs25
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs107
-rw-r--r--compiler/rustc_const_eval/src/const_eval/valtrees.rs103
-rw-r--r--compiler/rustc_const_eval/src/errors.rs27
-rw-r--r--compiler/rustc_const_eval/src/interpret/cast.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/discriminant.rs7
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs10
-rw-r--r--compiler/rustc_const_eval/src/interpret/intern.rs14
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/machine.rs5
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs7
-rw-r--r--compiler/rustc_const_eval/src/interpret/operand.rs19
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs16
-rw-r--r--compiler/rustc_const_eval/src/interpret/step.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/terminator.rs330
-rw-r--r--compiler/rustc_const_eval/src/interpret/traits.rs2
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs58
-rw-r--r--compiler/rustc_data_structures/src/sharded.rs58
-rw-r--r--compiler/rustc_data_structures/src/sync.rs179
-rw-r--r--compiler/rustc_data_structures/src/sync/freeze.rs124
-rw-r--r--compiler/rustc_data_structures/src/sync/lock.rs375
-rw-r--r--compiler/rustc_data_structures/src/sync/parallel.rs188
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs17
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0401.md6
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0788.md16
-rw-r--r--compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs2
-rw-r--r--compiler/rustc_errors/src/emitter.rs4
-rw-r--r--compiler/rustc_errors/src/json.rs2
-rw-r--r--compiler/rustc_errors/src/lib.rs8
-rw-r--r--compiler/rustc_expand/src/expand.rs2
-rw-r--r--compiler/rustc_feature/src/active.rs8
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs6
-rw-r--r--compiler/rustc_feature/src/removed.rs3
-rw-r--r--compiler/rustc_hir/src/hir.rs4
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl34
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/errors.rs47
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs311
-rw-r--r--compiler/rustc_hir_analysis/src/check/region.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/builtin.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs93
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs5
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs88
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs2
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl6
-rw-r--r--compiler/rustc_hir_typeck/src/_match.rs23
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs9
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs26
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/fallback.rs64
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs61
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/gather_locals.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/mod.rs11
-rw-r--r--compiler/rustc_hir_typeck/src/upvar.rs46
-rw-r--r--compiler/rustc_incremental/src/persist/save.rs26
-rw-r--r--compiler/rustc_infer/src/infer/canonical/canonicalizer.rs14
-rw-r--r--compiler/rustc_infer/src/infer/canonical/mod.rs6
-rw-r--r--compiler/rustc_infer/src/infer/combine.rs69
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs6
-rw-r--r--compiler/rustc_infer/src/infer/freshen.rs15
-rw-r--r--compiler/rustc_infer/src/infer/generalize.rs1
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs83
-rw-r--r--compiler/rustc_infer/src/infer/opaque_types.rs20
-rw-r--r--compiler/rustc_infer/src/infer/undo_log.rs3
-rw-r--r--compiler/rustc_interface/src/interface.rs7
-rw-r--r--compiler/rustc_interface/src/passes.rs2
-rw-r--r--compiler/rustc_interface/src/queries.rs8
-rw-r--r--compiler/rustc_interface/src/tests.rs3
-rw-r--r--compiler/rustc_interface/src/util.rs2
-rw-r--r--compiler/rustc_lint/messages.ftl13
-rw-r--r--compiler/rustc_lint/src/context.rs67
-rw-r--r--compiler/rustc_lint/src/errors.rs54
-rw-r--r--compiler/rustc_lint/src/late.rs29
-rw-r--r--compiler/rustc_lint/src/levels.rs100
-rw-r--r--compiler/rustc_lint/src/lints.rs70
-rw-r--r--compiler/rustc_lint/src/ptr_nulls.rs52
-rw-r--r--compiler/rustc_lint/src/reference_casting.rs44
-rw-r--r--compiler/rustc_lint/src/tests.rs2
-rw-r--r--compiler/rustc_lint/src/traits.rs2
-rw-r--r--compiler/rustc_lint/src/types.rs11
-rw-r--r--compiler/rustc_lint/src/unused.rs12
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs53
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs20
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp119
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp16
-rw-r--r--compiler/rustc_metadata/messages.ftl3
-rw-r--r--compiler/rustc_metadata/src/creader.rs10
-rw-r--r--compiler/rustc_metadata/src/locator.rs3
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs205
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs4
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs47
-rw-r--r--compiler/rustc_middle/src/dep_graph/dep_node.rs2
-rw-r--r--compiler/rustc_middle/src/dep_graph/mod.rs16
-rw-r--r--compiler/rustc_middle/src/hir/map/mod.rs2
-rw-r--r--compiler/rustc_middle/src/infer/canonical.rs55
-rw-r--r--compiler/rustc_middle/src/infer/unify_key.rs50
-rw-r--r--compiler/rustc_middle/src/middle/codegen_fn_attrs.rs2
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs21
-rw-r--r--compiler/rustc_middle/src/mir/interpret/mod.rs17
-rw-r--r--compiler/rustc_middle/src/mir/interpret/value.rs98
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs454
-rw-r--r--compiler/rustc_middle/src/mir/mono.rs8
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs33
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs19
-rw-r--r--compiler/rustc_middle/src/query/mod.rs1
-rw-r--r--compiler/rustc_middle/src/query/on_disk_cache.rs4
-rw-r--r--compiler/rustc_middle/src/traits/solve.rs63
-rw-r--r--compiler/rustc_middle/src/traits/solve/inspect.rs72
-rw-r--r--compiler/rustc_middle/src/traits/solve/inspect/format.rs94
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs249
-rw-r--r--compiler/rustc_middle/src/ty/consts/int.rs5
-rw-r--r--compiler/rustc_middle/src/ty/consts/kind.rs43
-rw-r--r--compiler/rustc_middle/src/ty/context.rs10
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs6
-rw-r--r--compiler/rustc_middle/src/ty/flags.rs40
-rw-r--r--compiler/rustc_middle/src/ty/generic_args.rs16
-rw-r--r--compiler/rustc_middle/src/ty/generics.rs12
-rw-r--r--compiler/rustc_middle/src/ty/instance.rs3
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs6
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs37
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs37
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs20
-rw-r--r--compiler/rustc_middle/src/ty/trait_def.rs4
-rw-r--r--compiler/rustc_middle/src/ty/typeck_results.rs15
-rw-r--r--compiler/rustc_middle/src/ty/visit.rs40
-rw-r--r--compiler/rustc_middle/src/ty/vtable.rs6
-rw-r--r--compiler/rustc_mir_build/src/build/custom/parse.rs1
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs6
-rw-r--r--compiler/rustc_mir_dataflow/src/value_analysis.rs136
-rw-r--r--compiler/rustc_mir_transform/messages.ftl2
-rw-r--r--compiler/rustc_mir_transform/src/check_unsafety.rs2
-rw-r--r--compiler/rustc_mir_transform/src/const_prop.rs166
-rw-r--r--compiler/rustc_mir_transform/src/const_prop_lint.rs4
-rw-r--r--compiler/rustc_mir_transform/src/coverage/debug.rs287
-rw-r--r--compiler/rustc_mir_transform/src/coverage/query.rs123
-rw-r--r--compiler/rustc_mir_transform/src/dataflow_const_prop.rs480
-rw-r--r--compiler/rustc_mir_transform/src/errors.rs7
-rw-r--r--compiler/rustc_mir_transform/src/inline.rs7
-rw-r--r--compiler/rustc_mir_transform/src/instsimplify.rs2
-rw-r--r--compiler/rustc_mir_transform/src/large_enums.rs5
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs6
-rw-r--r--compiler/rustc_mir_transform/src/lower_intrinsics.rs12
-rw-r--r--compiler/rustc_mir_transform/src/normalize_array_len.rs2
-rw-r--r--compiler/rustc_mir_transform/src/pass_manager.rs11
-rw-r--r--compiler/rustc_mir_transform/src/remove_zsts.rs5
-rw-r--r--compiler/rustc_mir_transform/src/sroa.rs93
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs50
-rw-r--r--compiler/rustc_monomorphize/src/partitioning.rs4
-rw-r--r--compiler/rustc_parse/messages.ftl3
-rw-r--r--compiler/rustc_parse/src/errors.rs5
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs2
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs377
-rw-r--r--compiler/rustc_parse/src/parser/item.rs12
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs1
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs3
-rw-r--r--compiler/rustc_parse_format/Cargo.toml2
-rw-r--r--compiler/rustc_parse_format/src/lib.rs2
-rw-r--r--compiler/rustc_passes/messages.ftl61
-rw-r--r--compiler/rustc_passes/src/abi_test.rs210
-rw-r--r--compiler/rustc_passes/src/check_attr.rs25
-rw-r--r--compiler/rustc_passes/src/errors.rs55
-rw-r--r--compiler/rustc_passes/src/layout_test.rs94
-rw-r--r--compiler/rustc_passes/src/reachable.rs4
-rw-r--r--compiler/rustc_privacy/src/lib.rs9
-rw-r--r--compiler/rustc_query_impl/src/plumbing.rs3
-rw-r--r--compiler/rustc_query_system/src/dep_graph/edges.rs73
-rw-r--r--compiler/rustc_query_system/src/dep_graph/graph.rs37
-rw-r--r--compiler/rustc_query_system/src/dep_graph/mod.rs8
-rw-r--r--compiler/rustc_query_system/src/dep_graph/serialized.rs351
-rw-r--r--compiler/rustc_query_system/src/ich/impls_syntax.rs9
-rw-r--r--compiler/rustc_query_system/src/lib.rs1
-rw-r--r--compiler/rustc_query_system/src/query/caches.rs8
-rw-r--r--compiler/rustc_query_system/src/query/job.rs4
-rw-r--r--compiler/rustc_query_system/src/query/plumbing.rs6
-rw-r--r--compiler/rustc_resolve/messages.ftl33
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs2
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs55
-rw-r--r--compiler/rustc_resolve/src/errors.rs44
-rw-r--r--compiler/rustc_resolve/src/ident.rs15
-rw-r--r--compiler/rustc_resolve/src/imports.rs9
-rw-r--r--compiler/rustc_resolve/src/late.rs8
-rw-r--r--compiler/rustc_resolve/src/lib.rs23
-rw-r--r--compiler/rustc_resolve/src/macros.rs15
-rw-r--r--compiler/rustc_resolve/src/rustdoc.rs89
-rw-r--r--compiler/rustc_serialize/src/opaque.rs2
-rw-r--r--compiler/rustc_session/src/config.rs72
-rw-r--r--compiler/rustc_session/src/cstore.rs4
-rw-r--r--compiler/rustc_session/src/options.rs25
-rw-r--r--compiler/rustc_session/src/session.rs8
-rw-r--r--compiler/rustc_smir/src/rustc_internal/mod.rs19
-rw-r--r--compiler/rustc_smir/src/rustc_smir/alloc.rs5
-rw-r--r--compiler/rustc_smir/src/rustc_smir/mod.rs35
-rw-r--r--compiler/rustc_smir/src/stable_mir/mod.rs8
-rw-r--r--compiler/rustc_smir/src/stable_mir/ty.rs24
-rw-r--r--compiler/rustc_span/src/lib.rs333
-rw-r--r--compiler/rustc_span/src/source_map.rs14
-rw-r--r--compiler/rustc_span/src/source_map/tests.rs4
-rw-r--r--compiler/rustc_span/src/span_encoding.rs255
-rw-r--r--compiler/rustc_span/src/symbol.rs8
-rw-r--r--compiler/rustc_span/src/tests.rs4
-rw-r--r--compiler/rustc_symbol_mangling/src/legacy.rs2
-rw-r--r--compiler/rustc_symbol_mangling/src/lib.rs9
-rw-r--r--compiler/rustc_symbol_mangling/src/v0.rs2
-rw-r--r--compiler/rustc_target/src/abi/call/mod.rs105
-rw-r--r--compiler/rustc_target/src/abi/call/wasm.rs4
-rw-r--r--compiler/rustc_target/src/abi/mod.rs34
-rw-r--r--compiler/rustc_target/src/spec/abi.rs3
-rw-r--r--compiler/rustc_target/src/spec/i686_pc_windows_gnullvm.rs26
-rw-r--r--compiler/rustc_target/src/spec/mod.rs1
-rw-r--r--compiler/rustc_trait_selection/messages.ftl2
-rw-r--r--compiler/rustc_trait_selection/src/solve/alias_relate.rs6
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs70
-rw-r--r--compiler/rustc_trait_selection/src/solve/canonicalize.rs11
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt.rs28
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs11
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs38
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs4
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect.rs212
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs6
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals.rs13
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/mod.rs10
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs18
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs26
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs17
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs23
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs23
-rw-r--r--compiler/rustc_transmute/src/lib.rs11
-rw-r--r--compiler/rustc_ty_utils/src/abi.rs20
-rw-r--r--compiler/rustc_ty_utils/src/instance.rs1
-rw-r--r--compiler/rustc_ty_utils/src/layout.rs3
-rw-r--r--compiler/rustc_ty_utils/src/needs_drop.rs24
-rw-r--r--compiler/rustc_type_ir/src/fold.rs14
-rw-r--r--compiler/rustc_type_ir/src/lib.rs36
-rw-r--r--compiler/rustc_type_ir/src/visit.rs5
-rw-r--r--config.example.toml8
-rw-r--r--library/alloc/src/collections/vec_deque/mod.rs4
-rw-r--r--library/alloc/src/fmt.rs4
-rw-r--r--library/alloc/src/lib.rs1
-rw-r--r--library/alloc/src/macros.rs14
-rw-r--r--library/alloc/src/rc.rs2
-rw-r--r--library/alloc/src/sync.rs2
-rw-r--r--library/alloc/src/vec/mod.rs6
-rw-r--r--library/core/src/ascii/ascii_char.rs41
-rw-r--r--library/core/src/cell.rs5
-rw-r--r--library/core/src/char/methods.rs52
-rw-r--r--library/core/src/cmp.rs27
-rw-r--r--library/core/src/error.md2
-rw-r--r--library/core/src/ffi/c_str.rs1
-rw-r--r--library/core/src/fmt/builders.rs8
-rw-r--r--library/core/src/hash/mod.rs5
-rw-r--r--library/core/src/iter/range.rs87
-rw-r--r--library/core/src/lib.rs4
-rw-r--r--library/core/src/macros/mod.rs2
-rw-r--r--library/core/src/mem/mod.rs7
-rw-r--r--library/core/src/num/mod.rs3
-rw-r--r--library/core/src/num/saturating.rs437
-rw-r--r--library/core/src/ops/deref.rs10
-rw-r--r--library/core/src/primitive_docs.rs6
-rw-r--r--library/core/src/ptr/mod.rs3
-rw-r--r--library/core/src/ptr/mut_ptr.rs18
-rw-r--r--library/core/src/ptr/non_null.rs2
-rw-r--r--library/core/src/slice/mod.rs2
-rw-r--r--library/core/src/str/mod.rs2
-rw-r--r--library/core/src/sync/atomic.rs3
-rw-r--r--library/std/src/lib.rs27
-rw-r--r--library/std/src/num.rs2
-rw-r--r--library/std/src/os/unix/fs.rs2
-rw-r--r--library/std/src/primitive_docs.rs6
-rw-r--r--library/std/src/process.rs60
-rw-r--r--library/std/src/sync/mpsc/mod.rs15
-rw-r--r--library/std/src/sys/unix/net.rs4
-rw-r--r--library/std/src/sys/unix/process/process_common.rs30
-rw-r--r--library/std/src/sys/unix/thread.rs4
-rw-r--r--library/std/src/sys/unsupported/process.rs20
-rw-r--r--library/std/src/sys/windows/process.rs35
-rw-r--r--library/std/src/thread/mod.rs82
m---------library/stdarch0
-rw-r--r--src/bootstrap/README.md5
-rw-r--r--src/bootstrap/bin/_helper.rs24
-rw-r--r--src/bootstrap/bin/rustc.rs17
-rw-r--r--src/bootstrap/bin/rustdoc.rs19
-rw-r--r--src/bootstrap/builder.rs9
-rw-r--r--src/bootstrap/builder/tests.rs4
-rw-r--r--src/bootstrap/compile.rs6
-rw-r--r--src/bootstrap/config.rs29
-rw-r--r--src/bootstrap/config/tests.rs14
-rw-r--r--src/bootstrap/download.rs2
-rw-r--r--src/bootstrap/format.rs4
-rw-r--r--src/bootstrap/lib.rs13
-rw-r--r--src/bootstrap/llvm.rs36
-rw-r--r--src/bootstrap/sanity.rs21
-rw-r--r--src/bootstrap/setup.rs1
-rw-r--r--src/bootstrap/test.rs148
-rwxr-xr-xsrc/ci/docker/host-x86_64/dist-various-2/build-wasi-threads-toolchain.sh2
-rwxr-xr-xsrc/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh2
-rw-r--r--src/ci/docker/host-x86_64/wasm32/Dockerfile3
-rwxr-xr-xsrc/ci/run.sh3
m---------src/doc/edition-guide0
m---------src/doc/nomicon0
m---------src/doc/reference0
m---------src/doc/rust-by-example0
m---------src/doc/rustc-dev-guide0
-rw-r--r--src/doc/rustc/src/command-line-arguments.md4
-rw-r--r--src/doc/rustc/src/instrument-coverage.md4
-rw-r--r--src/doc/rustc/src/platform-support.md3
-rw-r--r--src/doc/rustc/src/platform-support/pc-windows-gnullvm.md3
-rw-r--r--src/doc/rustc/src/platform-support/wasm32-wasi-preview1-threads.md33
-rw-r--r--src/doc/rustdoc/src/how-to-read-rustdoc.md16
-rw-r--r--src/doc/rustdoc/src/unstable-features.md44
-rw-r--r--src/doc/rustdoc/src/write-documentation/re-exports.md2
-rw-r--r--src/doc/unstable-book/src/compiler-flags/path-options.md11
-rw-r--r--src/doc/unstable-book/src/language-features/coverage-attribute.md (renamed from src/doc/unstable-book/src/language-features/no-coverage.md)8
-rw-r--r--src/etc/rust_analyzer_settings.json4
-rw-r--r--src/librustdoc/clean/inline.rs13
-rw-r--r--src/librustdoc/clean/mod.rs142
-rw-r--r--src/librustdoc/clean/types.rs16
-rw-r--r--src/librustdoc/config.rs31
-rw-r--r--src/librustdoc/core.rs2
-rw-r--r--src/librustdoc/doctest.rs5
-rw-r--r--src/librustdoc/fold.rs23
-rw-r--r--src/librustdoc/formats/cache.rs1
-rw-r--r--src/librustdoc/html/highlight.rs25
-rw-r--r--src/librustdoc/html/markdown.rs403
-rw-r--r--src/librustdoc/html/markdown/tests.rs182
-rw-r--r--src/librustdoc/html/render/context.rs71
-rw-r--r--src/librustdoc/html/render/mod.rs25
-rw-r--r--src/librustdoc/html/render/print_item.rs439
-rw-r--r--src/librustdoc/html/render/sidebar.rs42
-rw-r--r--src/librustdoc/html/static/css/noscript.css213
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css474
-rw-r--r--src/librustdoc/html/static/css/settings.css63
-rw-r--r--src/librustdoc/html/static/css/themes/ayu.css181
-rw-r--r--src/librustdoc/html/static/css/themes/dark.css102
-rw-r--r--src/librustdoc/html/static/js/main.js39
-rw-r--r--src/librustdoc/html/static/js/settings.js8
-rw-r--r--src/librustdoc/html/static/js/storage.js36
-rw-r--r--src/librustdoc/html/static_files.rs4
-rw-r--r--src/librustdoc/html/templates/page.html13
-rw-r--r--src/librustdoc/json/conversions.rs2
-rw-r--r--src/librustdoc/lib.rs4
-rw-r--r--src/librustdoc/passes/check_custom_code_classes.rs77
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs43
-rw-r--r--src/librustdoc/passes/lint/bare_urls.rs7
-rw-r--r--src/librustdoc/passes/lint/check_code_block_syntax.rs16
-rw-r--r--src/librustdoc/passes/lint/html_tags.rs7
-rw-r--r--src/librustdoc/passes/lint/redundant_explicit_links.rs34
-rw-r--r--src/librustdoc/passes/lint/unescaped_backticks.rs13
-rw-r--r--src/librustdoc/passes/mod.rs96
-rw-r--r--src/librustdoc/theme.rs3
-rw-r--r--src/librustdoc/theme/tests.rs2
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/.github/driver.sh2
-rw-r--r--src/tools/clippy/.github/workflows/clippy_bors.yml33
-rw-r--r--src/tools/clippy/CHANGELOG.md5
-rw-r--r--src/tools/clippy/Cargo.toml2
-rw-r--r--src/tools/clippy/book/src/SUMMARY.md3
-rw-r--r--src/tools/clippy/book/src/development/emitting_lints.md217
-rw-r--r--src/tools/clippy/book/src/development/trait_checking.md105
-rw-r--r--src/tools/clippy/book/src/development/writing_tests.md218
-rw-r--r--src/tools/clippy/book/src/lint_configuration.md24
-rw-r--r--src/tools/clippy/clippy_lints/Cargo.toml1
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/default_union_representation.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs31
-rw-r--r--src/tools/clippy/clippy_lints/src/derivable_impls.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/derive.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_macros.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/doc.rs189
-rw-r--r--src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs242
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/mod.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/never_loop.rs268
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_range_patterns.rs129
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs106
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs58
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs391
-rw-r--r--src/tools/clippy/clippy_lints/src/non_canonical_impls.rs (renamed from src/tools/clippy/clippy_lints/src/incorrect_impls.rs)45
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/float_cmp.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/raw_strings.rs89
-rw-r--r--src/tools/clippy/clippy_lints/src/renamed_lints.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/std_instead_of_core.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs191
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unwrap.rs111
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/conf.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/consts.rs70
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/paths.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/source.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/sugg.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs7
-rw-r--r--src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/usage.rs13
-rw-r--r--src/tools/clippy/rust-toolchain2
-rw-r--r--src/tools/clippy/tests/compile-test.rs47
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/Cargo.stderr1
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.stderr1
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.stderr1
-rw-r--r--src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr1
-rw-r--r--src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.stderr2
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.stderr1
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr1
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr1
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr1
-rw-r--r--src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr1
-rw-r--r--src/tools/clippy/tests/ui-internal/check_formulation.stderr1
-rw-r--r--src/tools/clippy/tests/ui-internal/if_chain_style.stderr1
-rw-r--r--src/tools/clippy/tests/ui-internal/invalid_paths.stderr1
-rw-r--r--src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr1
-rw-r--r--src/tools/clippy/tests/ui-toml/disallowed_macros/auxiliary/macros.rs15
-rw-r--r--src/tools/clippy/tests/ui-toml/disallowed_macros/clippy.toml2
-rw-r--r--src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.rs5
-rw-r--r--src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr22
-rw-r--r--src/tools/clippy/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs469
-rw-r--r--src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs4
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr2
-rw-r--r--src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/auxiliary/proc_macro_unsafe.rs13
-rw-r--r--src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs16
-rw-r--r--src/tools/clippy/tests/ui/allow_attributes.fixed7
-rw-r--r--src/tools/clippy/tests/ui/allow_attributes.rs7
-rw-r--r--src/tools/clippy/tests/ui/arithmetic_side_effects.rs2
-rw-r--r--src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr14
-rw-r--r--src/tools/clippy/tests/ui/bool_comparison.fixed2
-rw-r--r--src/tools/clippy/tests/ui/bool_comparison.rs2
-rw-r--r--src/tools/clippy/tests/ui/cast_size.32bit.stderr (renamed from src/tools/clippy/tests/ui/cast_size_32bit.stderr)56
-rw-r--r--src/tools/clippy/tests/ui/cast_size.64bit.stderr (renamed from src/tools/clippy/tests/ui/cast_size.stderr)34
-rw-r--r--src/tools/clippy/tests/ui/cast_size.rs31
-rw-r--r--src/tools/clippy/tests/ui/cast_size_32bit.rs56
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs51
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr70
-rw-r--r--src/tools/clippy/tests/ui/clone_on_copy_impl.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-11337.rs9
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-11422.fixed25
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-11422.rs25
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-11422.stderr16
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-360.rs3
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-360.stderr20
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.fixed13
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.rs13
-rw-r--r--src/tools/clippy/tests/ui/derive.rs6
-rw-r--r--src/tools/clippy/tests/ui/derive.stderr20
-rw-r--r--src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs2
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.fixed10
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.rs10
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.stderr13
-rw-r--r--src/tools/clippy/tests/ui/doc/unbalanced_ticks.rs2
-rw-r--r--src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr21
-rw-r--r--src/tools/clippy/tests/ui/doc_errors.rs31
-rw-r--r--src/tools/clippy/tests/ui/doc_errors.stderr2
-rw-r--r--src/tools/clippy/tests/ui/empty_loop.rs3
-rw-r--r--src/tools/clippy/tests/ui/empty_loop.stderr4
-rw-r--r--src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs2
-rw-r--r--src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr17
-rw-r--r--src/tools/clippy/tests/ui/explicit_auto_deref.fixed30
-rw-r--r--src/tools/clippy/tests/ui/explicit_auto_deref.rs28
-rw-r--r--src/tools/clippy/tests/ui/explicit_auto_deref.stderr12
-rw-r--r--src/tools/clippy/tests/ui/explicit_iter_loop.fixed7
-rw-r--r--src/tools/clippy/tests/ui/explicit_iter_loop.rs1
-rw-r--r--src/tools/clippy/tests/ui/explicit_iter_loop.stderr65
-rw-r--r--src/tools/clippy/tests/ui/float_cmp.rs13
-rw-r--r--src/tools/clippy/tests/ui/float_cmp.stderr12
-rw-r--r--src/tools/clippy/tests/ui/fn_to_numeric_cast.32bit.stderr (renamed from src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.stderr)62
-rw-r--r--src/tools/clippy/tests/ui/fn_to_numeric_cast.64bit.stderr (renamed from src/tools/clippy/tests/ui/fn_to_numeric_cast.stderr)44
-rw-r--r--src/tools/clippy/tests/ui/fn_to_numeric_cast.rs27
-rw-r--r--src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.rs80
-rw-r--r--src/tools/clippy/tests/ui/if_then_some_else_none.rs12
-rw-r--r--src/tools/clippy/tests/ui/ignored_unit_patterns.fixed16
-rw-r--r--src/tools/clippy/tests/ui/ignored_unit_patterns.rs16
-rw-r--r--src/tools/clippy/tests/ui/ignored_unit_patterns.stderr10
-rw-r--r--src/tools/clippy/tests/ui/implied_bounds_in_impls.fixed57
-rw-r--r--src/tools/clippy/tests/ui/implied_bounds_in_impls.rs57
-rw-r--r--src/tools/clippy/tests/ui/implied_bounds_in_impls.stderr74
-rw-r--r--src/tools/clippy/tests/ui/iter_out_of_bounds.rs71
-rw-r--r--src/tools/clippy/tests/ui/iter_out_of_bounds.stderr119
-rw-r--r--src/tools/clippy/tests/ui/iter_overeager_cloned.fixed6
-rw-r--r--src/tools/clippy/tests/ui/iter_overeager_cloned.rs2
-rw-r--r--src/tools/clippy/tests/ui/iter_overeager_cloned.stderr18
-rw-r--r--src/tools/clippy/tests/ui/iter_skip_next.fixed1
-rw-r--r--src/tools/clippy/tests/ui/iter_skip_next.rs1
-rw-r--r--src/tools/clippy/tests/ui/iter_skip_next.stderr14
-rw-r--r--src/tools/clippy/tests/ui/iter_skip_next_unfixable.rs2
-rw-r--r--src/tools/clippy/tests/ui/iter_skip_zero.fixed2
-rw-r--r--src/tools/clippy/tests/ui/iter_skip_zero.rs2
-rw-r--r--src/tools/clippy/tests/ui/large_enum_variant.32bit.stderr280
-rw-r--r--src/tools/clippy/tests/ui/large_enum_variant.64bit.stderr (renamed from src/tools/clippy/tests/ui/large_enum_variant.stderr)44
-rw-r--r--src/tools/clippy/tests/ui/large_enum_variant.rs1
-rw-r--r--src/tools/clippy/tests/ui/manual_range_patterns.fixed17
-rw-r--r--src/tools/clippy/tests/ui/manual_range_patterns.rs11
-rw-r--r--src/tools/clippy/tests/ui/manual_range_patterns.stderr66
-rw-r--r--src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed121
-rw-r--r--src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs121
-rw-r--r--src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr253
-rw-r--r--src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs49
-rw-r--r--src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr164
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.fixed13
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.rs13
-rw-r--r--src/tools/clippy/tests/ui/needless_doc_main.rs9
-rw-r--r--src/tools/clippy/tests/ui/needless_doc_main.stderr42
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string.fixed13
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string.rs13
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string.stderr48
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed19
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string_hashes.rs19
-rw-r--r--src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr144
-rw-r--r--src/tools/clippy/tests/ui/never_loop.rs55
-rw-r--r--src/tools/clippy/tests/ui/never_loop.stderr27
-rw-r--r--src/tools/clippy/tests/ui/non_canonical_clone_impl.fixed (renamed from src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.fixed)0
-rw-r--r--src/tools/clippy/tests/ui/non_canonical_clone_impl.rs (renamed from src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.rs)0
-rw-r--r--src/tools/clippy/tests/ui/non_canonical_clone_impl.stderr (renamed from src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.stderr)19
-rw-r--r--src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed (renamed from src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type.fixed)0
-rw-r--r--src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs (renamed from src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type.rs)0
-rw-r--r--src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr (renamed from src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type.stderr)11
-rw-r--r--src/tools/clippy/tests/ui/non_canonical_partial_ord_impl_fully_qual.rs (renamed from src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs)3
-rw-r--r--src/tools/clippy/tests/ui/non_canonical_partial_ord_impl_fully_qual.stderr (renamed from src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.stderr)14
-rw-r--r--src/tools/clippy/tests/ui/rename.fixed8
-rw-r--r--src/tools/clippy/tests/ui/rename.rs8
-rw-r--r--src/tools/clippy/tests/ui/rename.stderr132
-rw-r--r--src/tools/clippy/tests/ui/result_large_err.rs2
-rw-r--r--src/tools/clippy/tests/ui/result_large_err.stderr24
-rw-r--r--src/tools/clippy/tests/ui/similar_names.rs1
-rw-r--r--src/tools/clippy/tests/ui/similar_names.stderr28
-rw-r--r--src/tools/clippy/tests/ui/single_call_fn.rs1
-rw-r--r--src/tools/clippy/tests/ui/single_call_fn.stderr16
-rw-r--r--src/tools/clippy/tests/ui/slow_vector_initialization.rs16
-rw-r--r--src/tools/clippy/tests/ui/slow_vector_initialization.stderr12
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core.fixed62
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core.rs1
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core.stderr71
-rw-r--r--src/tools/clippy/tests/ui/transmute_32bit.stderr30
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed2
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs2
-rw-r--r--src/tools/clippy/tests/ui/vec.fixed1
-rw-r--r--src/tools/clippy/tests/ui/vec.rs1
-rw-r--r--src/tools/clippy/tests/ui/vec.stderr10
-rw-r--r--src/tools/clippy/tests/ui/write_literal_2.stderr4
-rw-r--r--src/tools/compiletest/src/read2.rs63
-rw-r--r--src/tools/compiletest/src/read2/tests.rs50
-rw-r--r--src/tools/compiletest/src/runtest.rs40
-rw-r--r--src/tools/miri/.github/workflows/ci.yml2
-rwxr-xr-xsrc/tools/miri/miri2
-rw-r--r--src/tools/miri/miri-script/src/commands.rs10
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/src/bin/miri.rs4
-rw-r--r--src/tools/miri/src/concurrency/data_race.rs5
-rw-r--r--src/tools/miri/src/concurrency/thread.rs2
-rw-r--r--src/tools/miri/src/diagnostics.rs24
-rw-r--r--src/tools/miri/src/eval.rs2
-rw-r--r--src/tools/miri/src/machine.rs2
-rw-r--r--src/tools/miri/src/shims/backtrace.rs4
-rw-r--r--src/tools/miri/src/shims/foreign_items.rs5
-rw-r--r--src/tools/miri/src/shims/os_str.rs5
-rw-r--r--src/tools/miri/src/shims/unix/foreign_items.rs2
-rw-r--r--src/tools/miri/src/shims/unix/macos/foreign_items.rs10
-rw-r--r--src/tools/miri/src/shims/windows/foreign_items.rs2
-rw-r--r--src/tools/miri/src/shims/x86/mod.rs44
-rw-r--r--src/tools/miri/src/shims/x86/sse.rs69
-rw-r--r--src/tools/miri/src/shims/x86/sse2.rs982
-rw-r--r--src/tools/miri/tests/fail/function_pointers/abi_mismatch_array_vs_struct.stderr2
-rw-r--r--src/tools/miri/tests/fail/function_pointers/abi_mismatch_int_vs_float.stderr2
-rw-r--r--src/tools/miri/tests/fail/function_pointers/abi_mismatch_raw_pointer.stderr2
-rw-r--r--src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr2
-rw-r--r--src/tools/miri/tests/fail/function_pointers/abi_mismatch_simple.stderr2
-rw-r--r--src/tools/miri/tests/fail/function_pointers/abi_mismatch_vector.stderr2
-rw-r--r--src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_arg.rs (renamed from src/tools/miri/tests/fail/validity/cast_fn_ptr1.rs)0
-rw-r--r--src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_arg.stderr (renamed from src/tools/miri/tests/fail/validity/cast_fn_ptr1.stderr)4
-rw-r--r--src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.rs28
-rw-r--r--src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.stderr15
-rw-r--r--src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.rs34
-rw-r--r--src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.stderr20
-rw-r--r--src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_ret.rs (renamed from src/tools/miri/tests/fail/validity/cast_fn_ptr2.rs)0
-rw-r--r--src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_ret.stderr (renamed from src/tools/miri/tests/fail/validity/cast_fn_ptr2.stderr)4
-rw-r--r--src/tools/miri/tests/pass/function_calls/abi_compat.rs65
-rw-r--r--src/tools/miri/tests/pass/intrinsics-x86-sse.rs23
-rw-r--r--src/tools/miri/tests/pass/intrinsics-x86-sse2.rs828
-rw-r--r--src/tools/miri/triagebot.toml3
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs10
-rw-r--r--src/tools/rustdoc-themes/main.rs44
-rw-r--r--src/tools/rustfmt/src/expr.rs2
-rw-r--r--src/tools/tidy/src/lib.rs1
-rw-r--r--src/tools/tidy/src/main.rs1
-rw-r--r--src/tools/tidy/src/rustdoc_css_themes.rs99
-rw-r--r--tests/codegen/debuginfo-inline-callsite-location.rs28
-rw-r--r--tests/codegen/issues/issue-115385-llvm-jump-threading.rs46
-rw-r--r--tests/codegen/repr/transparent.rs1
-rw-r--r--tests/codegen/sanitizer/address-sanitizer-globals-tracking.rs43
-rw-r--r--tests/coverage-map/status-quo/closure_macro.rs2
-rw-r--r--tests/coverage-map/status-quo/closure_macro_async.rs14
-rw-r--r--tests/coverage-map/status-quo/no_cov_crate.rs14
-rw-r--r--tests/debuginfo/pretty-std-collections.rs1
-rw-r--r--tests/mir-opt/building/async_await.b-{closure#0}.generator_resume.0.mir18
-rw-r--r--tests/mir-opt/const_debuginfo.main.ConstDebugInfo.diff12
-rw-r--r--tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-abort.diff6
-rw-r--r--tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-unwind.diff6
-rw-r--r--tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-abort.diff6
-rw-r--r--tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-unwind.diff6
-rw-r--r--tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs3
-rw-r--r--tests/mir-opt/const_prop/checked_add.main.ConstProp.panic-abort.diff4
-rw-r--r--tests/mir-opt/const_prop/checked_add.main.ConstProp.panic-unwind.diff4
-rw-r--r--tests/mir-opt/const_prop/indirect.main.ConstProp.panic-abort.diff4
-rw-r--r--tests/mir-opt/const_prop/indirect.main.ConstProp.panic-unwind.diff4
-rw-r--r--tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-abort.diff4
-rw-r--r--tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-unwind.diff4
-rw-r--r--tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-abort.diff8
-rw-r--r--tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-unwind.diff8
-rw-r--r--tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-abort.diff12
-rw-r--r--tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-unwind.diff12
-rw-r--r--tests/mir-opt/const_prop/large_array_index.rs1
-rw-r--r--tests/mir-opt/const_prop/mutable_variable_aggregate.main.ConstProp.diff8
-rw-r--r--tests/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.panic-abort.diff4
-rw-r--r--tests/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.panic-unwind.diff4
-rw-r--r--tests/mir-opt/const_prop/repeat.rs1
-rw-r--r--tests/mir-opt/const_prop/return_place.add.ConstProp.panic-abort.diff4
-rw-r--r--tests/mir-opt/const_prop/return_place.add.ConstProp.panic-unwind.diff4
-rw-r--r--tests/mir-opt/const_prop/return_place.add.PreCodegen.before.panic-abort.mir4
-rw-r--r--tests/mir-opt/const_prop/return_place.add.PreCodegen.before.panic-unwind.mir4
-rw-r--r--tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-abort.diff12
-rw-r--r--tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-unwind.diff12
-rw-r--r--tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-abort.diff39
-rw-r--r--tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-unwind.diff39
-rw-r--r--tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-abort.diff39
-rw-r--r--tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-unwind.diff39
-rw-r--r--tests/mir-opt/dataflow-const-prop/array_index.rs8
-rw-r--r--tests/mir-opt/dataflow-const-prop/boolean_identities.rs10
-rw-r--r--tests/mir-opt/dataflow-const-prop/boolean_identities.test.DataflowConstProp.diff33
-rw-r--r--tests/mir-opt/dataflow-const-prop/enum.constant.DataflowConstProp.32bit.diff63
-rw-r--r--tests/mir-opt/dataflow-const-prop/enum.constant.DataflowConstProp.64bit.diff63
-rw-r--r--tests/mir-opt/dataflow-const-prop/enum.multiple.DataflowConstProp.32bit.diff (renamed from tests/mir-opt/dataflow-const-prop/enum.multiple.DataflowConstProp.diff)0
-rw-r--r--tests/mir-opt/dataflow-const-prop/enum.multiple.DataflowConstProp.64bit.diff82
-rw-r--r--tests/mir-opt/dataflow-const-prop/enum.mutate_discriminant.DataflowConstProp.32bit.diff (renamed from tests/mir-opt/dataflow-const-prop/enum.mutate_discriminant.DataflowConstProp.diff)0
-rw-r--r--tests/mir-opt/dataflow-const-prop/enum.mutate_discriminant.DataflowConstProp.64bit.diff26
-rw-r--r--tests/mir-opt/dataflow-const-prop/enum.rs22
-rw-r--r--tests/mir-opt/dataflow-const-prop/enum.simple.DataflowConstProp.32bit.diff (renamed from tests/mir-opt/dataflow-const-prop/enum.simple.DataflowConstProp.diff)0
-rw-r--r--tests/mir-opt/dataflow-const-prop/enum.simple.DataflowConstProp.64bit.diff63
-rw-r--r--tests/mir-opt/dataflow-const-prop/enum.statics.DataflowConstProp.32bit.diff126
-rw-r--r--tests/mir-opt/dataflow-const-prop/enum.statics.DataflowConstProp.64bit.diff126
-rw-r--r--tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-abort.diff39
-rw-r--r--tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-unwind.diff39
-rw-r--r--tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-abort.diff39
-rw-r--r--tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-unwind.diff39
-rw-r--r--tests/mir-opt/dataflow-const-prop/large_array_index.rs9
-rw-r--r--tests/mir-opt/dataflow-const-prop/mult_by_zero.rs10
-rw-r--r--tests/mir-opt/dataflow-const-prop/mult_by_zero.test.DataflowConstProp.diff18
-rw-r--r--tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-abort.diff76
-rw-r--r--tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-unwind.diff76
-rw-r--r--tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-abort.diff72
-rw-r--r--tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-unwind.diff72
-rw-r--r--tests/mir-opt/dataflow-const-prop/offset_of.rs49
-rw-r--r--tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-abort.diff43
-rw-r--r--tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-unwind.diff43
-rw-r--r--tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-abort.diff43
-rw-r--r--tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-unwind.diff43
-rw-r--r--tests/mir-opt/dataflow-const-prop/repeat.rs8
-rw-r--r--tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff77
-rw-r--r--tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff77
-rw-r--r--tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff77
-rw-r--r--tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff77
-rw-r--r--tests/mir-opt/dataflow-const-prop/slice_len.rs12
-rw-r--r--tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.32bit.diff129
-rw-r--r--tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.64bit.diff129
-rw-r--r--tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff51
-rw-r--r--tests/mir-opt/dataflow-const-prop/struct.rs11
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.from_char.DataflowConstProp.32bit.diff15
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.from_char.DataflowConstProp.64bit.diff15
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.invalid_bool.DataflowConstProp.32bit.diff15
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.invalid_bool.DataflowConstProp.64bit.diff15
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.invalid_char.DataflowConstProp.32bit.diff15
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.invalid_char.DataflowConstProp.64bit.diff15
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.less_as_i8.DataflowConstProp.32bit.diff18
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.less_as_i8.DataflowConstProp.64bit.diff18
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.rs63
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.undef_union_as_integer.DataflowConstProp.32bit.diff22
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.undef_union_as_integer.DataflowConstProp.64bit.diff22
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.32bit.diff20
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.64bit.diff20
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.unreachable_direct.DataflowConstProp.32bit.diff22
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.unreachable_direct.DataflowConstProp.64bit.diff22
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.unreachable_mut.DataflowConstProp.32bit.diff24
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.unreachable_mut.DataflowConstProp.64bit.diff24
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.unreachable_ref.DataflowConstProp.32bit.diff20
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.unreachable_ref.DataflowConstProp.64bit.diff20
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.valid_char.DataflowConstProp.32bit.diff15
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.valid_char.DataflowConstProp.64bit.diff15
-rw-r--r--tests/mir-opt/enum_opt.cand.EnumSizeOpt.32bit.diff4
-rw-r--r--tests/mir-opt/enum_opt.cand.EnumSizeOpt.64bit.diff4
-rw-r--r--tests/mir-opt/enum_opt.unin.EnumSizeOpt.32bit.diff4
-rw-r--r--tests/mir-opt/enum_opt.unin.EnumSizeOpt.64bit.diff4
-rw-r--r--tests/mir-opt/funky_arms.rs2
-rw-r--r--tests/mir-opt/generator_drop_cleanup.main-{closure#0}.generator_drop.0.panic-abort.mir6
-rw-r--r--tests/mir-opt/generator_drop_cleanup.main-{closure#0}.generator_drop.0.panic-unwind.mir6
-rw-r--r--tests/mir-opt/generator_tiny.main-{closure#0}.generator_resume.0.mir6
-rw-r--r--tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff8
-rw-r--r--tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff8
-rw-r--r--tests/mir-opt/issue_99325.main.built.after.32bit.mir (renamed from tests/mir-opt/issue_99325.main.built.after.mir)4
-rw-r--r--tests/mir-opt/issue_99325.main.built.after.64bit.mir276
-rw-r--r--tests/mir-opt/issue_99325.rs2
-rw-r--r--tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir2
-rw-r--r--tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir2
-rw-r--r--tests/mir-opt/pre-codegen/optimizes_into_variable.main.ConstProp.32bit.panic-abort.diff4
-rw-r--r--tests/mir-opt/pre-codegen/optimizes_into_variable.main.ConstProp.32bit.panic-unwind.diff4
-rw-r--r--tests/mir-opt/pre-codegen/optimizes_into_variable.main.ConstProp.64bit.panic-abort.diff4
-rw-r--r--tests/mir-opt/pre-codegen/optimizes_into_variable.main.ConstProp.64bit.panic-unwind.diff4
-rw-r--r--tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir12
-rw-r--r--tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir12
-rw-r--r--tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff3
-rw-r--r--tests/mir-opt/sroa/structs.constant.ScalarReplacementOfAggregates.diff3
-rw-r--r--tests/mir-opt/sroa/structs.copies.ScalarReplacementOfAggregates.diff10
-rw-r--r--tests/mir-opt/sroa/structs.ref_copies.ScalarReplacementOfAggregates.diff5
-rw-r--r--tests/pretty/tests-are-sorted.pp2
-rw-r--r--tests/run-coverage/closure_macro.coverage2
-rw-r--r--tests/run-coverage/closure_macro.rs2
-rw-r--r--tests/run-coverage/closure_macro_async.coverage14
-rw-r--r--tests/run-coverage/closure_macro_async.rs14
-rw-r--r--tests/run-coverage/no_cov_crate.coverage14
-rw-r--r--tests/run-coverage/no_cov_crate.rs14
-rw-r--r--tests/run-make-fulldeps/issue-19371/foo.rs1
-rw-r--r--tests/run-make-fulldeps/obtain-borrowck/driver.rs3
-rw-r--r--tests/run-make/compressed-debuginfo/Makefile15
-rw-r--r--tests/run-make/compressed-debuginfo/foo.rs3
-rw-r--r--tests/run-make/emit-path-unhashed/Makefile8
-rw-r--r--tests/run-make/issue-88756-default-output/output-default.stdout2
-rw-r--r--tests/run-make/ls-metadata/Makefile4
-rw-r--r--tests/run-make/lto-linkage-used-attr/Makefile9
-rw-r--r--tests/run-make/lto-linkage-used-attr/lib.rs50
-rw-r--r--tests/run-make/lto-linkage-used-attr/main.rs10
-rw-r--r--tests/run-make/output-filename-overwrites-input/Makefile2
-rw-r--r--tests/run-make/pdb-buildinfo-cl-cmd/Makefile16
-rw-r--r--tests/run-make/pdb-buildinfo-cl-cmd/main.rs2
-rw-r--r--tests/run-make/pdb-buildinfo-cl-cmd/stringlist.txt1
-rw-r--r--tests/run-make/print-cfg/Makefile8
-rw-r--r--tests/run-make/rustdoc-themes/Makefile3
-rw-r--r--tests/rustdoc-gui/help-page.goml18
-rw-r--r--tests/rustdoc-gui/search-no-result.goml12
-rw-r--r--tests/rustdoc-gui/search-result-color.goml2
-rw-r--r--tests/rustdoc-gui/sidebar-source-code.goml2
-rw-r--r--tests/rustdoc-gui/src/theme_css/Cargo.lock7
-rw-r--r--tests/rustdoc-gui/src/theme_css/Cargo.toml7
-rw-r--r--tests/rustdoc-gui/src/theme_css/custom-theme.css (renamed from src/librustdoc/html/static/css/themes/light.css)2
-rw-r--r--tests/rustdoc-gui/src/theme_css/lib.rs2
-rw-r--r--tests/rustdoc-gui/theme-change.goml33
-rw-r--r--tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs85
-rw-r--r--tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr113
-rw-r--r--tests/rustdoc-ui/custom_code_classes_in_docs-warning3.rs17
-rw-r--r--tests/rustdoc-ui/custom_code_classes_in_docs-warning3.stderr33
-rw-r--r--tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs5
-rw-r--r--tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr15
-rw-r--r--tests/rustdoc-ui/issues/issue-91713.stdout2
-rw-r--r--tests/rustdoc/auxiliary/cross_crate_generic_typedef.rs5
-rw-r--r--tests/rustdoc/const-generics/const-generic-defaults.rs2
-rw-r--r--tests/rustdoc/const-generics/const-generics-docs.rs4
-rw-r--r--tests/rustdoc/custom_code_classes.rs28
-rw-r--r--tests/rustdoc/issue-32077-type-alias-impls.rs66
-rw-r--r--tests/rustdoc/issue-88600.rs4
-rw-r--r--tests/rustdoc/private-fields-tuple-struct.rs15
-rw-r--r--tests/rustdoc/typedef-inner-variants-lazy_type_alias.rs34
-rw-r--r--tests/rustdoc/typedef-inner-variants.rs119
-rw-r--r--tests/rustdoc/where.SWhere_Simd_item-decl.html2
-rw-r--r--tests/rustdoc/where.alpha_trait_decl.html2
-rw-r--r--tests/rustdoc/where.rs2
-rw-r--r--tests/ui-fulldeps/plugin/lint-tool-cmdline-allow.stderr10
-rw-r--r--tests/ui-fulldeps/pprust-expr-roundtrip.rs20
-rw-r--r--tests/ui-fulldeps/stable-mir/crate-info.rs6
-rw-r--r--tests/ui/abi/compatibility.rs194
-rw-r--r--tests/ui/abi/debug.rs32
-rw-r--r--tests/ui/abi/debug.stderr713
-rw-r--r--tests/ui/associated-consts/associated-const-array-len.stderr6
-rw-r--r--tests/ui/associated-consts/issue-105330.stderr10
-rw-r--r--tests/ui/associated-type-bounds/return-type-notation/basic.without.stderr4
-rw-r--r--tests/ui/associated-types/associated-types-ICE-when-projecting-out-of-err.stderr6
-rw-r--r--tests/ui/associated-types/associated-types-no-suitable-supertrait.stderr6
-rw-r--r--tests/ui/associated-types/defaults-suitability.stderr5
-rw-r--r--tests/ui/associated-types/issue-23595-2.stderr2
-rw-r--r--tests/ui/associated-types/issue-59324.stderr6
-rw-r--r--tests/ui/associated-types/issue-64855.stderr6
-rw-r--r--tests/ui/associated-types/issue-85103-layout-debug.rs9
-rw-r--r--tests/ui/associated-types/issue-85103-layout-debug.stderr16
-rw-r--r--tests/ui/associated-types/issue-85103.rs9
-rw-r--r--tests/ui/associated-types/issue-85103.stderr8
-rw-r--r--tests/ui/associated-types/point-at-type-on-obligation-failure-2.stderr15
-rw-r--r--tests/ui/async-await/async-is-unwindsafe.stderr2
-rw-r--r--tests/ui/async-await/in-trait/async-example-desugared-extra.rs7
-rw-r--r--tests/ui/async-await/in-trait/async-example-desugared.rs2
-rw-r--r--tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.current.stderr27
-rw-r--r--tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.next.stderr11
-rw-r--r--tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.rs24
-rw-r--r--tests/ui/borrowck/issue-114374-invalid-help-fmt-args.rs16
-rw-r--r--tests/ui/borrowck/issue-114374-invalid-help-fmt-args.stderr33
-rw-r--r--tests/ui/borrowck/issue-115259-suggest-iter-mut.fixed20
-rw-r--r--tests/ui/borrowck/issue-115259-suggest-iter-mut.rs20
-rw-r--r--tests/ui/borrowck/issue-115259-suggest-iter-mut.stderr16
-rw-r--r--tests/ui/borrowck/issue-62387-suggest-iter-mut-2.fixed36
-rw-r--r--tests/ui/borrowck/issue-62387-suggest-iter-mut-2.rs36
-rw-r--r--tests/ui/borrowck/issue-62387-suggest-iter-mut-2.stderr16
-rw-r--r--tests/ui/borrowck/issue-62387-suggest-iter-mut.fixed30
-rw-r--r--tests/ui/borrowck/issue-62387-suggest-iter-mut.rs30
-rw-r--r--tests/ui/borrowck/issue-62387-suggest-iter-mut.stderr29
-rw-r--r--tests/ui/c-variadic/variadic-ffi-2.rs5
-rw-r--r--tests/ui/c-variadic/variadic-ffi-2.stderr2
-rw-r--r--tests/ui/cast/cast-as-bool.rs40
-rw-r--r--tests/ui/cast/cast-as-bool.stderr56
-rw-r--r--tests/ui/cast/cast-rfc0401-2.rs2
-rw-r--r--tests/ui/cast/cast-rfc0401-2.stderr2
-rw-r--r--tests/ui/closures/2229_closure_analysis/repr_packed.rs10
-rw-r--r--tests/ui/closures/2229_closure_analysis/repr_packed.stderr19
-rw-r--r--tests/ui/codegen/subtyping-enforces-type-equality.rs48
-rw-r--r--tests/ui/codegen/subtyping-enforces-type-equality.stderr1
-rw-r--r--tests/ui/coherence/coherence-unsafe-trait-object-impl.stderr5
-rw-r--r--tests/ui/const-generics/dont-evaluate-array-len-on-err-1.stderr6
-rw-r--r--tests/ui/const-generics/early/const-param-from-outer-fn.rs2
-rw-r--r--tests/ui/const-generics/early/const-param-from-outer-fn.stderr8
-rw-r--r--tests/ui/const-generics/generic_const_exprs/issue-85848.stderr6
-rw-r--r--tests/ui/const-generics/issues/issue-86530.stderr5
-rw-r--r--tests/ui/consts/drop-maybe_uninit.rs17
-rw-r--r--tests/ui/consts/issue-105536-const-val-roundtrip-ptr-eq.rs31
-rw-r--r--tests/ui/cross/cross-fn-cache-hole.stderr5
-rw-r--r--tests/ui/deriving/deriving-all-codegen.stdout28
-rw-r--r--tests/ui/drop-bounds/drop-bounds-impl-drop.rs10
-rw-r--r--tests/ui/dst/dst-bad-coerce1.stderr10
-rw-r--r--tests/ui/dyn-star/error.stderr6
-rw-r--r--tests/ui/error-codes/E0054.stderr2
-rw-r--r--tests/ui/error-codes/E0220.stderr2
-rw-r--r--tests/ui/error-codes/E0277.stderr5
-rw-r--r--tests/ui/error-codes/E0401.stderr22
-rw-r--r--tests/ui/error-codes/E0602.rs2
-rw-r--r--tests/ui/error-codes/E0602.stderr11
-rw-r--r--tests/ui/error-festival.stderr2
-rw-r--r--tests/ui/expr/if/bad-if-let-suggestion.rs3
-rw-r--r--tests/ui/expr/if/bad-if-let-suggestion.stderr23
-rw-r--r--tests/ui/feature-gates/feature-gate-coverage-attribute.rs14
-rw-r--r--tests/ui/feature-gates/feature-gate-coverage-attribute.stderr21
-rw-r--r--tests/ui/feature-gates/feature-gate-no_coverage.rs13
-rw-r--r--tests/ui/feature-gates/feature-gate-no_coverage.stderr12
-rw-r--r--tests/ui/feature-gates/print-with-path.cfg.stderr2
-rw-r--r--tests/ui/feature-gates/print-with-path.rs7
-rw-r--r--tests/ui/feature-gates/print-with-path.target-cpus.stderr2
-rw-r--r--tests/ui/feature-gates/print-with-path.target-features.stderr2
-rw-r--r--tests/ui/fn/keyword-order.stderr2
-rw-r--r--tests/ui/generic-associated-types/issue-101020.stderr5
-rw-r--r--tests/ui/generics/issue-94432-garbage-ice.rs2
-rw-r--r--tests/ui/generics/issue-98432.rs2
-rw-r--r--tests/ui/generics/issue-98432.stderr8
-rw-r--r--tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-89118.stderr15
-rw-r--r--tests/ui/impl-trait/async_scope_creep.rs15
-rw-r--r--tests/ui/impl-trait/async_scope_creep.tait.stderr9
-rw-r--r--tests/ui/impl-trait/in-trait/auxiliary/rpitit.rs3
-rw-r--r--tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.rs3
-rw-r--r--tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.stderr18
-rw-r--r--tests/ui/impl-trait/in-trait/deep-match-works.rs7
-rw-r--r--tests/ui/impl-trait/in-trait/foreign.rs14
-rw-r--r--tests/ui/impl-trait/in-trait/issue-102571.rs8
-rw-r--r--tests/ui/impl-trait/in-trait/issue-102571.stderr2
-rw-r--r--tests/ui/impl-trait/in-trait/nested-rpitit.rs10
-rw-r--r--tests/ui/impl-trait/in-trait/object-safety.rs2
-rw-r--r--tests/ui/impl-trait/in-trait/refine.rs48
-rw-r--r--tests/ui/impl-trait/in-trait/refine.stderr67
-rw-r--r--tests/ui/impl-trait/in-trait/reveal.rs5
-rw-r--r--tests/ui/impl-trait/in-trait/rpitit-shadowed-by-missing-adt.rs3
-rw-r--r--tests/ui/impl-trait/in-trait/signature-mismatch.failure.stderr2
-rw-r--r--tests/ui/impl-trait/in-trait/signature-mismatch.rs12
-rw-r--r--tests/ui/impl-trait/in-trait/specialization-substs-remap.rs5
-rw-r--r--tests/ui/impl-trait/in-trait/success.rs9
-rw-r--r--tests/ui/impl-trait/lifetime-ambiguity-regression.rs13
-rw-r--r--tests/ui/imports/import-after-macro-expand-10.rs17
-rw-r--r--tests/ui/imports/import-after-macro-expand-11.rs21
-rw-r--r--tests/ui/imports/import-after-macro-expand-12.rs34
-rw-r--r--tests/ui/imports/import-after-macro-expand-13.rs22
-rw-r--r--tests/ui/imports/import-after-macro-expand-14.rs22
-rw-r--r--tests/ui/imports/import-after-macro-expand-2.rs4
-rw-r--r--tests/ui/imports/import-after-macro-expand-4.rs11
-rw-r--r--tests/ui/imports/import-after-macro-expand-4.stderr53
-rw-r--r--tests/ui/imports/import-after-macro-expand-6.rs4
-rw-r--r--tests/ui/imports/import-after-macro-expand-9.rs17
-rw-r--r--tests/ui/inner-static-type-parameter.rs2
-rw-r--r--tests/ui/inner-static-type-parameter.stderr6
-rw-r--r--tests/ui/issues/issue-18611.stderr6
-rw-r--r--tests/ui/issues/issue-25076.stderr5
-rw-r--r--tests/ui/issues/issue-3214.rs2
-rw-r--r--tests/ui/issues/issue-3214.stderr8
-rw-r--r--tests/ui/issues/issue-35570.stderr6
-rw-r--r--tests/ui/issues/issue-5997-enum.rs2
-rw-r--r--tests/ui/issues/issue-5997-enum.stderr8
-rw-r--r--tests/ui/issues/issue-5997-struct.rs2
-rw-r--r--tests/ui/issues/issue-5997-struct.stderr8
-rw-r--r--tests/ui/issues/issue-60218.stderr5
-rw-r--r--tests/ui/issues/issue-66353.stderr12
-rw-r--r--tests/ui/layout/debug.rs14
-rw-r--r--tests/ui/layout/debug.stderr46
-rw-r--r--tests/ui/layout/homogeneous-aggr-transparent.rs44
-rw-r--r--tests/ui/layout/homogeneous-aggr-transparent.stderr32
-rw-r--r--tests/ui/layout/zero-sized-array-enum-niche.stderr6
-rw-r--r--tests/ui/lifetimes/anonymize-unnamed-bound-vars-in-binders.rs27
-rw-r--r--tests/ui/lifetimes/issue-95023.stderr2
-rw-r--r--tests/ui/lifetimes/lifetime-elision-return-type-trait.stderr6
-rw-r--r--tests/ui/limits/issue-55878.stderr4
-rw-r--r--tests/ui/limits/issue-56762.rs6
-rw-r--r--tests/ui/limits/issue-56762.stderr10
-rw-r--r--tests/ui/linkage-attr/common-linkage-non-zero-init.rs14
-rw-r--r--tests/ui/linkage-attr/common-linkage-non-zero-init.stderr3
-rw-r--r--tests/ui/lint/cli-unknown-force-warn.rs6
-rw-r--r--tests/ui/lint/cli-unknown-force-warn.stderr11
-rw-r--r--tests/ui/lint/expr-field.rs15
-rw-r--r--tests/ui/lint/lint-ctypes-94223.rs7
-rw-r--r--tests/ui/lint/lint-ctypes-94223.stderr27
-rw-r--r--tests/ui/lint/lint-ctypes-option-nonnull-unsized.rs8
-rw-r--r--tests/ui/lint/lint-ctypes-option-nonnull-unsized.stderr16
-rw-r--r--tests/ui/lint/lint-removed-cmdline-deny.rs13
-rw-r--r--tests/ui/lint/lint-removed-cmdline-deny.stderr28
-rw-r--r--tests/ui/lint/lint-removed-cmdline.rs1
-rw-r--r--tests/ui/lint/lint-removed-cmdline.stderr5
-rw-r--r--tests/ui/lint/lint-renamed-cmdline-deny.rs10
-rw-r--r--tests/ui/lint/lint-renamed-cmdline-deny.stderr31
-rw-r--r--tests/ui/lint/lint-renamed-cmdline.rs1
-rw-r--r--tests/ui/lint/lint-renamed-cmdline.stderr8
-rw-r--r--tests/ui/lint/lint-unexported-no-mangle.stderr1
-rw-r--r--tests/ui/lint/lint-unknown-lint-cmdline-allow.rs4
-rw-r--r--tests/ui/lint/lint-unknown-lint-cmdline-deny.rs9
-rw-r--r--tests/ui/lint/lint-unknown-lint-cmdline-deny.stderr31
-rw-r--r--tests/ui/lint/lint-unknown-lint-cmdline.rs2
-rw-r--r--tests/ui/lint/lint-unknown-lint-cmdline.stderr20
-rw-r--r--tests/ui/lint/no-coverage.rs40
-rw-r--r--tests/ui/lint/no-coverage.stderr72
-rw-r--r--tests/ui/lint/ptr_null_checks.rs14
-rw-r--r--tests/ui/lint/ptr_null_checks.stderr38
-rw-r--r--tests/ui/lint/reference_casting.rs28
-rw-r--r--tests/ui/lint/reference_casting.stderr68
-rw-r--r--tests/ui/mir/issue-92893.rs4
-rw-r--r--tests/ui/mir/issue-92893.stderr18
-rw-r--r--tests/ui/mismatched_types/cast-rfc0401.stderr4
-rw-r--r--tests/ui/namespace/namespace-mix.stderr220
-rw-r--r--tests/ui/nested-ty-params.rs2
-rw-r--r--tests/ui/nested-ty-params.stderr16
-rw-r--r--tests/ui/never_type/feature-gate-never_type_fallback.stderr5
-rw-r--r--tests/ui/never_type/impl_trait_fallback3.stderr6
-rw-r--r--tests/ui/never_type/impl_trait_fallback4.stderr6
-rw-r--r--tests/ui/on-unimplemented/on-trait.stderr10
-rw-r--r--tests/ui/on-unimplemented/parent-label.stderr20
-rw-r--r--tests/ui/parser/default-unmatched.stderr2
-rw-r--r--tests/ui/parser/impl-parsing.stderr2
-rw-r--r--tests/ui/parser/issue-101477-enum.stderr2
-rw-r--r--tests/ui/parser/issues/issue-113110-non-item-at-module-root.rs1
-rw-r--r--tests/ui/parser/issues/issue-113110-non-item-at-module-root.stderr10
-rw-r--r--tests/ui/parser/issues/issue-115780-pat-lt-bracket-in-macro-call.rs21
-rw-r--r--tests/ui/parser/issues/issue-17904-2.stderr2
-rw-r--r--tests/ui/parser/issues/issue-43196.stderr2
-rw-r--r--tests/ui/parser/issues/issue-62913.stderr2
-rw-r--r--tests/ui/parser/issues/issue-68890.stderr2
-rw-r--r--tests/ui/parser/shebang/shebang-doc-comment.stderr2
-rw-r--r--tests/ui/parser/virtual-structs.stderr2
-rw-r--r--tests/ui/pattern/issue-114896.rs7
-rw-r--r--tests/ui/pattern/issue-114896.stderr11
-rw-r--r--tests/ui/privacy/private-bounds-locally-allowed.rs7
-rw-r--r--tests/ui/privacy/unnameable_types.rs8
-rw-r--r--tests/ui/proc-macro/bad-projection.stderr6
-rw-r--r--tests/ui/pub/pub-restricted-error-fn.stderr2
-rw-r--r--tests/ui/range/range-1.stderr2
-rw-r--r--tests/ui/resolve/bad-type-env-capture.stderr8
-rw-r--r--tests/ui/resolve/generic-params-from-outer-item-in-const-item.default.stderr28
-rw-r--r--tests/ui/resolve/generic-params-from-outer-item-in-const-item.generic_const_items.stderr34
-rw-r--r--tests/ui/resolve/generic-params-from-outer-item-in-const-item.rs39
-rw-r--r--tests/ui/resolve/issue-12796.rs2
-rw-r--r--tests/ui/resolve/issue-12796.stderr4
-rw-r--r--tests/ui/resolve/issue-3021-c.rs4
-rw-r--r--tests/ui/resolve/issue-3021-c.stderr16
-rw-r--r--tests/ui/resolve/issue-65025-extern-static-parent-generics.rs2
-rw-r--r--tests/ui/resolve/issue-65025-extern-static-parent-generics.stderr6
-rw-r--r--tests/ui/resolve/issue-65035-static-with-parent-generics.rs10
-rw-r--r--tests/ui/resolve/issue-65035-static-with-parent-generics.stderr30
-rw-r--r--tests/ui/resolve/resolve-type-param-in-item-in-trait.rs8
-rw-r--r--tests/ui/resolve/resolve-type-param-in-item-in-trait.stderr32
-rw-r--r--tests/ui/resolve/suggest-import-without-clobbering-attrs.fixed16
-rw-r--r--tests/ui/resolve/suggest-import-without-clobbering-attrs.rs15
-rw-r--r--tests/ui/resolve/suggest-import-without-clobbering-attrs.stderr14
-rw-r--r--tests/ui/resolve/use-self-in-inner-fn.rs6
-rw-r--r--tests/ui/resolve/use-self-in-inner-fn.stderr6
-rw-r--r--tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.rs36
-rw-r--r--tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.stderr285
-rw-r--r--tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.stderr1
-rw-r--r--tests/ui/rfcs/rfc-2294-if-let-guard/parens.rs2
-rw-r--r--tests/ui/rfcs/rfc-2294-if-let-guard/parens.stderr34
-rw-r--r--tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs2
-rw-r--r--tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr2
-rw-r--r--tests/ui/rfcs/rfc-2497-if-let-chains/avoid-invalid-mir.rs14
-rw-r--r--tests/ui/rfcs/rfc-2497-if-let-chains/avoid-invalid-mir.stderr10
-rw-r--r--tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions-without-feature-gate.rs340
-rw-r--r--tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions-without-feature-gate.stderr1021
-rw-r--r--tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs261
-rw-r--r--tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.stderr1462
-rw-r--r--tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs1
-rw-r--r--tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr30
-rw-r--r--tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.rs6
-rw-r--r--tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.stderr30
-rw-r--r--tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs2
-rw-r--r--tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr36
-rw-r--r--tests/ui/rfcs/rfc-2632-const-trait-impl/effects/fallback.rs16
-rw-r--r--tests/ui/sanitize/cfg.rs16
-rw-r--r--tests/ui/span/issue-29595.stderr6
-rw-r--r--tests/ui/span/send-is-not-static-std-sync.rs2
-rw-r--r--tests/ui/span/send-is-not-static-std-sync.stderr8
-rw-r--r--tests/ui/specialization/issue-38091.stderr5
-rw-r--r--tests/ui/suggestions/issue-89333.stderr5
-rw-r--r--tests/ui/thir-print/thir-flat-const-variant.stdout96
-rw-r--r--tests/ui/thir-print/thir-tree-match.stdout20
-rw-r--r--tests/ui/trait-bounds/issue-82038.rs9
-rw-r--r--tests/ui/trait-bounds/issue-82038.stderr15
-rw-r--r--tests/ui/traits/bound/on-structs-and-enums-in-fns.stderr10
-rw-r--r--tests/ui/traits/bound/on-structs-and-enums-in-impls.stderr5
-rw-r--r--tests/ui/traits/bound/on-structs-and-enums-locals.stderr10
-rw-r--r--tests/ui/traits/bound/on-structs-and-enums-static.stderr5
-rw-r--r--tests/ui/traits/bound/on-structs-and-enums.stderr20
-rw-r--r--tests/ui/traits/deny-builtin-object-impl.current.stderr5
-rw-r--r--tests/ui/traits/deny-builtin-object-impl.next.stderr5
-rw-r--r--tests/ui/traits/dont-autoderef-ty-with-escaping-var.stderr5
-rw-r--r--tests/ui/traits/impl-bounds-checking.stderr5
-rw-r--r--tests/ui/traits/new-solver/canonicalize-effect-var.rs22
-rw-r--r--tests/ui/traits/new-solver/projection-discr-kind.stderr5
-rw-r--r--tests/ui/traits/non_lifetime_binders/fail.stderr5
-rw-r--r--tests/ui/traits/object-does-not-impl-trait.stderr5
-rw-r--r--tests/ui/traits/object/enforce-supertrait-projection.rs2
-rw-r--r--tests/ui/traits/object/enforce-supertrait-projection.stderr2
-rw-r--r--tests/ui/traits/suggest-dereferences/dont-suggest-unsize-deref.rs15
-rw-r--r--tests/ui/traits/suggest-dereferences/dont-suggest-unsize-deref.stderr22
-rw-r--r--tests/ui/traits/suggest-dereferences/issue-39029.fixed (renamed from tests/ui/traits/suggest-deferences/issue-39029.fixed)0
-rw-r--r--tests/ui/traits/suggest-dereferences/issue-39029.rs (renamed from tests/ui/traits/suggest-deferences/issue-39029.rs)0
-rw-r--r--tests/ui/traits/suggest-dereferences/issue-39029.stderr (renamed from tests/ui/traits/suggest-deferences/issue-39029.stderr)0
-rw-r--r--tests/ui/traits/suggest-dereferences/issue-62530.fixed (renamed from tests/ui/traits/suggest-deferences/issue-62530.fixed)0
-rw-r--r--tests/ui/traits/suggest-dereferences/issue-62530.rs (renamed from tests/ui/traits/suggest-deferences/issue-62530.rs)0
-rw-r--r--tests/ui/traits/suggest-dereferences/issue-62530.stderr (renamed from tests/ui/traits/suggest-deferences/issue-62530.stderr)0
-rw-r--r--tests/ui/traits/suggest-dereferences/multiple-0.fixed (renamed from tests/ui/traits/suggest-deferences/multiple-0.fixed)0
-rw-r--r--tests/ui/traits/suggest-dereferences/multiple-0.rs (renamed from tests/ui/traits/suggest-deferences/multiple-0.rs)0
-rw-r--r--tests/ui/traits/suggest-dereferences/multiple-0.stderr (renamed from tests/ui/traits/suggest-deferences/multiple-0.stderr)0
-rw-r--r--tests/ui/traits/suggest-dereferences/multiple-1.rs (renamed from tests/ui/traits/suggest-deferences/multiple-1.rs)0
-rw-r--r--tests/ui/traits/suggest-dereferences/multiple-1.stderr (renamed from tests/ui/traits/suggest-deferences/multiple-1.stderr)0
-rw-r--r--tests/ui/traits/suggest-dereferences/root-obligation.fixed (renamed from tests/ui/traits/suggest-deferences/root-obligation.fixed)0
-rw-r--r--tests/ui/traits/suggest-dereferences/root-obligation.rs (renamed from tests/ui/traits/suggest-deferences/root-obligation.rs)0
-rw-r--r--tests/ui/traits/suggest-dereferences/root-obligation.stderr (renamed from tests/ui/traits/suggest-deferences/root-obligation.stderr)0
-rw-r--r--tests/ui/traits/suggest-dereferences/suggest-dereferencing-receiver-argument.fixed (renamed from tests/ui/traits/suggest-deferences/suggest-dereferencing-receiver-argument.fixed)0
-rw-r--r--tests/ui/traits/suggest-dereferences/suggest-dereferencing-receiver-argument.rs (renamed from tests/ui/traits/suggest-deferences/suggest-dereferencing-receiver-argument.rs)0
-rw-r--r--tests/ui/traits/suggest-dereferences/suggest-dereferencing-receiver-argument.stderr (renamed from tests/ui/traits/suggest-deferences/suggest-dereferencing-receiver-argument.stderr)0
-rw-r--r--tests/ui/traits/vtable-res-trait-param.stderr5
-rw-r--r--tests/ui/trivial-bounds/trivial-bounds-leak.stderr11
-rw-r--r--tests/ui/type-alias-impl-trait/nested-tait-hrtb.rs2
-rw-r--r--tests/ui/type-alias-impl-trait/nested-tait-hrtb.stderr14
-rw-r--r--tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.rs25
-rw-r--r--tests/ui/type/type-arg-out-of-scope.rs2
-rw-r--r--tests/ui/type/type-arg-out-of-scope.stderr16
-rw-r--r--tests/ui/union/projection-as-union-type-error-2.stderr5
-rw-r--r--tests/ui/union/projection-as-union-type-error.stderr6
-rw-r--r--tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.rs17
-rw-r--r--tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.stderr16
-rw-r--r--tests/ui/unsafe/issue-115348-false-positive-warning-of-unnecessary-unsafe.rs16
-rw-r--r--tests/ui/unsafe/issue-115348-false-positive-warning-of-unnecessary-unsafe.stderr21
-rw-r--r--tests/ui/unsized/issue-115203.rs11
-rw-r--r--tests/ui/unsized/issue-115203.stderr19
-rw-r--r--tests/ui/unsized/issue-115809.rs13
-rw-r--r--tests/ui/unsized/issue-115809.stderr19
-rw-r--r--tests/ui/unsized/issue-75707.stderr5
-rw-r--r--tests/ui/wf/hir-wf-canonicalized.stderr12
-rw-r--r--tests/ui/wf/issue-95665.stderr5
-rw-r--r--tests/ui/wf/wf-complex-assoc-type.stderr5
-rw-r--r--tests/ui/wf/wf-foreign-fn-decl-ret.stderr11
-rw-r--r--tests/ui/wf/wf-packed-on-proj-of-type-as-unimpl-trait.stderr6
-rw-r--r--tests/ui/where-clauses/higher-ranked-fn-type.quiet.stderr5
-rw-r--r--tests/ui/where-clauses/higher-ranked-fn-type.verbose.stderr5
-rw-r--r--tests/ui/where-clauses/where-clause-method-substituion.stderr5
-rw-r--r--triagebot.toml2
1150 files changed, 27445 insertions, 10347 deletions
diff --git a/.mailmap b/.mailmap
index c072f6282f1..eb82cf4de8d 100644
--- a/.mailmap
+++ b/.mailmap
@@ -205,6 +205,10 @@ Gareth Daniel Smith <garethdanielsmith@gmail.com> gareth <gareth@gareth-N56VM.(n
 Gareth Daniel Smith <garethdanielsmith@gmail.com> Gareth Smith <garethdanielsmith@gmail.com>
 Gauri Kholkar <f2013002@goa.bits-pilani.ac.in>
 Georges Dubus <georges.dubus@gmail.com> <georges.dubus@compiletoi.net>
+Ghost <ghost> <jonasschievink@gmail.com>
+Ghost <ghost> <jonas.schievink@ferrous-systems.com>
+Ghost <ghost> <jonas@schievink.net>
+Ghost <ghost> <Jonas.Schievink@sony.com>
 Giles Cope <gilescope@gmail.com>
 Glen De Cauwsemaecker <decauwsemaecker.glen@gmail.com>
 Graham Fawcett <graham.fawcett@gmail.com> Graham Fawcett <fawcett@uwindsor.ca>
diff --git a/Cargo.lock b/Cargo.lock
index 35a9fdccf9c..386b57e6a44 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -531,7 +531,7 @@ dependencies = [
  "tester",
  "tokio",
  "toml 0.7.5",
- "ui_test 0.18.1",
+ "ui_test 0.20.0",
  "walkdir",
 ]
 
@@ -558,7 +558,6 @@ dependencies = [
  "declare_clippy_lint",
  "if_chain",
  "itertools",
- "pulldown-cmark",
  "quine-mc_cluskey",
  "regex",
  "regex-syntax 0.7.2",
@@ -4175,7 +4174,7 @@ dependencies = [
 name = "rustc_parse_format"
 version = "0.0.0"
 dependencies = [
- "rustc_data_structures",
+ "rustc_index",
  "rustc_lexer",
 ]
 
@@ -5597,9 +5596,9 @@ dependencies = [
 
 [[package]]
 name = "ui_test"
-version = "0.18.1"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "640159421816683e558867ffc0e60ed3a3ed97ec6ccb22c03adb41bf87c5cfa4"
+checksum = "bfd8fb9b15c8332cf51bfc2dc4830063b2446a9c9d732421b56f2478024a3971"
 dependencies = [
  "annotate-snippets",
  "anyhow",
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index 571aaf631bd..b30ff058a30 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -1300,12 +1300,18 @@ impl Abi {
         matches!(*self, Abi::Uninhabited)
     }
 
-    /// Returns `true` is this is a scalar type
+    /// Returns `true` if this is a scalar type
     #[inline]
     pub fn is_scalar(&self) -> bool {
         matches!(*self, Abi::Scalar(_))
     }
 
+    /// Returns `true` if this is a bool
+    #[inline]
+    pub fn is_bool(&self) -> bool {
+        matches!(*self, Abi::Scalar(s) if s.is_bool())
+    }
+
     /// Returns the fixed alignment of this ABI, if any is mandated.
     pub fn inherent_align<C: HasDataLayout>(&self, cx: &C) -> Option<AbiAndPrefAlign> {
         Some(match *self {
@@ -1348,6 +1354,23 @@ impl Abi {
             Abi::Uninhabited | Abi::Aggregate { .. } => Abi::Aggregate { sized: true },
         }
     }
+
+    pub fn eq_up_to_validity(&self, other: &Self) -> bool {
+        match (self, other) {
+            // Scalar, Vector, ScalarPair have `Scalar` in them where we ignore validity ranges.
+            // We do *not* ignore the sign since it matters for some ABIs (e.g. s390x).
+            (Abi::Scalar(l), Abi::Scalar(r)) => l.primitive() == r.primitive(),
+            (
+                Abi::Vector { element: element_l, count: count_l },
+                Abi::Vector { element: element_r, count: count_r },
+            ) => element_l.primitive() == element_r.primitive() && count_l == count_r,
+            (Abi::ScalarPair(l1, l2), Abi::ScalarPair(r1, r2)) => {
+                l1.primitive() == r1.primitive() && l2.primitive() == r2.primitive()
+            }
+            // Everything else must be strictly identical.
+            _ => self == other,
+        }
+    }
 }
 
 #[derive(PartialEq, Eq, Hash, Clone, Debug)]
@@ -1686,6 +1709,22 @@ impl LayoutS {
             Abi::Aggregate { sized } => sized && self.size.bytes() == 0,
         }
     }
+
+    /// Checks if these two `Layout` are equal enough to be considered "the same for all function
+    /// call ABIs". Note however that real ABIs depend on more details that are not reflected in the
+    /// `Layout`; the `PassMode` need to be compared as well.
+    pub fn eq_abi(&self, other: &Self) -> bool {
+        // The one thing that we are not capturing here is that for unsized types, the metadata must
+        // also have the same ABI, and moreover that the same metadata leads to the same size. The
+        // 2nd point is quite hard to check though.
+        self.size == other.size
+            && self.is_sized() == other.is_sized()
+            && self.abi.eq_up_to_validity(&other.abi)
+            && self.abi.is_bool() == other.abi.is_bool()
+            && self.align.abi == other.align.abi
+            && self.max_repr_align == other.max_repr_align
+            && self.unadjusted_abi_align == other.unadjusted_abi_align
+    }
 }
 
 #[derive(Copy, Clone, Debug)]
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 395540764ea..e8cbd7b69fb 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -33,7 +33,7 @@ use rustc_macros::HashStable_Generic;
 use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 use rustc_span::source_map::{respan, Spanned};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
 use std::fmt;
 use std::mem;
 use thin_vec::{thin_vec, ThinVec};
@@ -1426,7 +1426,7 @@ pub enum ExprKind {
     /// of `if` / `while` expressions. (e.g., `if let 0 = x { .. }`).
     ///
     /// `Span` represents the whole `let pat = expr` statement.
-    Let(P<Pat>, P<Expr>, Span),
+    Let(P<Pat>, P<Expr>, Span, Option<ErrorGuaranteed>),
     /// An `if` block, with an optional `else` block.
     ///
     /// `if expr { block } else { expr }`
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index e3504a5638e..ba2887146cf 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -1366,7 +1366,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
             vis.visit_ty(ty);
         }
         ExprKind::AddrOf(_, _, ohs) => vis.visit_expr(ohs),
-        ExprKind::Let(pat, scrutinee, _) => {
+        ExprKind::Let(pat, scrutinee, _, _) => {
             vis.visit_pat(pat);
             vis.visit_expr(scrutinee);
         }
diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs
index 607b77705cf..f9f1c0cf956 100644
--- a/compiler/rustc_ast/src/util/classify.rs
+++ b/compiler/rustc_ast/src/util/classify.rs
@@ -36,7 +36,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
             | AssignOp(_, _, e)
             | Binary(_, _, e)
             | Break(_, Some(e))
-            | Let(_, e, _)
+            | Let(_, e, _, _)
             | Range(_, Some(e), _)
             | Ret(Some(e))
             | Unary(_, e)
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index ddbbf5a10bc..e66c4a9ee26 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -827,7 +827,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
             visitor.visit_expr(subexpression);
             visitor.visit_ty(typ)
         }
-        ExprKind::Let(pat, expr, _) => {
+        ExprKind::Let(pat, expr, _, _) => {
             visitor.visit_pat(pat);
             visitor.visit_expr(expr);
         }
diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl
index f63a9bfcd70..8115c4b55b0 100644
--- a/compiler/rustc_ast_lowering/messages.ftl
+++ b/compiler/rustc_ast_lowering/messages.ftl
@@ -29,10 +29,6 @@ ast_lowering_bad_return_type_notation_inputs =
     argument types not allowed with return type notation
     .suggestion = remove the input types
 
-ast_lowering_bad_return_type_notation_needs_dots =
-    return type notation arguments must be elided with `..`
-    .suggestion = add `..`
-
 ast_lowering_bad_return_type_notation_output =
     return type not allowed with return type notation
     .suggestion = remove the return type
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 7408b4fb0af..57c54f8540c 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -152,13 +152,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     let ohs = self.lower_expr(ohs);
                     hir::ExprKind::AddrOf(*k, *m, ohs)
                 }
-                ExprKind::Let(pat, scrutinee, span) => {
+                ExprKind::Let(pat, scrutinee, span, is_recovered) => {
                     hir::ExprKind::Let(self.arena.alloc(hir::Let {
                         hir_id: self.next_id(),
                         span: self.lower_span(*span),
                         pat: self.lower_pat(pat),
                         ty: None,
                         init: self.lower_expr(scrutinee),
+                        is_recovered: *is_recovered,
                     }))
                 }
                 ExprKind::If(cond, then, else_opt) => {
@@ -558,13 +559,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
     fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> {
         let pat = self.lower_pat(&arm.pat);
         let guard = arm.guard.as_ref().map(|cond| {
-            if let ExprKind::Let(pat, scrutinee, span) = &cond.kind {
+            if let ExprKind::Let(pat, scrutinee, span, is_recovered) = &cond.kind {
                 hir::Guard::IfLet(self.arena.alloc(hir::Let {
                     hir_id: self.next_id(),
                     span: self.lower_span(*span),
                     pat: self.lower_pat(pat),
                     ty: None,
                     init: self.lower_expr(scrutinee),
+                    is_recovered: *is_recovered,
                 }))
             } else {
                 hir::Guard::If(self.lower_expr(cond))
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 41db61d391a..32046b0febf 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -770,7 +770,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     /// Intercept all spans entering HIR.
     /// Mark a span as relative to the current owning item.
     fn lower_span(&self, span: Span) -> Span {
-        if self.tcx.sess.opts.incremental_relative_spans() {
+        if self.tcx.sess.opts.incremental.is_some() {
             span.with_parent(Some(self.current_hir_id_owner.def_id))
         } else {
             // Do not make spans relative when not using incremental compilation.
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index ee5007027de..43020a93078 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -117,16 +117,6 @@ ast_passes_forbidden_default =
     `default` is only allowed on items in trait impls
     .label = `default` because of this
 
-ast_passes_forbidden_let =
-    `let` expressions are not supported here
-    .note = only supported directly in conditions of `if` and `while` expressions
-    .not_supported_or = `||` operators are not supported in let chain expressions
-    .not_supported_parentheses = `let`s wrapped in parentheses are not supported in a context with let chains
-
-ast_passes_forbidden_let_stable =
-    expected expression, found statement (`let`)
-    .note = variable declaration using `let` is a statement
-
 ast_passes_forbidden_lifetime_bound =
     lifetime bounds cannot be used in this context
 
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index ad367d05f04..7bc685a5450 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -14,14 +14,12 @@ use rustc_ast::{walk_list, StaticItem};
 use rustc_ast_pretty::pprust::{self, State};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_feature::Features;
-use rustc_macros::Subdiagnostic;
 use rustc_parse::validate_attr;
 use rustc_session::lint::builtin::{
     DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, PATTERNS_IN_FNS_WITHOUT_BODY,
 };
 use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer};
 use rustc_session::Session;
-use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::Span;
 use rustc_target::spec::abi;
@@ -69,9 +67,6 @@ struct AstValidator<'a> {
     /// or `Foo::Bar<impl Trait>`
     is_impl_trait_banned: bool,
 
-    /// See [ForbiddenLetReason]
-    forbidden_let_reason: Option<ForbiddenLetReason>,
-
     lint_buffer: &'a mut LintBuffer,
 }
 
@@ -118,26 +113,6 @@ impl<'a> AstValidator<'a> {
         self.with_tilde_const(Some(ctx), f)
     }
 
-    fn with_let_management(
-        &mut self,
-        forbidden_let_reason: Option<ForbiddenLetReason>,
-        f: impl FnOnce(&mut Self, Option<ForbiddenLetReason>),
-    ) {
-        let old = mem::replace(&mut self.forbidden_let_reason, forbidden_let_reason);
-        f(self, old);
-        self.forbidden_let_reason = old;
-    }
-
-    /// Emits an error banning the `let` expression provided in the given location.
-    fn ban_let_expr(&self, expr: &'a Expr, forbidden_let_reason: ForbiddenLetReason) {
-        let sess = &self.session;
-        if sess.opts.unstable_features.is_nightly_build() {
-            sess.emit_err(errors::ForbiddenLet { span: expr.span, reason: forbidden_let_reason });
-        } else {
-            sess.emit_err(errors::ForbiddenLetStable { span: expr.span });
-        }
-    }
-
     fn check_type_alias_where_clause_location(
         &mut self,
         ty_alias: &TyAlias,
@@ -779,67 +754,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
         validate_attr::check_attr(&self.session.parse_sess, attr);
     }
 
-    fn visit_expr(&mut self, expr: &'a Expr) {
-        self.with_let_management(Some(ForbiddenLetReason::GenericForbidden), |this, forbidden_let_reason| {
-            match &expr.kind {
-                ExprKind::Binary(Spanned { node: BinOpKind::Or, span }, lhs, rhs) => {
-                    let local_reason = Some(ForbiddenLetReason::NotSupportedOr(*span));
-                    this.with_let_management(local_reason, |this, _| this.visit_expr(lhs));
-                    this.with_let_management(local_reason, |this, _| this.visit_expr(rhs));
-                }
-                ExprKind::If(cond, then, opt_else) => {
-                    this.visit_block(then);
-                    walk_list!(this, visit_expr, opt_else);
-                    this.with_let_management(None, |this, _| this.visit_expr(cond));
-                    return;
-                }
-                ExprKind::Let(..) if let Some(elem) = forbidden_let_reason => {
-                    this.ban_let_expr(expr, elem);
-                },
-                ExprKind::Match(scrutinee, arms) => {
-                    this.visit_expr(scrutinee);
-                    for arm in arms {
-                        this.visit_expr(&arm.body);
-                        this.visit_pat(&arm.pat);
-                        walk_list!(this, visit_attribute, &arm.attrs);
-                        if let Some(guard) = &arm.guard {
-                            this.with_let_management(None, |this, _| {
-                                this.visit_expr(guard)
-                            });
-                        }
-                    }
-                }
-                ExprKind::Paren(local_expr) => {
-                    fn has_let_expr(expr: &Expr) -> bool {
-                        match &expr.kind {
-                            ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs),
-                            ExprKind::Let(..) => true,
-                            _ => false,
-                        }
-                    }
-                    let local_reason = if has_let_expr(local_expr) {
-                        Some(ForbiddenLetReason::NotSupportedParentheses(local_expr.span))
-                    }
-                    else {
-                        forbidden_let_reason
-                    };
-                    this.with_let_management(local_reason, |this, _| this.visit_expr(local_expr));
-                }
-                ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, ..) => {
-                    this.with_let_management(forbidden_let_reason, |this, _| visit::walk_expr(this, expr));
-                    return;
-                }
-                ExprKind::While(cond, then, opt_label) => {
-                    walk_list!(this, visit_label, opt_label);
-                    this.visit_block(then);
-                    this.with_let_management(None, |this, _| this.visit_expr(cond));
-                    return;
-                }
-                _ => visit::walk_expr(this, expr),
-            }
-        });
-    }
-
     fn visit_ty(&mut self, ty: &'a Ty) {
         self.visit_ty_common(ty);
         self.deny_anon_struct_or_union(ty);
@@ -1601,26 +1515,9 @@ pub fn check_crate(
         outer_impl_trait: None,
         disallow_tilde_const: None,
         is_impl_trait_banned: false,
-        forbidden_let_reason: Some(ForbiddenLetReason::GenericForbidden),
         lint_buffer: lints,
     };
     visit::walk_crate(&mut validator, krate);
 
     validator.has_proc_macro_decls
 }
-
-/// Used to forbid `let` expressions in certain syntactic locations.
-#[derive(Clone, Copy, Subdiagnostic)]
-pub(crate) enum ForbiddenLetReason {
-    /// `let` is not valid and the source environment is not important
-    GenericForbidden,
-    /// A let chain with the `||` operator
-    #[note(ast_passes_not_supported_or)]
-    NotSupportedOr(#[primary_span] Span),
-    /// A let chain with invalid parentheses
-    ///
-    /// For example, `let 1 = 1 && (expr && expr)` is allowed
-    /// but `(let 1 = 1 && (let 1 = 1 && (let 1 = 1))) && let a = 1` is not
-    #[note(ast_passes_not_supported_parentheses)]
-    NotSupportedParentheses(#[primary_span] Span),
-}
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 74ab48c06ff..e74d94e4347 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -5,28 +5,9 @@ use rustc_errors::AddToDiagnostic;
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::{symbol::Ident, Span, Symbol};
 
-use crate::ast_validation::ForbiddenLetReason;
 use crate::fluent_generated as fluent;
 
 #[derive(Diagnostic)]
-#[diag(ast_passes_forbidden_let)]
-#[note]
-pub struct ForbiddenLet {
-    #[primary_span]
-    pub span: Span,
-    #[subdiagnostic]
-    pub(crate) reason: ForbiddenLetReason,
-}
-
-#[derive(Diagnostic)]
-#[diag(ast_passes_forbidden_let_stable)]
-#[note]
-pub struct ForbiddenLetStable {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(ast_passes_keyword_lifetime)]
 pub struct KeywordLifetime {
     #[primary_span]
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index 39741a03930..1142d492160 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -352,7 +352,7 @@ impl<'a> State<'a> {
                 self.end();
                 self.word(")");
             }
-            ast::ExprKind::Let(pat, scrutinee, _) => {
+            ast::ExprKind::Let(pat, scrutinee, _, _) => {
                 self.print_let(pat, scrutinee);
             }
             ast::ExprKind::If(test, blk, elseopt) => self.print_if(test, blk, elseopt.as_deref()),
diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl
index 67fdb671742..2c7b97afa80 100644
--- a/compiler/rustc_borrowck/messages.ftl
+++ b/compiler/rustc_borrowck/messages.ftl
@@ -74,9 +74,6 @@ borrowck_higher_ranked_subtype_error =
 borrowck_lifetime_constraints_error =
     lifetime may not live long enough
 
-borrowck_move_borrowed =
-    cannot move out of `{$desc}` because it is borrowed
-
 borrowck_move_out_place_here =
     {$place} is moved here
 
@@ -166,6 +163,8 @@ borrowck_returned_lifetime_wrong =
 borrowck_returned_ref_escaped =
     returns a reference to a captured variable which escapes the closure body
 
+borrowck_simd_shuffle_last_const = last argument of `simd_shuffle` is required to be a `const` item
+
 borrowck_suggest_create_freash_reborrow =
     consider reborrowing the `Pin` instead of moving it
 
@@ -248,12 +247,6 @@ borrowck_var_move_by_use_in_closure =
 borrowck_var_move_by_use_in_generator =
     move occurs due to use in generator
 
-borrowck_var_move_by_use_place_in_closure =
-    move occurs due to use of {$place} in closure
-
-borrowck_var_move_by_use_place_in_generator =
-    move occurs due to use of {$place} in generator
-
 borrowck_var_mutable_borrow_by_use_place_in_closure =
     mutable borrow occurs due to use of {$place} in closure
 
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index fe4a45b3898..48d09f2c2b2 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -2130,21 +2130,27 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 /// misleading users in cases like `tests/ui/nll/borrowed-temporary-error.rs`.
                 /// We could expand the analysis to suggest hoising all of the relevant parts of
                 /// the users' code to make the code compile, but that could be too much.
-                struct NestedStatementVisitor {
+                /// We found the `prop_expr` by the way to check whether the expression is a `FormatArguments`,
+                /// which is a special case since it's generated by the compiler.
+                struct NestedStatementVisitor<'tcx> {
                     span: Span,
                     current: usize,
                     found: usize,
+                    prop_expr: Option<&'tcx hir::Expr<'tcx>>,
                 }
 
-                impl<'tcx> Visitor<'tcx> for NestedStatementVisitor {
-                    fn visit_block(&mut self, block: &hir::Block<'tcx>) {
+                impl<'tcx> Visitor<'tcx> for NestedStatementVisitor<'tcx> {
+                    fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) {
                         self.current += 1;
                         walk_block(self, block);
                         self.current -= 1;
                     }
-                    fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) {
+                    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
                         if self.span == expr.span.source_callsite() {
                             self.found = self.current;
+                            if self.prop_expr.is_none() {
+                                self.prop_expr = Some(expr);
+                            }
                         }
                         walk_expr(self, expr);
                     }
@@ -2162,22 +2168,40 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             span: proper_span,
                             current: 0,
                             found: 0,
+                            prop_expr: None,
                         };
                         visitor.visit_stmt(stmt);
+
+                        let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
+                        let expr_ty: Option<Ty<'_>> = visitor.prop_expr.map(|expr| typeck_results.expr_ty(expr).peel_refs());
+
+                        let is_format_arguments_item =
+                            if let Some(expr_ty) = expr_ty
+                               && let ty::Adt(adt, _) = expr_ty.kind() {
+                                    self.infcx.tcx.lang_items().get(LangItem::FormatArguments) == Some(adt.did())
+                               } else {
+                                   false
+                               };
+
                         if visitor.found == 0
                             && stmt.span.contains(proper_span)
                             && let Some(p) = sm.span_to_margin(stmt.span)
                             && let Ok(s) = sm.span_to_snippet(proper_span)
                         {
-                            let addition = format!("let binding = {};\n{}", s, " ".repeat(p));
-                            err.multipart_suggestion_verbose(
-                                msg,
-                                vec![
-                                    (stmt.span.shrink_to_lo(), addition),
-                                    (proper_span, "binding".to_string()),
-                                ],
-                                Applicability::MaybeIncorrect,
-                            );
+                            if !is_format_arguments_item {
+                                let addition = format!("let binding = {};\n{}", s, " ".repeat(p));
+                                err.multipart_suggestion_verbose(
+                                    msg,
+                                    vec![
+                                        (stmt.span.shrink_to_lo(), addition),
+                                        (proper_span, "binding".to_string()),
+                                    ],
+                                    Applicability::MaybeIncorrect,
+                                );
+                            } else {
+                                err.note("the result of `format_args!` can only be assigned directly if no placeholders in it's arguments are used");
+                                err.note("to learn more, visit <https://doc.rust-lang.org/std/macro.format_args.html>");
+                            }
                             suggested = true;
                             break;
                         }
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index 31e863b8a4e..a0edeec59d0 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -1,9 +1,10 @@
+use hir::ExprKind;
 use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
 use rustc_hir as hir;
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::Node;
 use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt};
 use rustc_middle::{
     hir::place::PlaceBase,
     mir::{self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location},
@@ -370,12 +371,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     err.span_label(span, format!("cannot {act}"));
                 }
                 if suggest {
-                    err.span_suggestion_verbose(
-                        local_decl.source_info.span.shrink_to_lo(),
-                        "consider changing this to be mutable",
-                        "mut ",
-                        Applicability::MachineApplicable,
-                    );
+                    self.construct_mut_suggestion_for_local_binding_patterns(&mut err, local);
                     let tcx = self.infcx.tcx;
                     if let ty::Closure(id, _) = *the_place_err.ty(self.body, tcx).ty.kind() {
                         self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
@@ -491,6 +487,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                             ),
                         );
 
+                        self.suggest_using_iter_mut(&mut err);
                         self.suggest_make_local_mut(&mut err, local, name);
                     }
                     _ => {
@@ -710,6 +707,83 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         )
     }
 
+    fn construct_mut_suggestion_for_local_binding_patterns(
+        &self,
+        err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+        local: Local,
+    ) {
+        let local_decl = &self.body.local_decls[local];
+        debug!("local_decl: {:?}", local_decl);
+        let pat_span = match *local_decl.local_info() {
+            LocalInfo::User(BindingForm::Var(mir::VarBindingForm {
+                binding_mode: ty::BindingMode::BindByValue(Mutability::Not),
+                opt_ty_info: _,
+                opt_match_place: _,
+                pat_span,
+            })) => pat_span,
+            _ => local_decl.source_info.span,
+        };
+
+        struct BindingFinder {
+            span: Span,
+            hir_id: Option<hir::HirId>,
+        }
+
+        impl<'tcx> Visitor<'tcx> for BindingFinder {
+            fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
+                if let hir::StmtKind::Local(local) = s.kind {
+                    if local.pat.span == self.span {
+                        self.hir_id = Some(local.hir_id);
+                    }
+                }
+                hir::intravisit::walk_stmt(self, s);
+            }
+        }
+
+        let hir_map = self.infcx.tcx.hir();
+        let def_id = self.body.source.def_id();
+        let hir_id = if let Some(local_def_id) = def_id.as_local()
+            && let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id)
+        {
+            let body = hir_map.body(body_id);
+            let mut v = BindingFinder {
+                span: pat_span,
+                hir_id: None,
+            };
+            v.visit_body(body);
+            v.hir_id
+        } else {
+            None
+        };
+
+        // With ref-binding patterns, the mutability suggestion has to apply to
+        // the binding, not the reference (which would be a type error):
+        //
+        // `let &b = a;` -> `let &(mut b) = a;`
+        if let Some(hir_id) = hir_id
+            && let Some(hir::Node::Local(hir::Local {
+                pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. },
+                ..
+            })) = hir_map.find(hir_id)
+            && let Ok(name) = self.infcx.tcx.sess.source_map().span_to_snippet(local_decl.source_info.span)
+        {
+            err.span_suggestion(
+                pat_span,
+                "consider changing this to be mutable",
+                format!("&(mut {name})"),
+                Applicability::MachineApplicable,
+            );
+            return;
+        }
+
+        err.span_suggestion_verbose(
+            local_decl.source_info.span.shrink_to_lo(),
+            "consider changing this to be mutable",
+            "mut ",
+            Applicability::MachineApplicable,
+        );
+    }
+
     // point to span of upvar making closure call require mutable borrow
     fn show_mutating_upvar(
         &self,
@@ -953,6 +1027,44 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         }
     }
 
+    fn suggest_using_iter_mut(&self, err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>) {
+        let source = self.body.source;
+        let hir = self.infcx.tcx.hir();
+        if let InstanceDef::Item(def_id) = source.instance
+            && let Some(Node::Expr(hir::Expr { hir_id, kind, ..})) = hir.get_if_local(def_id)
+            && let ExprKind::Closure(closure) = kind && closure.movability == None
+            && let Some(Node::Expr(expr)) = hir.find_parent(*hir_id) {
+                let mut cur_expr = expr;
+                while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind {
+                    if path_segment.ident.name == sym::iter {
+                        // check `_ty` has `iter_mut` method
+                        let res = self
+                            .infcx
+                            .tcx
+                            .typeck(path_segment.hir_id.owner.def_id)
+                            .type_dependent_def_id(cur_expr.hir_id)
+                            .and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
+                            .map(|def_id| self.infcx.tcx.associated_items(def_id))
+                            .map(|assoc_items| {
+                                assoc_items.filter_by_name_unhygienic(sym::iter_mut).peekable()
+                            });
+
+                        if let Some(mut res) = res && res.peek().is_some() {
+                            err.span_suggestion_verbose(
+                                path_segment.ident.span,
+                                "you may want to use `iter_mut` here",
+                                "iter_mut",
+                                Applicability::MaybeIncorrect,
+                            );
+                        }
+                        break;
+                    } else {
+                        cur_expr = recv;
+                    }
+                }
+            }
+    }
+
     fn suggest_make_local_mut(
         &self,
         err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs
index 679a19710a7..3f60f5aca71 100644
--- a/compiler/rustc_borrowck/src/nll.rs
+++ b/compiler/rustc_borrowck/src/nll.rs
@@ -10,6 +10,7 @@ use rustc_middle::mir::{
     Body, ClosureOutlivesSubject, ClosureRegionRequirements, LocalKind, Location, Promoted,
     START_BLOCK,
 };
+use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, OpaqueHiddenType, TyCtxt};
 use rustc_span::symbol::sym;
 use std::env;
@@ -441,7 +442,10 @@ fn for_each_region_constraint<'tcx>(
         let subject = match req.subject {
             ClosureOutlivesSubject::Region(subject) => format!("{subject:?}"),
             ClosureOutlivesSubject::Ty(ty) => {
-                format!("{:?}", ty.instantiate(tcx, |vid| ty::Region::new_var(tcx, vid)))
+                with_no_trimmed_paths!(format!(
+                    "{}",
+                    ty.instantiate(tcx, |vid| ty::Region::new_var(tcx, vid))
+                ))
             }
         };
         with_msg(format!("where {}: {:?}", subject, req.outlived_free_region,))?;
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
index 4da7b602571..b7da15af6dc 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
@@ -328,19 +328,26 @@ fn check_opaque_type_well_formed<'tcx>(
 
     // Require that the hidden type actually fulfills all the bounds of the opaque type, even without
     // the bounds that the function supplies.
-    let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), identity_args);
-    ocx.eq(&ObligationCause::misc(definition_span, def_id), param_env, opaque_ty, definition_ty)
-        .map_err(|err| {
-            infcx
-                .err_ctxt()
-                .report_mismatched_types(
-                    &ObligationCause::misc(definition_span, def_id),
-                    opaque_ty,
-                    definition_ty,
-                    err,
-                )
-                .emit()
-        })?;
+    let mut obligations = vec![];
+    infcx
+        .insert_hidden_type(
+            OpaqueTypeKey { def_id, args: identity_args },
+            &ObligationCause::misc(definition_span, def_id),
+            param_env,
+            definition_ty,
+            true,
+            &mut obligations,
+        )
+        .unwrap();
+    infcx.add_item_bounds_for_hidden_type(
+        def_id.to_def_id(),
+        identity_args,
+        ObligationCause::misc(definition_span, def_id),
+        param_env,
+        definition_ty,
+        &mut obligations,
+    );
+    ocx.register_obligations(obligations);
 
     // Require the hidden type to be well-formed with only the generics of the opaque type.
     // Defining use functions may have more bounds than the opaque type, which is ok, as long as the
diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs
index d1d8cfa74aa..ca3ccf439f2 100644
--- a/compiler/rustc_borrowck/src/session_diagnostics.rs
+++ b/compiler/rustc_borrowck/src/session_diagnostics.rs
@@ -452,3 +452,10 @@ pub(crate) enum TypeNoCopy<'a, 'tcx> {
     #[note(borrowck_ty_no_impl_copy)]
     Note { is_partial_move: bool, ty: Ty<'tcx>, place: &'a str },
 }
+
+#[derive(Diagnostic)]
+#[diag(borrowck_simd_shuffle_last_const)]
+pub(crate) struct SimdShuffleLastConst {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 28286243e82..0f661421cdc 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -50,7 +50,7 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
 use rustc_mir_dataflow::move_paths::MoveData;
 use rustc_mir_dataflow::ResultsCursor;
 
-use crate::session_diagnostics::MoveUnsized;
+use crate::session_diagnostics::{MoveUnsized, SimdShuffleLastConst};
 use crate::{
     borrow_set::BorrowSet,
     constraints::{OutlivesConstraint, OutlivesConstraintSet},
@@ -1426,7 +1426,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         .add_element(region_vid, term_location);
                 }
 
-                self.check_call_inputs(body, term, &sig, args, term_location, *call_source);
+                self.check_call_inputs(body, term, func, &sig, args, term_location, *call_source);
             }
             TerminatorKind::Assert { cond, msg, .. } => {
                 self.check_operand(cond, term_location);
@@ -1546,25 +1546,36 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         }
     }
 
+    #[instrument(level = "debug", skip(self, body, term, func, term_location, call_source))]
     fn check_call_inputs(
         &mut self,
         body: &Body<'tcx>,
         term: &Terminator<'tcx>,
+        func: &Operand<'tcx>,
         sig: &ty::FnSig<'tcx>,
         args: &[Operand<'tcx>],
         term_location: Location,
         call_source: CallSource,
     ) {
-        debug!("check_call_inputs({:?}, {:?})", sig, args);
         if args.len() < sig.inputs().len() || (args.len() > sig.inputs().len() && !sig.c_variadic) {
             span_mirbug!(self, term, "call to {:?} with wrong # of args", sig);
         }
 
-        let func_ty = if let TerminatorKind::Call { func, .. } = &term.kind {
-            Some(func.ty(body, self.infcx.tcx))
-        } else {
-            None
-        };
+        let func_ty = func.ty(body, self.infcx.tcx);
+        if let ty::FnDef(def_id, _) = *func_ty.kind() {
+            if self.tcx().is_intrinsic(def_id) {
+                match self.tcx().item_name(def_id) {
+                    sym::simd_shuffle => {
+                        if !matches!(args[2], Operand::Constant(_)) {
+                            self.tcx()
+                                .sess
+                                .emit_err(SimdShuffleLastConst { span: term.source_info.span });
+                        }
+                    }
+                    _ => {}
+                }
+            }
+        }
         debug!(?func_ty);
 
         for (n, (fn_arg, op_arg)) in iter::zip(sig.inputs(), args).enumerate() {
@@ -1572,7 +1583,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
 
             let op_arg_ty = self.normalize(op_arg_ty, term_location);
             let category = if call_source.from_hir_call() {
-                ConstraintCategory::CallArgument(self.infcx.tcx.erase_regions(func_ty))
+                ConstraintCategory::CallArgument(Some(self.infcx.tcx.erase_regions(func_ty)))
             } else {
                 ConstraintCategory::Boring
             };
diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs
index 56945f43fcd..3b5f1178db5 100644
--- a/compiler/rustc_borrowck/src/universal_regions.rs
+++ b/compiler/rustc_borrowck/src/universal_regions.rs
@@ -21,6 +21,7 @@ use rustc_hir::BodyOwnerKind;
 use rustc_index::IndexVec;
 use rustc_infer::infer::NllRegionVariableOrigin;
 use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, InlineConstArgs, InlineConstArgsParts, RegionVid, Ty, TyCtxt};
 use rustc_middle::ty::{GenericArgs, GenericArgsRef};
 use rustc_span::symbol::{kw, sym};
@@ -332,10 +333,16 @@ impl<'tcx> UniversalRegions<'tcx> {
     pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diagnostic) {
         match self.defining_ty {
             DefiningTy::Closure(def_id, args) => {
+                let v = with_no_trimmed_paths!(
+                    args[tcx.generics_of(def_id).parent_count..]
+                        .iter()
+                        .map(|arg| arg.to_string())
+                        .collect::<Vec<_>>()
+                );
                 err.note(format!(
-                    "defining type: {} with closure args {:#?}",
+                    "defining type: {} with closure args [\n    {},\n]",
                     tcx.def_path_str_with_args(def_id, args),
-                    &args[tcx.generics_of(def_id).parent_count..],
+                    v.join(",\n    "),
                 ));
 
                 // FIXME: It'd be nice to print the late-bound regions
@@ -348,10 +355,16 @@ impl<'tcx> UniversalRegions<'tcx> {
                 });
             }
             DefiningTy::Generator(def_id, args, _) => {
+                let v = with_no_trimmed_paths!(
+                    args[tcx.generics_of(def_id).parent_count..]
+                        .iter()
+                        .map(|arg| arg.to_string())
+                        .collect::<Vec<_>>()
+                );
                 err.note(format!(
-                    "defining type: {} with generator args {:#?}",
+                    "defining type: {} with generator args [\n    {},\n]",
                     tcx.def_path_str_with_args(def_id, args),
-                    &args[tcx.generics_of(def_id).parent_count..],
+                    v.join(",\n    "),
                 ));
 
                 // FIXME: As above, we'd like to print out the region
diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs
index bda473120ed..0682d48aca6 100644
--- a/compiler/rustc_builtin_macros/src/assert/context.rs
+++ b/compiler/rustc_builtin_macros/src/assert/context.rs
@@ -241,7 +241,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
                 self.manage_cond_expr(prefix);
                 self.manage_cond_expr(suffix);
             }
-            ExprKind::Let(_, local_expr, _) => {
+            ExprKind::Let(_, local_expr, _, _) => {
                 self.manage_cond_expr(local_expr);
             }
             ExprKind::Match(local_expr, _) => {
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
index c78a0eb04a0..745358fde4b 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
@@ -34,7 +34,7 @@ pub fn expand_deriving_eq(
             attributes: thin_vec![
                 cx.attr_word(sym::inline, span),
                 cx.attr_nested_word(sym::doc, sym::hidden, span),
-                cx.attr_word(sym::no_coverage, span)
+                cx.attr_nested_word(sym::coverage, sym::off, span)
             ],
             fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
             combine_substructure: combine_substructure(Box::new(|a, b, c| {
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 6597ee3cf1b..edc6f9f098e 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -88,7 +88,7 @@
 //!
 //! When generating the `expr` for the `A` impl, the `SubstructureFields` is
 //!
-//! ```{.text}
+//! ```text
 //! Struct(vec![FieldInfo {
 //!            span: <span of x>
 //!            name: Some(<ident of x>),
@@ -99,7 +99,7 @@
 //!
 //! For the `B` impl, called with `B(a)` and `B(b)`,
 //!
-//! ```{.text}
+//! ```text
 //! Struct(vec![FieldInfo {
 //!           span: <span of `i32`>,
 //!           name: None,
@@ -113,7 +113,7 @@
 //! When generating the `expr` for a call with `self == C0(a)` and `other
 //! == C0(b)`, the SubstructureFields is
 //!
-//! ```{.text}
+//! ```text
 //! EnumMatching(0, <ast::Variant for C0>,
 //!              vec![FieldInfo {
 //!                 span: <span of i32>
@@ -125,7 +125,7 @@
 //!
 //! For `C1 {x}` and `C1 {x}`,
 //!
-//! ```{.text}
+//! ```text
 //! EnumMatching(1, <ast::Variant for C1>,
 //!              vec![FieldInfo {
 //!                 span: <span of x>
@@ -137,7 +137,7 @@
 //!
 //! For the tags,
 //!
-//! ```{.text}
+//! ```text
 //! EnumTag(
 //!     &[<ident of self tag>, <ident of other tag>], <expr to combine with>)
 //! ```
@@ -149,7 +149,7 @@
 //!
 //! A static method on the types above would result in,
 //!
-//! ```{.text}
+//! ```text
 //! StaticStruct(<ast::VariantData of A>, Named(vec![(<ident of x>, <span of x>)]))
 //!
 //! StaticStruct(<ast::VariantData of B>, Unnamed(vec![<span of x>]))
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index d8846a9f0aa..53ff089d7b4 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -254,7 +254,7 @@ fn generate_test_harness(
     let expn_id = ext_cx.resolver.expansion_for_ast_pass(
         DUMMY_SP,
         AstPass::TestHarness,
-        &[sym::test, sym::rustc_attrs, sym::no_coverage],
+        &[sym::test, sym::rustc_attrs, sym::coverage_attribute],
         None,
     );
     let def_site = DUMMY_SP.with_def_site_ctxt(expn_id.to_expn_id());
@@ -335,8 +335,8 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
 
     // #[rustc_main]
     let main_attr = ecx.attr_word(sym::rustc_main, sp);
-    // #[no_coverage]
-    let no_coverage_attr = ecx.attr_word(sym::no_coverage, sp);
+    // #[coverage(off)]
+    let coverage_attr = ecx.attr_nested_word(sym::coverage, sym::off, sp);
 
     // pub fn main() { ... }
     let main_ret_ty = ecx.ty(sp, ast::TyKind::Tup(ThinVec::new()));
@@ -366,7 +366,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
 
     let main = P(ast::Item {
         ident: main_id,
-        attrs: thin_vec![main_attr, no_coverage_attr],
+        attrs: thin_vec![main_attr, coverage_attr],
         id: ast::DUMMY_NODE_ID,
         kind: main,
         vis: ast::Visibility { span: sp, kind: ast::VisibilityKind::Public, tokens: None },
diff --git a/compiler/rustc_codegen_cranelift/docs/usage.md b/compiler/rustc_codegen_cranelift/docs/usage.md
index c6210f958d6..135a51ce392 100644
--- a/compiler/rustc_codegen_cranelift/docs/usage.md
+++ b/compiler/rustc_codegen_cranelift/docs/usage.md
@@ -54,7 +54,7 @@ These are a few functions that allow you to easily run rust code from the shell
 
 ```bash
 function jit_naked() {
-    echo "$@" | $cg_clif_dir/dist/rustc-clif - -Zunstable-features -Cllvm-args=mode=jit -Cprefer-dynamic
+    echo "$@" | $cg_clif_dir/dist/rustc-clif - -Zunstable-options -Cllvm-args=mode=jit-lazy -Cprefer-dynamic
 }
 
 function jit() {
diff --git a/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml b/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml
index fa175edcae6..5b79d6569bb 100644
--- a/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml
+++ b/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml
@@ -4,9 +4,9 @@ version = 3
 
 [[package]]
 name = "addr2line"
-version = "0.20.0"
+version = "0.21.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
 dependencies = [
  "compiler_builtins",
  "gimli",
@@ -140,9 +140,9 @@ dependencies = [
 
 [[package]]
 name = "gimli"
-version = "0.27.2"
+version = "0.28.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
+checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
 dependencies = [
  "compiler_builtins",
  "rustc-std-workspace-alloc",
@@ -205,9 +205,9 @@ dependencies = [
 
 [[package]]
 name = "object"
-version = "0.31.1"
+version = "0.32.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1"
+checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe"
 dependencies = [
  "compiler_builtins",
  "memchr",
diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain
index 5689bdee64d..2cc5d7777a6 100644
--- a/compiler/rustc_codegen_cranelift/rust-toolchain
+++ b/compiler/rustc_codegen_cranelift/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2023-08-08"
+channel = "nightly-2023-09-06"
 components = ["rust-src", "rustc-dev", "llvm-tools"]
diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
index c163b854384..3fc462a39cc 100755
--- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
+++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
@@ -45,6 +45,7 @@ rm tests/ui/proc-macro/quote-debug.rs
 rm tests/ui/proc-macro/no-missing-docs.rs
 rm tests/ui/rust-2018/proc-macro-crate-in-paths.rs
 rm tests/ui/proc-macro/allowed-signatures.rs
+rm tests/ui/proc-macro/no-mangle-in-proc-macro-issue-111888.rs
 
 # vendor intrinsics
 rm tests/ui/sse2.rs # cpuid not supported, so sse2 not detected
@@ -114,6 +115,7 @@ rm tests/ui/mir/mir_misc_casts.rs # depends on deduplication of constants
 rm tests/ui/mir/mir_raw_fat_ptr.rs # same
 rm tests/ui/consts/issue-33537.rs # same
 rm tests/ui/layout/valid_range_oob.rs # different ICE message
+rm tests/ui/const-generics/generic_const_exprs/issue-80742.rs # gives error instead of ICE with cg_clif
 
 rm tests/ui/consts/issue-miri-1910.rs # different error message
 rm tests/ui/consts/offset_ub.rs # same
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 9159bc36987..54f82dcc8ae 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -723,11 +723,8 @@ fn codegen_stmt<'tcx>(
                 }
                 Rvalue::Repeat(ref operand, times) => {
                     let operand = codegen_operand(fx, operand);
-                    let times = fx
-                        .monomorphize(times)
-                        .eval(fx.tcx, ParamEnv::reveal_all())
-                        .try_to_bits(fx.tcx.data_layout.pointer_size)
-                        .unwrap();
+                    let times =
+                        fx.monomorphize(times).eval_target_usize(fx.tcx, ParamEnv::reveal_all());
                     if operand.layout().size.bytes() == 0 {
                         // Do nothing for ZST's
                     } else if fx.clif_type(operand.layout().ty) == Some(types::I8) {
diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs
index a934b0767f1..8c67760a0b9 100644
--- a/compiler/rustc_codegen_cranelift/src/constant.rs
+++ b/compiler/rustc_codegen_cranelift/src/constant.rs
@@ -3,7 +3,7 @@
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::mir::interpret::{
-    read_target_uint, AllocId, ConstAllocation, ConstValue, ErrorHandled, GlobalAlloc, Scalar,
+    read_target_uint, AllocId, ConstValue, ErrorHandled, GlobalAlloc, Scalar,
 };
 
 use cranelift_module::*;
@@ -77,31 +77,9 @@ pub(crate) fn eval_mir_constant<'tcx>(
     fx: &FunctionCx<'_, '_, 'tcx>,
     constant: &Constant<'tcx>,
 ) -> Option<(ConstValue<'tcx>, Ty<'tcx>)> {
-    let constant_kind = fx.monomorphize(constant.literal);
-    let uv = match constant_kind {
-        ConstantKind::Ty(const_) => match const_.kind() {
-            ty::ConstKind::Unevaluated(uv) => uv.expand(),
-            ty::ConstKind::Value(val) => {
-                return Some((fx.tcx.valtree_to_const_val((const_.ty(), val)), const_.ty()));
-            }
-            err => span_bug!(
-                constant.span,
-                "encountered bad ConstKind after monomorphizing: {:?}",
-                err
-            ),
-        },
-        ConstantKind::Unevaluated(mir::UnevaluatedConst { def, .. }, _)
-            if fx.tcx.is_static(def) =>
-        {
-            span_bug!(constant.span, "MIR constant refers to static");
-        }
-        ConstantKind::Unevaluated(uv, _) => uv,
-        ConstantKind::Val(val, _) => return Some((val, constant_kind.ty())),
-    };
-
-    let val = fx
-        .tcx
-        .const_eval_resolve(ty::ParamEnv::reveal_all(), uv, None)
+    let cv = fx.monomorphize(constant.literal);
+    let val = cv
+        .eval(fx.tcx, ty::ParamEnv::reveal_all(), Some(constant.span))
         .map_err(|err| match err {
             ErrorHandled::Reported(_) => {
                 fx.tcx.sess.span_err(constant.span, "erroneous constant encountered");
@@ -111,7 +89,7 @@ pub(crate) fn eval_mir_constant<'tcx>(
             }
         })
         .ok();
-    val.map(|val| (val, constant_kind.ty()))
+    val.map(|val| (val, cv.ty()))
 }
 
 pub(crate) fn codegen_constant_operand<'tcx>(
@@ -138,7 +116,7 @@ pub(crate) fn codegen_const_value<'tcx>(
     }
 
     match const_val {
-        ConstValue::ZeroSized => unreachable!(), // we already handles ZST above
+        ConstValue::ZeroSized => unreachable!(), // we already handled ZST above
         ConstValue::Scalar(x) => match x {
             Scalar::Int(int) => {
                 if fx.clif_type(layout.ty).is_some() {
@@ -222,13 +200,14 @@ pub(crate) fn codegen_const_value<'tcx>(
                 CValue::by_val(val, layout)
             }
         },
-        ConstValue::ByRef { alloc, offset } => CValue::by_ref(
-            pointer_for_allocation(fx, alloc)
+        ConstValue::Indirect { alloc_id, offset } => CValue::by_ref(
+            pointer_for_allocation(fx, alloc_id)
                 .offset_i64(fx, i64::try_from(offset.bytes()).unwrap()),
             layout,
         ),
         ConstValue::Slice { data, start, end } => {
-            let ptr = pointer_for_allocation(fx, data)
+            let alloc_id = fx.tcx.reserve_and_set_memory_alloc(data);
+            let ptr = pointer_for_allocation(fx, alloc_id)
                 .offset_i64(fx, i64::try_from(start).unwrap())
                 .get_addr(fx);
             let len = fx
@@ -242,9 +221,9 @@ pub(crate) fn codegen_const_value<'tcx>(
 
 fn pointer_for_allocation<'tcx>(
     fx: &mut FunctionCx<'_, '_, 'tcx>,
-    alloc: ConstAllocation<'tcx>,
+    alloc_id: AllocId,
 ) -> crate::pointer::Pointer {
-    let alloc_id = fx.tcx.create_memory_alloc(alloc);
+    let alloc = fx.tcx.global_alloc(alloc_id).unwrap_memory();
     let data_id = data_id_for_alloc_id(
         &mut fx.constants_cx,
         &mut *fx.module,
@@ -375,6 +354,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
                         unreachable!()
                     }
                 };
+                // FIXME: should we have a cache so we don't do this multiple times for the same `ConstAllocation`?
                 let data_id = *cx.anon_allocs.entry(alloc_id).or_insert_with(|| {
                     module.declare_anonymous_data(alloc.inner().mutability.is_mut(), false).unwrap()
                 });
diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
index 998263de584..b19b935a0fe 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
@@ -81,7 +81,7 @@ impl DebugContext {
 
         match tcx.sess.source_map().lookup_line(span.lo()) {
             Ok(SourceFileAndLine { sf: file, line }) => {
-                let line_pos = file.lines(|lines| lines[line]);
+                let line_pos = file.lines()[line];
                 let col = file.relative_position(span.lo()) - line_pos;
 
                 (file, u64::try_from(line).unwrap() + 1, u64::from(col.to_u32()) + 1)
diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
index d143bcc96ef..3e93830951c 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
@@ -269,7 +269,7 @@ fn module_codegen(
     ),
 ) -> OngoingModuleCodegen {
     let (cgu_name, mut cx, mut module, codegened_functions) =
-        tcx.prof.verbose_generic_activity_with_arg("codegen cgu", cgu_name.as_str()).run(|| {
+        tcx.prof.generic_activity_with_arg("codegen cgu", cgu_name.as_str()).run(|| {
             let cgu = tcx.codegen_unit(cgu_name);
             let mono_items = cgu.items_in_deterministic_order(tcx);
 
@@ -322,35 +322,24 @@ fn module_codegen(
         });
 
     OngoingModuleCodegen::Async(std::thread::spawn(move || {
-        cx.profiler.clone().verbose_generic_activity_with_arg("compile functions", &*cgu_name).run(
-            || {
-                cranelift_codegen::timing::set_thread_profiler(Box::new(super::MeasuremeProfiler(
-                    cx.profiler.clone(),
-                )));
-
-                let mut cached_context = Context::new();
-                for codegened_func in codegened_functions {
-                    crate::base::compile_fn(
-                        &mut cx,
-                        &mut cached_context,
-                        &mut module,
-                        codegened_func,
-                    );
-                }
-            },
-        );
+        cx.profiler.clone().generic_activity_with_arg("compile functions", &*cgu_name).run(|| {
+            cranelift_codegen::timing::set_thread_profiler(Box::new(super::MeasuremeProfiler(
+                cx.profiler.clone(),
+            )));
+
+            let mut cached_context = Context::new();
+            for codegened_func in codegened_functions {
+                crate::base::compile_fn(&mut cx, &mut cached_context, &mut module, codegened_func);
+            }
+        });
 
-        let global_asm_object_file = cx
-            .profiler
-            .verbose_generic_activity_with_arg("compile assembly", &*cgu_name)
-            .run(|| {
+        let global_asm_object_file =
+            cx.profiler.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| {
                 crate::global_asm::compile_global_asm(&global_asm_config, &cgu_name, &cx.global_asm)
             })?;
 
-        let codegen_result = cx
-            .profiler
-            .verbose_generic_activity_with_arg("write object file", &*cgu_name)
-            .run(|| {
+        let codegen_result =
+            cx.profiler.generic_activity_with_arg("write object file", &*cgu_name).run(|| {
                 emit_cgu(
                     &global_asm_config.output_filenames,
                     &cx.profiler,
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
index fdd27a454e0..e62de6b6147 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
@@ -177,244 +177,6 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
                 bool_to_zero_or_max_uint(fx, res_lane_ty, res_lane)
             });
         }
-        "llvm.x86.sse2.psrli.d" => {
-            let (a, imm8) = match args {
-                [a, imm8] => (a, imm8),
-                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
-            };
-            let a = codegen_operand(fx, a);
-            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
-                .expect("llvm.x86.sse2.psrli.d imm8 not const");
-
-            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
-                .try_to_bits(Size::from_bytes(4))
-                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
-            {
-                imm8 if imm8 < 32 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)),
-                _ => fx.bcx.ins().iconst(types::I32, 0),
-            });
-        }
-        "llvm.x86.sse2.psrai.d" => {
-            let (a, imm8) = match args {
-                [a, imm8] => (a, imm8),
-                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
-            };
-            let a = codegen_operand(fx, a);
-            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
-                .expect("llvm.x86.sse2.psrai.d imm8 not const");
-
-            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
-                .try_to_bits(Size::from_bytes(4))
-                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
-            {
-                imm8 if imm8 < 32 => fx.bcx.ins().sshr_imm(lane, i64::from(imm8 as u8)),
-                _ => fx.bcx.ins().iconst(types::I32, 0),
-            });
-        }
-        "llvm.x86.sse2.pslli.d" => {
-            let (a, imm8) = match args {
-                [a, imm8] => (a, imm8),
-                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
-            };
-            let a = codegen_operand(fx, a);
-            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
-                .expect("llvm.x86.sse2.pslli.d imm8 not const");
-
-            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
-                .try_to_bits(Size::from_bytes(4))
-                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
-            {
-                imm8 if imm8 < 32 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)),
-                _ => fx.bcx.ins().iconst(types::I32, 0),
-            });
-        }
-        "llvm.x86.sse2.psrli.w" => {
-            let (a, imm8) = match args {
-                [a, imm8] => (a, imm8),
-                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
-            };
-            let a = codegen_operand(fx, a);
-            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
-                .expect("llvm.x86.sse2.psrli.d imm8 not const");
-
-            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
-                .try_to_bits(Size::from_bytes(4))
-                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
-            {
-                imm8 if imm8 < 16 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)),
-                _ => fx.bcx.ins().iconst(types::I32, 0),
-            });
-        }
-        "llvm.x86.sse2.psrai.w" => {
-            let (a, imm8) = match args {
-                [a, imm8] => (a, imm8),
-                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
-            };
-            let a = codegen_operand(fx, a);
-            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
-                .expect("llvm.x86.sse2.psrai.d imm8 not const");
-
-            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
-                .try_to_bits(Size::from_bytes(4))
-                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
-            {
-                imm8 if imm8 < 16 => fx.bcx.ins().sshr_imm(lane, i64::from(imm8 as u8)),
-                _ => fx.bcx.ins().iconst(types::I32, 0),
-            });
-        }
-        "llvm.x86.sse2.pslli.w" => {
-            let (a, imm8) = match args {
-                [a, imm8] => (a, imm8),
-                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
-            };
-            let a = codegen_operand(fx, a);
-            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
-                .expect("llvm.x86.sse2.pslli.d imm8 not const");
-
-            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
-                .try_to_bits(Size::from_bytes(4))
-                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
-            {
-                imm8 if imm8 < 16 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)),
-                _ => fx.bcx.ins().iconst(types::I32, 0),
-            });
-        }
-        "llvm.x86.avx.psrli.d" => {
-            let (a, imm8) = match args {
-                [a, imm8] => (a, imm8),
-                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
-            };
-            let a = codegen_operand(fx, a);
-            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
-                .expect("llvm.x86.avx.psrli.d imm8 not const");
-
-            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
-                .try_to_bits(Size::from_bytes(4))
-                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
-            {
-                imm8 if imm8 < 32 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)),
-                _ => fx.bcx.ins().iconst(types::I32, 0),
-            });
-        }
-        "llvm.x86.avx.psrai.d" => {
-            let (a, imm8) = match args {
-                [a, imm8] => (a, imm8),
-                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
-            };
-            let a = codegen_operand(fx, a);
-            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
-                .expect("llvm.x86.avx.psrai.d imm8 not const");
-
-            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
-                .try_to_bits(Size::from_bytes(4))
-                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
-            {
-                imm8 if imm8 < 32 => fx.bcx.ins().sshr_imm(lane, i64::from(imm8 as u8)),
-                _ => fx.bcx.ins().iconst(types::I32, 0),
-            });
-        }
-        "llvm.x86.sse2.psrli.q" => {
-            let (a, imm8) = match args {
-                [a, imm8] => (a, imm8),
-                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
-            };
-            let a = codegen_operand(fx, a);
-            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
-                .expect("llvm.x86.avx.psrli.q imm8 not const");
-
-            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
-                .try_to_bits(Size::from_bytes(4))
-                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
-            {
-                imm8 if imm8 < 64 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)),
-                _ => fx.bcx.ins().iconst(types::I32, 0),
-            });
-        }
-        "llvm.x86.sse2.pslli.q" => {
-            let (a, imm8) = match args {
-                [a, imm8] => (a, imm8),
-                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
-            };
-            let a = codegen_operand(fx, a);
-            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
-                .expect("llvm.x86.avx.pslli.q imm8 not const");
-
-            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
-                .try_to_bits(Size::from_bytes(4))
-                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
-            {
-                imm8 if imm8 < 64 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)),
-                _ => fx.bcx.ins().iconst(types::I32, 0),
-            });
-        }
-        "llvm.x86.avx.pslli.d" => {
-            let (a, imm8) = match args {
-                [a, imm8] => (a, imm8),
-                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
-            };
-            let a = codegen_operand(fx, a);
-            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
-                .expect("llvm.x86.avx.pslli.d imm8 not const");
-
-            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
-                .try_to_bits(Size::from_bytes(4))
-                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
-            {
-                imm8 if imm8 < 32 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)),
-                _ => fx.bcx.ins().iconst(types::I32, 0),
-            });
-        }
-        "llvm.x86.avx2.psrli.w" => {
-            let (a, imm8) = match args {
-                [a, imm8] => (a, imm8),
-                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
-            };
-            let a = codegen_operand(fx, a);
-            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
-                .expect("llvm.x86.avx.psrli.w imm8 not const");
-
-            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
-                .try_to_bits(Size::from_bytes(4))
-                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
-            {
-                imm8 if imm8 < 16 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)),
-                _ => fx.bcx.ins().iconst(types::I32, 0),
-            });
-        }
-        "llvm.x86.avx2.psrai.w" => {
-            let (a, imm8) = match args {
-                [a, imm8] => (a, imm8),
-                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
-            };
-            let a = codegen_operand(fx, a);
-            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
-                .expect("llvm.x86.avx.psrai.w imm8 not const");
-
-            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
-                .try_to_bits(Size::from_bytes(4))
-                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
-            {
-                imm8 if imm8 < 16 => fx.bcx.ins().sshr_imm(lane, i64::from(imm8 as u8)),
-                _ => fx.bcx.ins().iconst(types::I32, 0),
-            });
-        }
-        "llvm.x86.avx2.pslli.w" => {
-            let (a, imm8) = match args {
-                [a, imm8] => (a, imm8),
-                _ => bug!("wrong number of args for intrinsic {intrinsic}"),
-            };
-            let a = codegen_operand(fx, a);
-            let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
-                .expect("llvm.x86.avx.pslli.w imm8 not const");
-
-            simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
-                .try_to_bits(Size::from_bytes(4))
-                .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
-            {
-                imm8 if imm8 < 16 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)),
-                _ => fx.bcx.ins().iconst(types::I32, 0),
-            });
-        }
         "llvm.x86.ssse3.pshuf.b.128" | "llvm.x86.avx2.pshuf.b" => {
             let (a, b) = match args {
                 [a, b] => (a, b),
@@ -506,14 +268,6 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
             ret.place_lane(fx, 2).to_ptr().store(fx, res_2, MemFlags::trusted());
             ret.place_lane(fx, 3).to_ptr().store(fx, res_3, MemFlags::trusted());
         }
-        "llvm.x86.sse2.storeu.dq" | "llvm.x86.sse2.storeu.pd" => {
-            intrinsic_args!(fx, args => (mem_addr, a); intrinsic);
-            let mem_addr = mem_addr.load_scalar(fx);
-
-            // FIXME correctly handle the unalignment
-            let dest = CPlace::for_ptr(Pointer::new(mem_addr), a.layout());
-            dest.write_cvalue(fx, a);
-        }
         "llvm.x86.ssse3.pabs.b.128" | "llvm.x86.ssse3.pabs.w.128" | "llvm.x86.ssse3.pabs.d.128" => {
             let a = match args {
                 [a] => a,
@@ -571,8 +325,6 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
 // llvm.x86.avx2.vperm2i128
 // llvm.x86.ssse3.pshuf.b.128
 // llvm.x86.avx2.pshuf.b
-// llvm.x86.avx2.psrli.w
-// llvm.x86.sse2.psrli.w
 
 fn llvm_add_sub<'tcx>(
     fx: &mut FunctionCx<'_, '_, 'tcx>,
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
index 9863e40b5b7..c64a4008996 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
@@ -172,7 +172,8 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                     .expect("simd_shuffle idx not const");
 
                 let idx_bytes = match idx_const {
-                    ConstValue::ByRef { alloc, offset } => {
+                    ConstValue::Indirect { alloc_id, offset } => {
+                        let alloc = fx.tcx.global_alloc(alloc_id).unwrap_memory();
                         let size = Size::from_bytes(
                             4 * ret_lane_count, /* size_of([u32; ret_lane_count]) */
                         );
diff --git a/compiler/rustc_codegen_cranelift/src/vtable.rs b/compiler/rustc_codegen_cranelift/src/vtable.rs
index 7598c6eee03..41ea0b122de 100644
--- a/compiler/rustc_codegen_cranelift/src/vtable.rs
+++ b/compiler/rustc_codegen_cranelift/src/vtable.rs
@@ -48,19 +48,12 @@ pub(crate) fn get_ptr_and_method_ref<'tcx>(
 ) -> (Pointer, Value) {
     let (ptr, vtable) = 'block: {
         if let Abi::Scalar(_) = arg.layout().abi {
-            'descend_newtypes: while !arg.layout().ty.is_unsafe_ptr() && !arg.layout().ty.is_ref() {
-                for i in 0..arg.layout().fields.count() {
-                    let field = arg.value_field(fx, FieldIdx::new(i));
-                    if !field.layout().is_1zst() {
-                        // we found the one non-1-ZST field that is allowed
-                        // now find *its* non-zero-sized field, or stop if it's a
-                        // pointer
-                        arg = field;
-                        continue 'descend_newtypes;
-                    }
-                }
-
-                bug!("receiver has no non-zero-sized fields {:?}", arg);
+            while !arg.layout().ty.is_unsafe_ptr() && !arg.layout().ty.is_ref() {
+                let (idx, _) = arg
+                    .layout()
+                    .non_1zst_field(fx)
+                    .expect("not exactly one non-1-ZST field in a `DispatchFromDyn` type");
+                arg = arg.value_field(fx, FieldIdx::new(idx));
             }
         }
 
diff --git a/compiler/rustc_codegen_gcc/src/callee.rs b/compiler/rustc_codegen_gcc/src/callee.rs
index a96bd66ba79..9fc77627b1b 100644
--- a/compiler/rustc_codegen_gcc/src/callee.rs
+++ b/compiler/rustc_codegen_gcc/src/callee.rs
@@ -100,7 +100,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>)
             // whether we are sharing generics or not. The important thing here is
             // that the visibility we apply to the declaration is the same one that
             // has been applied to the definition (wherever that definition may be).
-            let is_generic = instance.args.non_erasable_generics().next().is_some();
+            let is_generic = instance.args.non_erasable_generics(tcx, instance.def_id()).next().is_some();
 
             if is_generic {
                 // This is a monomorphization. Its expected visibility depends
diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs
index a81585d4128..d1bfd833cd8 100644
--- a/compiler/rustc_codegen_gcc/src/debuginfo.rs
+++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs
@@ -55,7 +55,7 @@ impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
         _fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
         _llfn: RValue<'gcc>,
         _mir: &mir::Body<'tcx>,
-    ) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>> {
+    ) -> Option<FunctionDebugContext<'tcx, Self::DIScope, Self::DILocation>> {
         // TODO(antoyo)
         None
     }
diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl
index aed4a8f3c85..ddaff36f24b 100644
--- a/compiler/rustc_codegen_llvm/messages.ftl
+++ b/compiler/rustc_codegen_llvm/messages.ftl
@@ -83,6 +83,8 @@ codegen_llvm_unknown_ctarget_feature_prefix =
     unknown feature specified for `-Ctarget-feature`: `{$feature}`
     .note = features must begin with a `+` to enable or `-` to disable it
 
+codegen_llvm_unknown_debuginfo_compression = unknown debuginfo compression algorithm {$algorithm} - will fall back to uncompressed debuginfo
+
 codegen_llvm_write_bytecode = failed to write bytecode to {$path}: {$err}
 
 codegen_llvm_write_ir = failed to write LLVM IR to {$path}
diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs
index 863cb7068f8..64587f98b8a 100644
--- a/compiler/rustc_codegen_llvm/src/abi.rs
+++ b/compiler/rustc_codegen_llvm/src/abi.rs
@@ -340,15 +340,50 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
         };
 
         for arg in args {
+            // Note that the exact number of arguments pushed here is carefully synchronized with
+            // code all over the place, both in the codegen_llvm and codegen_ssa crates. That's how
+            // other code then knows which LLVM argument(s) correspond to the n-th Rust argument.
             let llarg_ty = match &arg.mode {
                 PassMode::Ignore => continue,
-                PassMode::Direct(_) => arg.layout.immediate_llvm_type(cx),
+                PassMode::Direct(_) => {
+                    // ABI-compatible Rust types have the same `layout.abi` (up to validity ranges),
+                    // and for Scalar ABIs the LLVM type is fully determined by `layout.abi`,
+                    // guarnateeing that we generate ABI-compatible LLVM IR. Things get tricky for
+                    // aggregates...
+                    if matches!(arg.layout.abi, abi::Abi::Aggregate { .. }) {
+                        // This really shouldn't happen, since `immediate_llvm_type` will use
+                        // `layout.fields` to turn this Rust type into an LLVM type. This means all
+                        // sorts of Rust type details leak into the ABI. However wasm sadly *does*
+                        // currently use this mode so we have to allow it -- but we absolutely
+                        // shouldn't let any more targets do that.
+                        // (Also see <https://github.com/rust-lang/rust/issues/115666>.)
+                        assert!(
+                            matches!(&*cx.tcx.sess.target.arch, "wasm32" | "wasm64"),
+                            "`PassMode::Direct` for aggregates only allowed on wasm targets\nProblematic type: {:#?}",
+                            arg.layout,
+                        );
+                    }
+                    arg.layout.immediate_llvm_type(cx)
+                }
                 PassMode::Pair(..) => {
+                    // ABI-compatible Rust types have the same `layout.abi` (up to validity ranges),
+                    // so for ScalarPair we can easily be sure that we are generating ABI-compatible
+                    // LLVM IR.
+                    assert!(
+                        matches!(arg.layout.abi, abi::Abi::ScalarPair(..)),
+                        "PassMode::Pair for type {}",
+                        arg.layout.ty
+                    );
                     llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 0, true));
                     llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 1, true));
                     continue;
                 }
                 PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => {
+                    assert!(arg.layout.is_unsized());
+                    // Construct the type of a (wide) pointer to `ty`, and pass its two fields.
+                    // Any two ABI-compatible unsized types have the same metadata type and
+                    // moreover the same metadata value leads to the same dynamic size and
+                    // alignment, so this respects ABI compatibility.
                     let ptr_ty = Ty::new_mut_ptr(cx.tcx, arg.layout.ty);
                     let ptr_layout = cx.layout_of(ptr_ty);
                     llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 0, true));
@@ -360,6 +395,8 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
                     if *pad_i32 {
                         llargument_tys.push(Reg::i32().llvm_type(cx));
                     }
+                    // Compute the LLVM type we use for this function from the cast type.
+                    // We assume here that ABI-compatible Rust types have the same cast type.
                     cast.llvm_type(cx)
                 }
                 PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => cx.type_ptr(),
diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs
index c1d3392386c..ba263296bb4 100644
--- a/compiler/rustc_codegen_llvm/src/back/lto.rs
+++ b/compiler/rustc_codegen_llvm/src/back/lto.rs
@@ -1,4 +1,6 @@
-use crate::back::write::{self, save_temp_bitcode, CodegenDiagnosticsStage, DiagnosticHandlers};
+use crate::back::write::{
+    self, bitcode_section_name, save_temp_bitcode, CodegenDiagnosticsStage, DiagnosticHandlers,
+};
 use crate::errors::{
     DynamicLinkingWithLTO, LlvmError, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib,
 };
@@ -120,6 +122,7 @@ fn prepare_lto(
                 info!("adding bitcode from {}", name);
                 match get_bitcode_slice_from_object_data(
                     child.data(&*archive_data).expect("corrupt rlib"),
+                    cgcx,
                 ) {
                     Ok(data) => {
                         let module = SerializedModule::FromRlib(data.to_vec());
@@ -141,10 +144,29 @@ fn prepare_lto(
     Ok((symbols_below_threshold, upstream_modules))
 }
 
-fn get_bitcode_slice_from_object_data(obj: &[u8]) -> Result<&[u8], LtoBitcodeFromRlib> {
+fn get_bitcode_slice_from_object_data<'a>(
+    obj: &'a [u8],
+    cgcx: &CodegenContext<LlvmCodegenBackend>,
+) -> Result<&'a [u8], LtoBitcodeFromRlib> {
+    // We're about to assume the data here is an object file with sections, but if it's raw LLVM IR that
+    // won't work. Fortunately, if that's what we have we can just return the object directly, so we sniff
+    // the relevant magic strings here and return.
+    if obj.starts_with(b"\xDE\xC0\x17\x0B") || obj.starts_with(b"BC\xC0\xDE") {
+        return Ok(obj);
+    }
+    // We drop the "__LLVM," prefix here because on Apple platforms there's a notion of "segment name"
+    // which in the public API for sections gets treated as part of the section name, but internally
+    // in MachOObjectFile.cpp gets treated separately.
+    let section_name = bitcode_section_name(cgcx).trim_start_matches("__LLVM,");
     let mut len = 0;
-    let data =
-        unsafe { llvm::LLVMRustGetBitcodeSliceFromObjectData(obj.as_ptr(), obj.len(), &mut len) };
+    let data = unsafe {
+        llvm::LLVMRustGetSliceFromObjectDataByName(
+            obj.as_ptr(),
+            obj.len(),
+            section_name.as_ptr(),
+            &mut len,
+        )
+    };
     if !data.is_null() {
         assert!(len != 0);
         let bc = unsafe { slice::from_raw_parts(data, len) };
@@ -583,7 +605,7 @@ pub(crate) fn run_pass_manager(
     module: &mut ModuleCodegen<ModuleLlvm>,
     thin: bool,
 ) -> Result<(), FatalError> {
-    let _timer = cgcx.prof.verbose_generic_activity_with_arg("LLVM_lto_optimize", &*module.name);
+    let _timer = cgcx.prof.generic_activity_with_arg("LLVM_lto_optimize", &*module.name);
     let config = cgcx.config(module.kind);
 
     // Now we have one massive module inside of llmod. Time to run the
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs
index 47cc5bd52e2..1f394a7335c 100644
--- a/compiler/rustc_codegen_llvm/src/back/write.rs
+++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -5,13 +5,17 @@ use crate::back::profiling::{
 use crate::base;
 use crate::common;
 use crate::errors::{
-    CopyBitcode, FromLlvmDiag, FromLlvmOptimizationDiag, LlvmError, WithLlvmError, WriteBytecode,
+    CopyBitcode, FromLlvmDiag, FromLlvmOptimizationDiag, LlvmError, UnknownCompression,
+    WithLlvmError, WriteBytecode,
 };
 use crate::llvm::{self, DiagnosticInfo, PassManager};
 use crate::llvm_util;
 use crate::type_::Type;
 use crate::LlvmCodegenBackend;
 use crate::ModuleLlvm;
+use llvm::{
+    LLVMRustLLVMHasZlibCompressionForDebugSymbols, LLVMRustLLVMHasZstdCompressionForDebugSymbols,
+};
 use rustc_codegen_ssa::back::link::ensure_removed;
 use rustc_codegen_ssa::back::write::{
     BitcodeSection, CodegenContext, EmitObj, ModuleConfig, TargetMachineFactoryConfig,
@@ -216,6 +220,40 @@ pub fn target_machine_factory(
 
     let force_emulated_tls = sess.target.force_emulated_tls;
 
+    // copy the exe path, followed by path all into one buffer
+    // null terminating them so we can use them as null terminated strings
+    let args_cstr_buff = {
+        let mut args_cstr_buff: Vec<u8> = Vec::new();
+        let exe_path = std::env::current_exe().unwrap_or_default();
+        let exe_path_str = exe_path.into_os_string().into_string().unwrap_or_default();
+
+        args_cstr_buff.extend_from_slice(exe_path_str.as_bytes());
+        args_cstr_buff.push(0);
+
+        for arg in sess.expanded_args.iter() {
+            args_cstr_buff.extend_from_slice(arg.as_bytes());
+            args_cstr_buff.push(0);
+        }
+
+        args_cstr_buff
+    };
+
+    let debuginfo_compression = sess.opts.debuginfo_compression.to_string();
+    match sess.opts.debuginfo_compression {
+        rustc_session::config::DebugInfoCompression::Zlib => {
+            if !unsafe { LLVMRustLLVMHasZlibCompressionForDebugSymbols() } {
+                sess.emit_warning(UnknownCompression { algorithm: "zlib" });
+            }
+        }
+        rustc_session::config::DebugInfoCompression::Zstd => {
+            if !unsafe { LLVMRustLLVMHasZstdCompressionForDebugSymbols() } {
+                sess.emit_warning(UnknownCompression { algorithm: "zstd" });
+            }
+        }
+        rustc_session::config::DebugInfoCompression::None => {}
+    };
+    let debuginfo_compression = SmallCStr::new(&debuginfo_compression);
+
     Arc::new(move |config: TargetMachineFactoryConfig| {
         let split_dwarf_file =
             path_mapping.map_prefix(config.split_dwarf_file.unwrap_or_default()).0;
@@ -241,7 +279,10 @@ pub fn target_machine_factory(
                 relax_elf_relocations,
                 use_init_array,
                 split_dwarf_file.as_ptr(),
+                debuginfo_compression.as_ptr(),
                 force_emulated_tls,
+                args_cstr_buff.as_ptr() as *const c_char,
+                args_cstr_buff.len(),
             )
         };
 
@@ -853,6 +894,27 @@ fn create_section_with_flags_asm(section_name: &str, section_flags: &str, data:
     asm
 }
 
+fn target_is_apple(cgcx: &CodegenContext<LlvmCodegenBackend>) -> bool {
+    cgcx.opts.target_triple.triple().contains("-ios")
+        || cgcx.opts.target_triple.triple().contains("-darwin")
+        || cgcx.opts.target_triple.triple().contains("-tvos")
+        || cgcx.opts.target_triple.triple().contains("-watchos")
+}
+
+fn target_is_aix(cgcx: &CodegenContext<LlvmCodegenBackend>) -> bool {
+    cgcx.opts.target_triple.triple().contains("-aix")
+}
+
+pub(crate) fn bitcode_section_name(cgcx: &CodegenContext<LlvmCodegenBackend>) -> &'static str {
+    if target_is_apple(cgcx) {
+        "__LLVM,__bitcode\0"
+    } else if target_is_aix(cgcx) {
+        ".ipa\0"
+    } else {
+        ".llvmbc\0"
+    }
+}
+
 /// Embed the bitcode of an LLVM module in the LLVM module itself.
 ///
 /// This is done primarily for iOS where it appears to be standard to compile C
@@ -913,11 +975,8 @@ unsafe fn embed_bitcode(
     // Unfortunately, LLVM provides no way to set custom section flags. For ELF
     // and COFF we emit the sections using module level inline assembly for that
     // reason (see issue #90326 for historical background).
-    let is_aix = cgcx.opts.target_triple.triple().contains("-aix");
-    let is_apple = cgcx.opts.target_triple.triple().contains("-ios")
-        || cgcx.opts.target_triple.triple().contains("-darwin")
-        || cgcx.opts.target_triple.triple().contains("-tvos")
-        || cgcx.opts.target_triple.triple().contains("-watchos");
+    let is_aix = target_is_aix(cgcx);
+    let is_apple = target_is_apple(cgcx);
     if is_apple
         || is_aix
         || cgcx.opts.target_triple.triple().starts_with("wasm")
@@ -932,13 +991,7 @@ unsafe fn embed_bitcode(
         );
         llvm::LLVMSetInitializer(llglobal, llconst);
 
-        let section = if is_apple {
-            "__LLVM,__bitcode\0"
-        } else if is_aix {
-            ".ipa\0"
-        } else {
-            ".llvmbc\0"
-        };
+        let section = bitcode_section_name(cgcx);
         llvm::LLVMSetSection(llglobal, section.as_ptr().cast());
         llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
         llvm::LLVMSetGlobalConstant(llglobal, llvm::True);
diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs
index 36c098218cf..5254c3f9c9a 100644
--- a/compiler/rustc_codegen_llvm/src/callee.rs
+++ b/compiler/rustc_codegen_llvm/src/callee.rs
@@ -95,7 +95,8 @@ pub fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) ->
         unsafe {
             llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::ExternalLinkage);
 
-            let is_generic = instance.args.non_erasable_generics().next().is_some();
+            let is_generic =
+                instance.args.non_erasable_generics(tcx, instance.def_id()).next().is_some();
 
             if is_generic {
                 // This is a monomorphization. Its expected visibility depends
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
index 97a99e51056..8ba7a11abe5 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
@@ -1,13 +1,14 @@
 use crate::common::CodegenCx;
 use crate::coverageinfo;
-use crate::coverageinfo::ffi::{Counter, CounterExpression, CounterMappingRegion};
+use crate::coverageinfo::ffi::CounterMappingRegion;
+use crate::coverageinfo::map_data::FunctionCoverage;
 use crate::llvm;
 
 use rustc_codegen_ssa::traits::ConstMethods;
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
-use rustc_llvm::RustString;
+use rustc_index::IndexVec;
 use rustc_middle::bug;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::mir::coverage::CodeRegion;
@@ -55,7 +56,7 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
         return;
     }
 
-    let mut mapgen = CoverageMapGenerator::new(tcx);
+    let mut global_file_table = GlobalFileTable::new(tcx);
 
     // Encode coverage mappings and generate function records
     let mut function_data = Vec::new();
@@ -64,12 +65,9 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
         let mangled_function_name = tcx.symbol_name(instance).name;
         let source_hash = function_coverage.source_hash();
         let is_used = function_coverage.is_used();
-        let (expressions, counter_regions) =
-            function_coverage.get_expressions_and_counter_regions();
 
-        let coverage_mapping_buffer = llvm::build_byte_buffer(|coverage_mapping_buffer| {
-            mapgen.write_coverage_mapping(expressions, counter_regions, coverage_mapping_buffer);
-        });
+        let coverage_mapping_buffer =
+            encode_mappings_for_function(&mut global_file_table, &function_coverage);
 
         if coverage_mapping_buffer.is_empty() {
             if function_coverage.is_used() {
@@ -87,19 +85,14 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
     }
 
     // Encode all filenames referenced by counters/expressions in this module
-    let filenames_buffer = llvm::build_byte_buffer(|filenames_buffer| {
-        coverageinfo::write_filenames_section_to_buffer(
-            mapgen.filenames.iter().map(Symbol::as_str),
-            filenames_buffer,
-        );
-    });
+    let filenames_buffer = global_file_table.into_filenames_buffer();
 
     let filenames_size = filenames_buffer.len();
     let filenames_val = cx.const_bytes(&filenames_buffer);
     let filenames_ref = coverageinfo::hash_bytes(&filenames_buffer);
 
     // Generate the LLVM IR representation of the coverage map and store it in a well-known global
-    let cov_data_val = mapgen.generate_coverage_map(cx, version, filenames_size, filenames_val);
+    let cov_data_val = generate_coverage_map(cx, version, filenames_size, filenames_val);
 
     let covfun_section_name = coverageinfo::covfun_section_name(cx);
     for (mangled_function_name, source_hash, is_used, coverage_mapping_buffer) in function_data {
@@ -118,13 +111,13 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
     coverageinfo::save_cov_data_to_mod(cx, cov_data_val);
 }
 
-struct CoverageMapGenerator {
-    filenames: FxIndexSet<Symbol>,
+struct GlobalFileTable {
+    global_file_table: FxIndexSet<Symbol>,
 }
 
-impl CoverageMapGenerator {
+impl GlobalFileTable {
     fn new(tcx: TyCtxt<'_>) -> Self {
-        let mut filenames = FxIndexSet::default();
+        let mut global_file_table = FxIndexSet::default();
         // LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
         // requires setting the first filename to the compilation directory.
         // Since rustc generates coverage maps with relative paths, the
@@ -133,94 +126,114 @@ impl CoverageMapGenerator {
         let working_dir = Symbol::intern(
             &tcx.sess.opts.working_dir.remapped_path_if_available().to_string_lossy(),
         );
-        filenames.insert(working_dir);
-        Self { filenames }
+        global_file_table.insert(working_dir);
+        Self { global_file_table }
     }
 
-    /// Using the `expressions` and `counter_regions` collected for the current function, generate
-    /// the `mapping_regions` and `virtual_file_mapping`, and capture any new filenames. Then use
-    /// LLVM APIs to encode the `virtual_file_mapping`, `expressions`, and `mapping_regions` into
-    /// the given `coverage_mapping` byte buffer, compliant with the LLVM Coverage Mapping format.
-    fn write_coverage_mapping<'a>(
-        &mut self,
-        expressions: Vec<CounterExpression>,
-        counter_regions: impl Iterator<Item = (Counter, &'a CodeRegion)>,
-        coverage_mapping_buffer: &RustString,
-    ) {
-        let mut counter_regions = counter_regions.collect::<Vec<_>>();
-        if counter_regions.is_empty() {
-            return;
-        }
+    fn global_file_id_for_file_name(&mut self, file_name: Symbol) -> u32 {
+        let (global_file_id, _) = self.global_file_table.insert_full(file_name);
+        global_file_id as u32
+    }
 
-        let mut virtual_file_mapping = Vec::new();
-        let mut mapping_regions = Vec::new();
-        let mut current_file_name = None;
-        let mut current_file_id = 0;
-
-        // Convert the list of (Counter, CodeRegion) pairs to an array of `CounterMappingRegion`, sorted
-        // by filename and position. Capture any new files to compute the `CounterMappingRegion`s
-        // `file_id` (indexing files referenced by the current function), and construct the
-        // function-specific `virtual_file_mapping` from `file_id` to its index in the module's
-        // `filenames` array.
-        counter_regions.sort_unstable_by_key(|(_counter, region)| *region);
-        for (counter, region) in counter_regions {
-            let CodeRegion { file_name, start_line, start_col, end_line, end_col } = *region;
-            let same_file = current_file_name.is_some_and(|p| p == file_name);
-            if !same_file {
-                if current_file_name.is_some() {
-                    current_file_id += 1;
-                }
-                current_file_name = Some(file_name);
-                debug!("  file_id: {} = '{:?}'", current_file_id, file_name);
-                let (filenames_index, _) = self.filenames.insert_full(file_name);
-                virtual_file_mapping.push(filenames_index as u32);
-            }
-            debug!("Adding counter {:?} to map for {:?}", counter, region);
+    fn into_filenames_buffer(self) -> Vec<u8> {
+        // This method takes `self` so that the caller can't accidentally
+        // modify the original file table after encoding it into a buffer.
+
+        llvm::build_byte_buffer(|buffer| {
+            coverageinfo::write_filenames_section_to_buffer(
+                self.global_file_table.iter().map(Symbol::as_str),
+                buffer,
+            );
+        })
+    }
+}
+
+/// Using the expressions and counter regions collected for a single function,
+/// generate the variable-sized payload of its corresponding `__llvm_covfun`
+/// entry. The payload is returned as a vector of bytes.
+///
+/// Newly-encountered filenames will be added to the global file table.
+fn encode_mappings_for_function(
+    global_file_table: &mut GlobalFileTable,
+    function_coverage: &FunctionCoverage<'_>,
+) -> Vec<u8> {
+    let (expressions, counter_regions) = function_coverage.get_expressions_and_counter_regions();
+
+    let mut counter_regions = counter_regions.collect::<Vec<_>>();
+    if counter_regions.is_empty() {
+        return Vec::new();
+    }
+
+    let mut virtual_file_mapping = IndexVec::<u32, u32>::new();
+    let mut mapping_regions = Vec::with_capacity(counter_regions.len());
+
+    // Sort the list of (counter, region) mapping pairs by region, so that they
+    // can be grouped by filename. Prepare file IDs for each filename, and
+    // prepare the mapping data so that we can pass it through FFI to LLVM.
+    counter_regions.sort_by_key(|(_counter, region)| *region);
+    for counter_regions_for_file in
+        counter_regions.group_by(|(_, a), (_, b)| a.file_name == b.file_name)
+    {
+        // Look up (or allocate) the global file ID for this filename.
+        let file_name = counter_regions_for_file[0].1.file_name;
+        let global_file_id = global_file_table.global_file_id_for_file_name(file_name);
+
+        // Associate that global file ID with a local file ID for this function.
+        let local_file_id: u32 = virtual_file_mapping.push(global_file_id);
+        debug!("  file id: local {local_file_id} => global {global_file_id} = '{file_name:?}'");
+
+        // For each counter/region pair in this function+file, convert it to a
+        // form suitable for FFI.
+        for &(counter, region) in counter_regions_for_file {
+            let CodeRegion { file_name: _, start_line, start_col, end_line, end_col } = *region;
+
+            debug!("Adding counter {counter:?} to map for {region:?}");
             mapping_regions.push(CounterMappingRegion::code_region(
                 counter,
-                current_file_id,
+                local_file_id,
                 start_line,
                 start_col,
                 end_line,
                 end_col,
             ));
         }
+    }
 
-        // Encode and append the current function's coverage mapping data
+    // Encode the function's coverage mappings into a buffer.
+    llvm::build_byte_buffer(|buffer| {
         coverageinfo::write_mapping_to_buffer(
-            virtual_file_mapping,
+            virtual_file_mapping.raw,
             expressions,
             mapping_regions,
-            coverage_mapping_buffer,
+            buffer,
         );
-    }
+    })
+}
 
-    /// Construct coverage map header and the array of function records, and combine them into the
-    /// coverage map. Save the coverage map data into the LLVM IR as a static global using a
-    /// specific, well-known section and name.
-    fn generate_coverage_map<'ll>(
-        self,
-        cx: &CodegenCx<'ll, '_>,
-        version: u32,
-        filenames_size: usize,
-        filenames_val: &'ll llvm::Value,
-    ) -> &'ll llvm::Value {
-        debug!("cov map: filenames_size = {}, 0-based version = {}", filenames_size, version);
-
-        // Create the coverage data header (Note, fields 0 and 2 are now always zero,
-        // as of `llvm::coverage::CovMapVersion::Version4`.)
-        let zero_was_n_records_val = cx.const_u32(0);
-        let filenames_size_val = cx.const_u32(filenames_size as u32);
-        let zero_was_coverage_size_val = cx.const_u32(0);
-        let version_val = cx.const_u32(version);
-        let cov_data_header_val = cx.const_struct(
-            &[zero_was_n_records_val, filenames_size_val, zero_was_coverage_size_val, version_val],
-            /*packed=*/ false,
-        );
+/// Construct coverage map header and the array of function records, and combine them into the
+/// coverage map. Save the coverage map data into the LLVM IR as a static global using a
+/// specific, well-known section and name.
+fn generate_coverage_map<'ll>(
+    cx: &CodegenCx<'ll, '_>,
+    version: u32,
+    filenames_size: usize,
+    filenames_val: &'ll llvm::Value,
+) -> &'ll llvm::Value {
+    debug!("cov map: filenames_size = {}, 0-based version = {}", filenames_size, version);
+
+    // Create the coverage data header (Note, fields 0 and 2 are now always zero,
+    // as of `llvm::coverage::CovMapVersion::Version4`.)
+    let zero_was_n_records_val = cx.const_u32(0);
+    let filenames_size_val = cx.const_u32(filenames_size as u32);
+    let zero_was_coverage_size_val = cx.const_u32(0);
+    let version_val = cx.const_u32(version);
+    let cov_data_header_val = cx.const_struct(
+        &[zero_was_n_records_val, filenames_size_val, zero_was_coverage_size_val, version_val],
+        /*packed=*/ false,
+    );
 
-        // Create the complete LLVM coverage data value to add to the LLVM IR
-        cx.const_struct(&[cov_data_header_val, filenames_val], /*packed=*/ false)
-    }
+    // Create the complete LLVM coverage data value to add to the LLVM IR
+    cx.const_struct(&[cov_data_header_val, filenames_val], /*packed=*/ false)
 }
 
 /// Construct a function record and combine it with the function's coverage mapping data.
@@ -317,10 +330,10 @@ fn add_unused_functions(cx: &CodegenCx<'_, '_>) {
     {
         let codegen_fn_attrs = tcx.codegen_fn_attrs(non_codegenned_def_id);
 
-        // If a function is marked `#[no_coverage]`, then skip generating a
+        // If a function is marked `#[coverage(off)]`, then skip generating a
         // dead code stub for it.
         if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_COVERAGE) {
-            debug!("skipping unused fn marked #[no_coverage]: {:?}", non_codegenned_def_id);
+            debug!("skipping unused fn marked #[coverage(off)]: {:?}", non_codegenned_def_id);
             continue;
         }
 
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs
index ebf4ee4164f..5ca2942ac4e 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs
@@ -20,7 +20,7 @@ pub fn compute_mir_scopes<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
     instance: Instance<'tcx>,
     mir: &Body<'tcx>,
-    debug_context: &mut FunctionDebugContext<&'ll DIScope, &'ll DILocation>,
+    debug_context: &mut FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>,
 ) {
     // Find all scopes with variables defined in them.
     let variables = if cx.sess().opts.debuginfo == DebugInfo::Full {
@@ -51,7 +51,7 @@ fn make_mir_scope<'ll, 'tcx>(
     instance: Instance<'tcx>,
     mir: &Body<'tcx>,
     variables: &Option<BitSet<SourceScope>>,
-    debug_context: &mut FunctionDebugContext<&'ll DIScope, &'ll DILocation>,
+    debug_context: &mut FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>,
     instantiated: &mut BitSet<SourceScope>,
     scope: SourceScope,
 ) {
@@ -86,7 +86,7 @@ fn make_mir_scope<'ll, 'tcx>(
     let loc = cx.lookup_debug_loc(scope_data.span.lo());
     let file_metadata = file_metadata(cx, &loc.file);
 
-    let dbg_scope = match scope_data.inlined {
+    let parent_dbg_scope = match scope_data.inlined {
         Some((callee, _)) => {
             // FIXME(eddyb) this would be `self.monomorphize(&callee)`
             // if this is moved to `rustc_codegen_ssa::mir::debuginfo`.
@@ -95,18 +95,22 @@ fn make_mir_scope<'ll, 'tcx>(
                 ty::ParamEnv::reveal_all(),
                 ty::EarlyBinder::bind(callee),
             );
-            let callee_fn_abi = cx.fn_abi_of_instance(callee, ty::List::empty());
-            cx.dbg_scope_fn(callee, callee_fn_abi, None)
+            debug_context.inlined_function_scopes.entry(callee).or_insert_with(|| {
+                let callee_fn_abi = cx.fn_abi_of_instance(callee, ty::List::empty());
+                cx.dbg_scope_fn(callee, callee_fn_abi, None)
+            })
         }
-        None => unsafe {
-            llvm::LLVMRustDIBuilderCreateLexicalBlock(
-                DIB(cx),
-                parent_scope.dbg_scope,
-                file_metadata,
-                loc.line,
-                loc.col,
-            )
-        },
+        None => parent_scope.dbg_scope,
+    };
+
+    let dbg_scope = unsafe {
+        llvm::LLVMRustDIBuilderCreateLexicalBlock(
+            DIB(cx),
+            parent_dbg_scope,
+            file_metadata,
+            loc.line,
+            loc.col,
+        )
     };
 
     let inlined_at = scope_data.inlined.map(|(_, callsite_span)| {
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
index 2301f66dc28..c862acdc7de 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
@@ -263,7 +263,7 @@ impl CodegenCx<'_, '_> {
     pub fn lookup_debug_loc(&self, pos: BytePos) -> DebugLoc {
         let (file, line, col) = match self.sess().source_map().lookup_line(pos) {
             Ok(SourceFileAndLine { sf: file, line }) => {
-                let line_pos = file.lines(|lines| lines[line]);
+                let line_pos = file.lines()[line];
 
                 // Use 1-based indexing.
                 let line = (line + 1) as u32;
@@ -292,7 +292,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
         llfn: &'ll Value,
         mir: &mir::Body<'tcx>,
-    ) -> Option<FunctionDebugContext<&'ll DIScope, &'ll DILocation>> {
+    ) -> Option<FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>> {
         if self.sess().opts.debuginfo == DebugInfo::None {
             return None;
         }
@@ -304,8 +304,10 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
             file_start_pos: BytePos(0),
             file_end_pos: BytePos(0),
         };
-        let mut fn_debug_context =
-            FunctionDebugContext { scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes) };
+        let mut fn_debug_context = FunctionDebugContext {
+            scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes),
+            inlined_function_scopes: Default::default(),
+        };
 
         // Fill in all the scopes, with the information from the MIR body.
         compute_mir_scopes(self, instance, mir, &mut fn_debug_context);
@@ -347,6 +349,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         type_names::push_generic_params(
             tcx,
             tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), args),
+            enclosing_fn_def_id,
             &mut name,
         );
 
diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs
index fced6d504d2..264c273ba30 100644
--- a/compiler/rustc_codegen_llvm/src/errors.rs
+++ b/compiler/rustc_codegen_llvm/src/errors.rs
@@ -226,3 +226,9 @@ pub(crate) struct WriteBytecode<'a> {
 pub(crate) struct CopyBitcode {
     pub err: std::io::Error,
 }
+
+#[derive(Diagnostic)]
+#[diag(codegen_llvm_unknown_debuginfo_compression)]
+pub struct UnknownCompression {
+    pub algorithm: &'static str,
+}
diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs
index d283299ac46..ac199624e34 100644
--- a/compiler/rustc_codegen_llvm/src/lib.rs
+++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -10,6 +10,7 @@
 #![feature(iter_intersperse)]
 #![feature(let_chains)]
 #![feature(never_type)]
+#![feature(slice_group_by)]
 #![feature(impl_trait_in_assoc_type)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index 01cbf7d3b11..2ebfdae39e8 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -2131,8 +2131,12 @@ extern "C" {
         RelaxELFRelocations: bool,
         UseInitArray: bool,
         SplitDwarfFile: *const c_char,
+        DebugInfoCompression: *const c_char,
         ForceEmulatedTls: bool,
+        ArgsCstrBuff: *const c_char,
+        ArgsCstrBuffLen: usize,
     ) -> Option<&'static mut TargetMachine>;
+
     pub fn LLVMRustDisposeTargetMachine(T: &'static mut TargetMachine);
     pub fn LLVMRustAddLibraryInfo<'a>(
         PM: &PassManager<'a>,
@@ -2319,6 +2323,12 @@ extern "C" {
         len: usize,
         out_len: &mut usize,
     ) -> *const u8;
+    pub fn LLVMRustGetSliceFromObjectDataByName(
+        data: *const u8,
+        len: usize,
+        name: *const u8,
+        out_len: &mut usize,
+    ) -> *const u8;
 
     pub fn LLVMRustLinkerNew(M: &Module) -> &mut Linker<'_>;
     pub fn LLVMRustLinkerAdd(
@@ -2357,6 +2367,10 @@ extern "C" {
 
     pub fn LLVMRustIsBitcode(ptr: *const u8, len: usize) -> bool;
 
+    pub fn LLVMRustLLVMHasZlibCompressionForDebugSymbols() -> bool;
+
+    pub fn LLVMRustLLVMHasZstdCompressionForDebugSymbols() -> bool;
+
     pub fn LLVMRustGetSymbols(
         buf_ptr: *const u8,
         buf_len: usize,
diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index 3dc83f50b21..9ce13ff469c 100644
--- a/compiler/rustc_codegen_ssa/messages.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
@@ -23,6 +23,8 @@ codegen_ssa_erroneous_constant = erroneous constant encountered
 
 codegen_ssa_error_creating_remark_dir = failed to create remark directory: {$error}
 
+codegen_ssa_expected_coverage_symbol = expected `coverage(off)` or `coverage(on)`
+
 codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)`
 
 codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
index 8fb2ccb7e8a..5b869bed0a0 100644
--- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
+++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
@@ -334,7 +334,7 @@ fn exported_symbols_provider_local(
 
             match *mono_item {
                 MonoItem::Fn(Instance { def: InstanceDef::Item(def), args }) => {
-                    if args.non_erasable_generics().next().is_some() {
+                    if args.non_erasable_generics(tcx, def).next().is_some() {
                         let symbol = ExportedSymbol::Generic(def, args);
                         symbols.push((
                             symbol,
@@ -346,10 +346,10 @@ fn exported_symbols_provider_local(
                         ));
                     }
                 }
-                MonoItem::Fn(Instance { def: InstanceDef::DropGlue(_, Some(ty)), args }) => {
+                MonoItem::Fn(Instance { def: InstanceDef::DropGlue(def_id, Some(ty)), args }) => {
                     // A little sanity-check
                     debug_assert_eq!(
-                        args.non_erasable_generics().next(),
+                        args.non_erasable_generics(tcx, def_id).next(),
                         Some(GenericArgKind::Type(ty))
                     );
                     symbols.push((
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index f485af00bca..3bf98c46dea 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -343,6 +343,12 @@ pub struct CodegenContext<B: WriteBackendMethods> {
     pub split_debuginfo: rustc_target::spec::SplitDebuginfo,
     pub split_dwarf_kind: rustc_session::config::SplitDwarfKind,
 
+    /// All commandline args used to invoke the compiler, with @file args fully expanded.
+    /// This will only be used within debug info, e.g. in the pdb file on windows
+    /// This is mainly useful for other tools that reads that debuginfo to figure out
+    /// how to call the compiler with the same arguments.
+    pub expanded_args: Vec<String>,
+
     /// Handler to use for diagnostics produced during codegen.
     pub diag_emitter: SharedEmitter,
     /// LLVM optimizations for which we want to print remarks.
@@ -1108,6 +1114,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
         incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()),
         cgu_reuse_tracker: sess.cgu_reuse_tracker.clone(),
         coordinator_send,
+        expanded_args: tcx.sess.expanded_args.clone(),
         diag_emitter: shared_emitter.clone(),
         output_filenames: tcx.output_filenames(()).clone(),
         regular_module_config: regular_config,
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index f6936c80b77..59efe4cd3cc 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -16,7 +16,10 @@ use rustc_target::spec::{abi, SanitizerSet};
 
 use crate::errors;
 use crate::target_features::from_target_feature;
-use crate::{errors::ExpectedUsedSymbol, target_features::check_target_feature_trait_unsafe};
+use crate::{
+    errors::{ExpectedCoverageSymbol, ExpectedUsedSymbol},
+    target_features::check_target_feature_trait_unsafe,
+};
 
 fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage {
     use rustc_middle::mir::mono::Linkage::*;
@@ -128,7 +131,21 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                         .emit();
                 }
             }
-            sym::no_coverage => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE,
+            sym::coverage => {
+                let inner = attr.meta_item_list();
+                match inner.as_deref() {
+                    Some([item]) if item.has_name(sym::off) => {
+                        codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE;
+                    }
+                    Some([item]) if item.has_name(sym::on) => {
+                        // Allow #[coverage(on)] for being explicit, maybe also in future to enable
+                        // coverage on a smaller scope within an excluded larger scope.
+                    }
+                    Some(_) | None => {
+                        tcx.sess.emit_err(ExpectedCoverageSymbol { span: attr.span });
+                    }
+                }
+            }
             sym::rustc_std_internal_symbol => {
                 codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
             }
diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
index 067c824aba0..e21148a4de1 100644
--- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
+++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
@@ -106,14 +106,14 @@ fn push_debuginfo_type_name<'tcx>(
                     ty_and_layout,
                     &|output, visited| {
                         push_item_name(tcx, def.did(), true, output);
-                        push_generic_params_internal(tcx, args, output, visited);
+                        push_generic_params_internal(tcx, args, def.did(), output, visited);
                     },
                     output,
                     visited,
                 );
             } else {
                 push_item_name(tcx, def.did(), qualified, output);
-                push_generic_params_internal(tcx, args, output, visited);
+                push_generic_params_internal(tcx, args, def.did(), output, visited);
             }
         }
         ty::Tuple(component_types) => {
@@ -237,8 +237,13 @@ fn push_debuginfo_type_name<'tcx>(
                 let principal =
                     tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), principal);
                 push_item_name(tcx, principal.def_id, qualified, output);
-                let principal_has_generic_params =
-                    push_generic_params_internal(tcx, principal.args, output, visited);
+                let principal_has_generic_params = push_generic_params_internal(
+                    tcx,
+                    principal.args,
+                    principal.def_id,
+                    output,
+                    visited,
+                );
 
                 let projection_bounds: SmallVec<[_; 4]> = trait_data
                     .projection_bounds()
@@ -516,7 +521,13 @@ pub fn compute_debuginfo_vtable_name<'tcx>(
             tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), trait_ref);
         push_item_name(tcx, trait_ref.def_id, true, &mut vtable_name);
         visited.clear();
-        push_generic_params_internal(tcx, trait_ref.args, &mut vtable_name, &mut visited);
+        push_generic_params_internal(
+            tcx,
+            trait_ref.args,
+            trait_ref.def_id,
+            &mut vtable_name,
+            &mut visited,
+        );
     } else {
         vtable_name.push('_');
     }
@@ -610,20 +621,20 @@ fn push_unqualified_item_name(
 fn push_generic_params_internal<'tcx>(
     tcx: TyCtxt<'tcx>,
     args: GenericArgsRef<'tcx>,
+    def_id: DefId,
     output: &mut String,
     visited: &mut FxHashSet<Ty<'tcx>>,
 ) -> bool {
-    if args.non_erasable_generics().next().is_none() {
+    debug_assert_eq!(args, tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), args));
+    let mut args = args.non_erasable_generics(tcx, def_id).peekable();
+    if args.peek().is_none() {
         return false;
     }
-
-    debug_assert_eq!(args, tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), args));
-
     let cpp_like_debuginfo = cpp_like_debuginfo(tcx);
 
     output.push('<');
 
-    for type_parameter in args.non_erasable_generics() {
+    for type_parameter in args {
         match type_parameter {
             GenericArgKind::Type(type_parameter) => {
                 push_debuginfo_type_name(tcx, type_parameter, true, output, visited);
@@ -670,10 +681,8 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S
                 // avoiding collisions and will make the emitted type names shorter.
                 let hash_short = tcx.with_stable_hashing_context(|mut hcx| {
                     let mut hasher = StableHasher::new();
-                    let ct = ct.eval(tcx, ty::ParamEnv::reveal_all());
-                    hcx.while_hashing_spans(false, |hcx| {
-                        ct.to_valtree().hash_stable(hcx, &mut hasher)
-                    });
+                    let ct = ct.eval(tcx, ty::ParamEnv::reveal_all(), None).unwrap();
+                    hcx.while_hashing_spans(false, |hcx| ct.hash_stable(hcx, &mut hasher));
                     hasher.finish::<Hash64>()
                 });
 
@@ -691,11 +700,12 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S
 pub fn push_generic_params<'tcx>(
     tcx: TyCtxt<'tcx>,
     args: GenericArgsRef<'tcx>,
+    def_id: DefId,
     output: &mut String,
 ) {
     let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name");
     let mut visited = FxHashSet::default();
-    push_generic_params_internal(tcx, args, output, &mut visited);
+    push_generic_params_internal(tcx, args, def_id, output, &mut visited);
 }
 
 fn push_closure_or_generator_name<'tcx>(
@@ -738,7 +748,7 @@ fn push_closure_or_generator_name<'tcx>(
     // Truncate the args to the length of the above generics. This will cut off
     // anything closure- or generator-specific.
     let args = args.truncate_to(tcx, generics);
-    push_generic_params_internal(tcx, args, output, visited);
+    push_generic_params_internal(tcx, args, enclosing_fn_def_id, output, visited);
 }
 
 fn push_close_angle_bracket(cpp_like_debuginfo: bool, output: &mut String) {
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index 41602321ded..fa49095c9e8 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -562,6 +562,13 @@ pub struct UnknownArchiveKind<'a> {
 }
 
 #[derive(Diagnostic)]
+#[diag(codegen_ssa_expected_coverage_symbol)]
+pub struct ExpectedCoverageSymbol {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(codegen_ssa_expected_used_symbol)]
 pub struct ExpectedUsedSymbol {
     #[primary_span]
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index d78a0c49107..d8f6b4ed836 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -928,21 +928,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         // we get a value of a built-in pointer type.
                         //
                         // This is also relevant for `Pin<&mut Self>`, where we need to peel the `Pin`.
-                        'descend_newtypes: while !op.layout.ty.is_unsafe_ptr()
-                            && !op.layout.ty.is_ref()
-                        {
-                            for i in 0..op.layout.fields.count() {
-                                let field = op.extract_field(bx, i);
-                                if !field.layout.is_1zst() {
-                                    // we found the one non-1-ZST field that is allowed
-                                    // now find *its* non-zero-sized field, or stop if it's a
-                                    // pointer
-                                    op = field;
-                                    continue 'descend_newtypes;
-                                }
-                            }
-
-                            span_bug!(span, "receiver has no non-zero-sized fields {:?}", op);
+                        while !op.layout.ty.is_unsafe_ptr() && !op.layout.ty.is_ref() {
+                            let (idx, _) = op.layout.non_1zst_field(bx).expect(
+                                "not exactly one non-1-ZST field in a `DispatchFromDyn` type",
+                            );
+                            op = op.extract_field(bx, idx);
                         }
 
                         // now that we have `*dyn Trait` or `&dyn Trait`, split it up into its
@@ -970,22 +960,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     }
                     Immediate(_) => {
                         // See comment above explaining why we peel these newtypes
-                        'descend_newtypes: while !op.layout.ty.is_unsafe_ptr()
-                            && !op.layout.ty.is_ref()
-                        {
-                            for i in 0..op.layout.fields.count() {
-                                let field = op.extract_field(bx, i);
-                                if !field.layout.is_1zst() {
-                                    // We found the one non-1-ZST field that is allowed. (The rules
-                                    // for `DispatchFromDyn` ensure there's exactly one such field.)
-                                    // Now find *its* non-zero-sized field, or stop if it's a
-                                    // pointer.
-                                    op = field;
-                                    continue 'descend_newtypes;
-                                }
-                            }
-
-                            span_bug!(span, "receiver has no non-zero-sized fields {:?}", op);
+                        while !op.layout.ty.is_unsafe_ptr() && !op.layout.ty.is_ref() {
+                            let (idx, _) = op.layout.non_1zst_field(bx).expect(
+                                "not exactly one non-1-ZST field in a `DispatchFromDyn` type",
+                            );
+                            op = op.extract_field(bx, idx);
                         }
 
                         // Make sure that we've actually unwrapped the rcvr down
diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs
index babcf9bee24..4d7bd60ceca 100644
--- a/compiler/rustc_codegen_ssa/src/mir/constant.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs
@@ -24,43 +24,31 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         &self,
         constant: &mir::Constant<'tcx>,
     ) -> Result<ConstValue<'tcx>, ErrorHandled> {
-        let ct = self.monomorphize(constant.literal);
-        let uv = match ct {
-            mir::ConstantKind::Ty(ct) => match ct.kind() {
-                ty::ConstKind::Unevaluated(uv) => uv.expand(),
-                ty::ConstKind::Value(val) => {
-                    return Ok(self.cx.tcx().valtree_to_const_val((ct.ty(), val)));
+        self.monomorphize(constant.literal)
+            .eval(self.cx.tcx(), ty::ParamEnv::reveal_all(), Some(constant.span))
+            .map_err(|err| {
+                match err {
+                    ErrorHandled::Reported(_) => {
+                        self.cx
+                            .tcx()
+                            .sess
+                            .emit_err(errors::ErroneousConstant { span: constant.span });
+                    }
+                    ErrorHandled::TooGeneric => {
+                        self.cx.tcx().sess.diagnostic().emit_bug(
+                            errors::PolymorphicConstantTooGeneric { span: constant.span },
+                        );
+                    }
                 }
-                err => span_bug!(
-                    constant.span,
-                    "encountered bad ConstKind after monomorphizing: {:?}",
-                    err
-                ),
-            },
-            mir::ConstantKind::Unevaluated(uv, _) => uv,
-            mir::ConstantKind::Val(val, _) => return Ok(val),
-        };
-
-        self.cx.tcx().const_eval_resolve(ty::ParamEnv::reveal_all(), uv, None).map_err(|err| {
-            match err {
-                ErrorHandled::Reported(_) => {
-                    self.cx.tcx().sess.emit_err(errors::ErroneousConstant { span: constant.span });
-                }
-                ErrorHandled::TooGeneric => {
-                    self.cx
-                        .tcx()
-                        .sess
-                        .diagnostic()
-                        .emit_bug(errors::PolymorphicConstantTooGeneric { span: constant.span });
-                }
-            }
-            err
-        })
+                err
+            })
     }
 
     /// This is a convenience helper for `simd_shuffle_indices`. It has the precondition
     /// that the given `constant` is an `ConstantKind::Unevaluated` and must be convertible to
     /// a `ValTree`. If you want a more general version of this, talk to `wg-const-eval` on zulip.
+    ///
+    /// Note that this function is cursed, since usually MIR consts should not be evaluated to valtrees!
     pub fn eval_unevaluated_mir_constant_to_valtree(
         &self,
         constant: &mir::Constant<'tcx>,
@@ -80,7 +68,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             // `simd_shuffle` call without wrapping the constant argument in a `const {}` block, but
             // the user pass through arbitrary expressions.
             // FIXME(oli-obk): replace the magic const generic argument of `simd_shuffle` with a real
-            // const generic.
+            // const generic, and get rid of this entire function.
             other => span_bug!(constant.span, "{other:#?}"),
         };
         let uv = self.monomorphize(uv);
diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
index 526c16a59de..ac705a5f35c 100644
--- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
@@ -1,10 +1,12 @@
 use crate::traits::*;
+use rustc_data_structures::fx::FxHashMap;
 use rustc_index::IndexVec;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::mir;
 use rustc_middle::ty;
 use rustc_middle::ty::layout::TyAndLayout;
 use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
+use rustc_middle::ty::Instance;
 use rustc_middle::ty::Ty;
 use rustc_session::config::DebugInfo;
 use rustc_span::symbol::{kw, Symbol};
@@ -17,10 +19,13 @@ use super::{FunctionCx, LocalRef};
 
 use std::ops::Range;
 
-pub struct FunctionDebugContext<S, L> {
+pub struct FunctionDebugContext<'tcx, S, L> {
+    /// Maps from source code to the corresponding debug info scope.
     pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
-}
 
+    /// Maps from an inlined function to its debug info declaration.
+    pub inlined_function_scopes: FxHashMap<Instance<'tcx>, S>,
+}
 #[derive(Copy, Clone)]
 pub enum VariableKind {
     ArgumentVariable(usize /*index*/),
@@ -484,54 +489,89 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 None
             };
 
-            let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| {
-                let (var_ty, var_kind) = match var.value {
+            let var_ty = if let Some(ref fragment) = var.composite {
+                self.monomorphize(fragment.ty)
+            } else {
+                match var.value {
                     mir::VarDebugInfoContents::Place(place) => {
-                        let var_ty = self.monomorphized_place_ty(place.as_ref());
-                        let var_kind = if let Some(arg_index) = var.argument_index
-                            && place.projection.is_empty()
-                        {
-                            let arg_index = arg_index as usize;
-                            if target_is_msvc {
-                                // ScalarPair parameters are spilled to the stack so they need to
-                                // be marked as a `LocalVariable` for MSVC debuggers to visualize
-                                // their data correctly. (See #81894 & #88625)
-                                let var_ty_layout = self.cx.layout_of(var_ty);
-                                if let Abi::ScalarPair(_, _) = var_ty_layout.abi {
-                                    VariableKind::LocalVariable
-                                } else {
-                                    VariableKind::ArgumentVariable(arg_index)
-                                }
-                            } else {
-                                // FIXME(eddyb) shouldn't `ArgumentVariable` indices be
-                                // offset in closures to account for the hidden environment?
-                                VariableKind::ArgumentVariable(arg_index)
-                            }
-                        } else {
-                            VariableKind::LocalVariable
-                        };
-                        (var_ty, var_kind)
-                    }
-                    mir::VarDebugInfoContents::Const(c) => {
-                        let ty = self.monomorphize(c.ty());
-                        (ty, VariableKind::LocalVariable)
+                        self.monomorphized_place_ty(place.as_ref())
                     }
-                    mir::VarDebugInfoContents::Composite { ty, fragments: _ } => {
-                        let ty = self.monomorphize(ty);
-                        (ty, VariableKind::LocalVariable)
+                    mir::VarDebugInfoContents::Const(c) => self.monomorphize(c.ty()),
+                }
+            };
+
+            let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| {
+                let var_kind = if let Some(arg_index) = var.argument_index
+                    && var.composite.is_none()
+                    && let mir::VarDebugInfoContents::Place(place) = var.value
+                    && place.projection.is_empty()
+                {
+                    let arg_index = arg_index as usize;
+                    if target_is_msvc {
+                        // ScalarPair parameters are spilled to the stack so they need to
+                        // be marked as a `LocalVariable` for MSVC debuggers to visualize
+                        // their data correctly. (See #81894 & #88625)
+                        let var_ty_layout = self.cx.layout_of(var_ty);
+                        if let Abi::ScalarPair(_, _) = var_ty_layout.abi {
+                            VariableKind::LocalVariable
+                        } else {
+                            VariableKind::ArgumentVariable(arg_index)
+                        }
+                    } else {
+                        // FIXME(eddyb) shouldn't `ArgumentVariable` indices be
+                        // offset in closures to account for the hidden environment?
+                        VariableKind::ArgumentVariable(arg_index)
                     }
+                } else {
+                    VariableKind::LocalVariable
                 };
 
                 self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
             });
 
+            let fragment = if let Some(ref fragment) = var.composite {
+                let var_layout = self.cx.layout_of(var_ty);
+
+                let mut fragment_start = Size::ZERO;
+                let mut fragment_layout = var_layout;
+
+                for elem in &fragment.projection {
+                    match *elem {
+                        mir::ProjectionElem::Field(field, _) => {
+                            let i = field.index();
+                            fragment_start += fragment_layout.fields.offset(i);
+                            fragment_layout = fragment_layout.field(self.cx, i);
+                        }
+                        _ => span_bug!(
+                            var.source_info.span,
+                            "unsupported fragment projection `{:?}`",
+                            elem,
+                        ),
+                    }
+                }
+
+                if fragment_layout.size == Size::ZERO {
+                    // Fragment is a ZST, so does not represent anything. Avoid generating anything
+                    // as this may conflict with a fragment that covers the entire variable.
+                    continue;
+                } else if fragment_layout.size == var_layout.size {
+                    // Fragment covers entire variable, so as far as
+                    // DWARF is concerned, it's not really a fragment.
+                    None
+                } else {
+                    Some(fragment_start..fragment_start + fragment_layout.size)
+                }
+            } else {
+                None
+            };
+
             match var.value {
                 mir::VarDebugInfoContents::Place(place) => {
                     per_local[place.local].push(PerLocalVarDebugInfo {
                         name: var.name,
                         source_info: var.source_info,
                         dbg_var,
-                        fragment: None,
+                        fragment,
                         projection: place.projection,
                     });
                 }
@@ -547,51 +587,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                                 bx,
                             );
 
-                            bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[], None);
-                        }
-                    }
-                }
-                mir::VarDebugInfoContents::Composite { ty, ref fragments } => {
-                    let var_ty = self.monomorphize(ty);
-                    let var_layout = self.cx.layout_of(var_ty);
-                    for fragment in fragments {
-                        let mut fragment_start = Size::ZERO;
-                        let mut fragment_layout = var_layout;
-
-                        for elem in &fragment.projection {
-                            match *elem {
-                                mir::ProjectionElem::Field(field, _) => {
-                                    let i = field.index();
-                                    fragment_start += fragment_layout.fields.offset(i);
-                                    fragment_layout = fragment_layout.field(self.cx, i);
-                                }
-                                _ => span_bug!(
-                                    var.source_info.span,
-                                    "unsupported fragment projection `{:?}`",
-                                    elem,
-                                ),
-                            }
+                            bx.dbg_var_addr(
+                                dbg_var,
+                                dbg_loc,
+                                base.llval,
+                                Size::ZERO,
+                                &[],
+                                fragment,
+                            );
                         }
-
-                        let place = fragment.contents;
-                        let fragment = if fragment_layout.size == Size::ZERO {
-                            // Fragment is a ZST, so does not represent anything.
-                            continue;
-                        } else if fragment_layout.size == var_layout.size {
-                            // Fragment covers entire variable, so as far as
-                            // DWARF is concerned, it's not really a fragment.
-                            None
-                        } else {
-                            Some(fragment_start..fragment_start + fragment_layout.size)
-                        };
-
-                        per_local[place.local].push(PerLocalVarDebugInfo {
-                            name: var.name,
-                            source_info: var.source_info,
-                            dbg_var,
-                            fragment,
-                            projection: place.projection,
-                        });
                     }
                 }
             }
diff --git a/compiler/rustc_codegen_ssa/src/mir/locals.rs b/compiler/rustc_codegen_ssa/src/mir/locals.rs
index da8bf5e7916..378c5401322 100644
--- a/compiler/rustc_codegen_ssa/src/mir/locals.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/locals.rs
@@ -7,7 +7,6 @@ use rustc_index::IndexVec;
 use rustc_middle::mir;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use std::ops::{Index, IndexMut};
-
 pub(super) struct Locals<'tcx, V> {
     values: IndexVec<mir::Local, LocalRef<'tcx, V>>,
 }
@@ -36,17 +35,18 @@ impl<'tcx, V> Locals<'tcx, V> {
 impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     pub(super) fn initialize_locals(&mut self, values: Vec<LocalRef<'tcx, Bx::Value>>) {
         assert!(self.locals.values.is_empty());
-
+        // FIXME(#115215): After #115025 get's merged this might not be necessary
         for (local, value) in values.into_iter().enumerate() {
             match value {
                 LocalRef::Place(_) | LocalRef::UnsizedPlace(_) | LocalRef::PendingOperand => (),
                 LocalRef::Operand(op) => {
                     let local = mir::Local::from_usize(local);
                     let expected_ty = self.monomorphize(self.mir.local_decls[local].ty);
-                    assert_eq!(expected_ty, op.layout.ty, "unexpected initial operand type");
+                    if expected_ty != op.layout.ty {
+                        warn!("Unexpected initial operand type. See the issues/114858");
+                    }
                 }
             }
-
             self.locals.values.push(value);
         }
     }
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index bf937c2fc7c..c4408f2db4c 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -46,7 +46,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
 
     mir: &'tcx mir::Body<'tcx>,
 
-    debug_context: Option<FunctionDebugContext<Bx::DIScope, Bx::DILocation>>,
+    debug_context: Option<FunctionDebugContext<'tcx, Bx::DIScope, Bx::DILocation>>,
 
     llfn: Bx::Function,
 
diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs
index ef66aa6ce1c..1926bb8df52 100644
--- a/compiler/rustc_codegen_ssa/src/mir/operand.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs
@@ -105,7 +105,10 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
                     bug!("from_const: invalid ScalarPair layout: {:#?}", layout);
                 };
                 let a = Scalar::from_pointer(
-                    Pointer::new(bx.tcx().create_memory_alloc(data), Size::from_bytes(start)),
+                    Pointer::new(
+                        bx.tcx().reserve_and_set_memory_alloc(data),
+                        Size::from_bytes(start),
+                    ),
                     &bx.tcx(),
                 );
                 let a_llval = bx.scalar_to_backend(
@@ -116,7 +119,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
                 let b_llval = bx.const_usize((end - start) as u64);
                 OperandValue::Pair(a_llval, b_llval)
             }
-            ConstValue::ByRef { alloc, offset } => {
+            ConstValue::Indirect { alloc_id, offset } => {
+                let alloc = bx.tcx().global_alloc(alloc_id).unwrap_memory();
                 return Self::from_const_alloc(bx, layout, alloc, offset);
             }
         };
@@ -182,6 +186,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
             _ if layout.is_zst() => OperandRef::zero_sized(layout),
             _ => {
                 // Neither a scalar nor scalar pair. Load from a place
+                // FIXME: should we cache `const_data_from_alloc` to avoid repeating this for the
+                // same `ConstAllocation`?
                 let init = bx.const_data_from_alloc(alloc);
                 let base_addr = bx.static_addr_of(init, alloc_align, None);
 
diff --git a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs
index 63fecaf34fd..4acc0ea076c 100644
--- a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs
@@ -26,7 +26,7 @@ pub trait DebugInfoMethods<'tcx>: BackendTypes {
         fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
         llfn: Self::Function,
         mir: &mir::Body<'tcx>,
-    ) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>>;
+    ) -> Option<FunctionDebugContext<'tcx, Self::DIScope, Self::DILocation>>;
 
     // FIXME(eddyb) find a common convention for all of the debuginfo-related
     // names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.).
diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs
index f146b93ff0c..2d20d553ef7 100644
--- a/compiler/rustc_const_eval/src/const_eval/error.rs
+++ b/compiler/rustc_const_eval/src/const_eval/error.rs
@@ -4,7 +4,6 @@ use rustc_errors::{DiagnosticArgValue, DiagnosticMessage, IntoDiagnostic, IntoDi
 use rustc_middle::mir::AssertKind;
 use rustc_middle::ty::TyCtxt;
 use rustc_middle::ty::{layout::LayoutError, ConstInt};
-use rustc_span::source_map::Spanned;
 use rustc_span::{ErrorGuaranteed, Span, Symbol};
 
 use super::InterpCx;
@@ -132,7 +131,8 @@ where
 {
     // Special handling for certain errors
     match error {
-        // Don't emit a new diagnostic for these errors
+        // Don't emit a new diagnostic for these errors, they are already reported elsewhere or
+        // should remain silent.
         err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
             ErrorHandled::TooGeneric
         }
@@ -140,27 +140,8 @@ where
         err_inval!(Layout(LayoutError::ReferencesError(guar))) => {
             ErrorHandled::Reported(guar.into())
         }
-        err_inval!(Layout(layout_error @ LayoutError::SizeOverflow(_))) => {
-            // We must *always* hard error on these, even if the caller wants just a lint.
-            // The `message` makes little sense here, this is a more serious error than the
-            // caller thinks anyway.
-            // See <https://github.com/rust-lang/rust/pull/63152>.
-            let (our_span, frames) = get_span_and_frames();
-            let span = span.unwrap_or(our_span);
-            let mut err =
-                tcx.sess.create_err(Spanned { span, node: layout_error.into_diagnostic() });
-            err.code(rustc_errors::error_code!(E0080));
-            let Some((mut err, handler)) = err.into_diagnostic() else {
-                panic!("did not emit diag");
-            };
-            for frame in frames {
-                err.eager_subdiagnostic(handler, frame);
-            }
-
-            ErrorHandled::Reported(handler.emit_diagnostic(&mut err).unwrap().into())
-        }
+        // Report remaining errors.
         _ => {
-            // Report as hard error.
             let (our_span, frames) = get_span_and_frames();
             let span = span.unwrap_or(our_span);
             let err = mk(span, frames);
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index 9df3e4030ff..454baf2a745 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -18,9 +18,9 @@ use super::{CanAccessStatics, CompileTimeEvalContext, CompileTimeInterpreter};
 use crate::errors;
 use crate::interpret::eval_nullary_intrinsic;
 use crate::interpret::{
-    intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId,
-    Immediate, InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy,
-    RefTracking, StackPopCleanup,
+    intern_const_alloc_recursive, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId, Immediate,
+    InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking,
+    StackPopCleanup,
 };
 
 // Returns a pointer to where the result lives
@@ -105,91 +105,68 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>(
     )
 }
 
-/// This function converts an interpreter value into a constant that is meant for use in the
-/// type system.
+/// This function converts an interpreter value into a MIR constant.
 #[instrument(skip(ecx), level = "debug")]
 pub(super) fn op_to_const<'tcx>(
     ecx: &CompileTimeEvalContext<'_, 'tcx>,
     op: &OpTy<'tcx>,
 ) -> ConstValue<'tcx> {
-    // We do not have value optimizations for everything.
-    // Only scalars and slices, since they are very common.
-    // Note that further down we turn scalars of uninitialized bits back to `ByRef`. These can result
-    // from scalar unions that are initialized with one of their zero sized variants. We could
-    // instead allow `ConstValue::Scalar` to store `ScalarMaybeUninit`, but that would affect all
-    // the usual cases of extracting e.g. a `usize`, without there being a real use case for the
-    // `Undef` situation.
-    let try_as_immediate = match op.layout.abi {
+    // Handle ZST consistently and early.
+    if op.layout.is_zst() {
+        return ConstValue::ZeroSized;
+    }
+
+    // All scalar types should be stored as `ConstValue::Scalar`. This is needed to make
+    // `ConstValue::try_to_scalar` efficient; we want that to work for *all* constants of scalar
+    // type (it's used throughout the compiler and having it work just on literals is not enough)
+    // and we want it to be fast (i.e., don't go to an `Allocation` and reconstruct the `Scalar`
+    // from its byte-serialized form).
+    let force_as_immediate = match op.layout.abi {
         Abi::Scalar(abi::Scalar::Initialized { .. }) => true,
-        Abi::ScalarPair(..) => match op.layout.ty.kind() {
-            ty::Ref(_, inner, _) => match *inner.kind() {
-                ty::Slice(elem) => elem == ecx.tcx.types.u8,
-                ty::Str => true,
-                _ => false,
-            },
-            _ => false,
-        },
+        // We don't *force* `ConstValue::Slice` for `ScalarPair`. This has the advantage that if the
+        // input `op` is a place, then turning it into a `ConstValue` and back into a `OpTy` will
+        // not have to generate any duplicate allocations (we preserve the original `AllocId` in
+        // `ConstValue::Indirect`). It means accessing the contents of a slice can be slow (since
+        // they can be stored as `ConstValue::Indirect`), but that's not relevant since we barely
+        // ever have to do this. (`try_get_slice_bytes_for_diagnostics` exists to provide this
+        // functionality.)
         _ => false,
     };
-    let immediate = if try_as_immediate {
+    let immediate = if force_as_immediate {
         Right(ecx.read_immediate(op).expect("normalization works on validated constants"))
     } else {
-        // It is guaranteed that any non-slice scalar pair is actually ByRef here.
-        // When we come back from raw const eval, we are always by-ref. The only way our op here is
-        // by-val is if we are in destructure_mir_constant, i.e., if this is (a field of) something that we
-        // "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or
-        // structs containing such.
         op.as_mplace_or_imm()
     };
 
     debug!(?immediate);
 
-    // We know `offset` is relative to the allocation, so we can use `into_parts`.
-    let to_const_value = |mplace: &MPlaceTy<'_>| {
-        debug!("to_const_value(mplace: {:?})", mplace);
-        match mplace.ptr().into_parts() {
-            (Some(alloc_id), offset) => {
-                let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory();
-                ConstValue::ByRef { alloc, offset }
-            }
-            (None, offset) => {
-                assert!(mplace.layout.is_zst());
-                assert_eq!(
-                    offset.bytes() % mplace.layout.align.abi.bytes(),
-                    0,
-                    "this MPlaceTy must come from a validated constant, thus we can assume the \
-                alignment is correct",
-                );
-                ConstValue::ZeroSized
-            }
-        }
-    };
     match immediate {
-        Left(ref mplace) => to_const_value(mplace),
-        // see comment on `let try_as_immediate` above
+        Left(ref mplace) => {
+            // We know `offset` is relative to the allocation, so we can use `into_parts`.
+            let (alloc_id, offset) = mplace.ptr().into_parts();
+            let alloc_id = alloc_id.expect("cannot have `fake` place fot non-ZST type");
+            ConstValue::Indirect { alloc_id, offset }
+        }
+        // see comment on `let force_as_immediate` above
         Right(imm) => match *imm {
-            _ if imm.layout.is_zst() => ConstValue::ZeroSized,
             Immediate::Scalar(x) => ConstValue::Scalar(x),
             Immediate::ScalarPair(a, b) => {
                 debug!("ScalarPair(a: {:?}, b: {:?})", a, b);
+                // FIXME: assert that this has an appropriate type.
+                // Currently we actually get here for non-[u8] slices during valtree construction!
+                let msg = "`op_to_const` on an immediate scalar pair must only be used on slice references to actually allocated memory";
                 // We know `offset` is relative to the allocation, so we can use `into_parts`.
-                let (data, start) = match a.to_pointer(ecx).unwrap().into_parts() {
-                    (Some(alloc_id), offset) => {
-                        (ecx.tcx.global_alloc(alloc_id).unwrap_memory(), offset.bytes())
-                    }
-                    (None, _offset) => (
-                        ecx.tcx.mk_const_alloc(Allocation::from_bytes_byte_aligned_immutable(
-                            b"" as &[u8],
-                        )),
-                        0,
-                    ),
-                };
-                let len = b.to_target_usize(ecx).unwrap();
-                let start = start.try_into().unwrap();
+                // We use `ConstValue::Slice` so that we don't have to generate an allocation for
+                // `ConstValue::Indirect` here.
+                let (alloc_id, offset) = a.to_pointer(ecx).expect(msg).into_parts();
+                let alloc_id = alloc_id.expect(msg);
+                let data = ecx.tcx.global_alloc(alloc_id).unwrap_memory();
+                let start = offset.bytes_usize();
+                let len = b.to_target_usize(ecx).expect(msg);
                 let len: usize = len.try_into().unwrap();
                 ConstValue::Slice { data, start, end: start + len }
             }
-            Immediate::Uninit => to_const_value(&op.assert_mem_place()),
+            Immediate::Uninit => bug!("`Uninit` is not a valid value for {}", op.layout.ty),
         },
     }
 }
@@ -372,7 +349,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
             };
             let alloc_id = mplace.ptr().provenance.unwrap();
 
-            // Validation failed, report an error. This is always a hard error.
+            // Validation failed, report an error.
             if let Err(error) = validation {
                 let (error, backtrace) = error.into_parts();
                 backtrace.print_backtrace();
diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
index 572d7f9ac2f..1675b824c52 100644
--- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs
+++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
@@ -7,7 +7,7 @@ use crate::interpret::{
     intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta,
     MemoryKind, PlaceTy, Projectable, Scalar,
 };
-use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
+use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
 use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
 use rustc_span::source_map::DUMMY_SP;
 use rustc_target::abi::VariantIdx;
@@ -189,12 +189,11 @@ fn reconstruct_place_meta<'tcx>(
 }
 
 #[instrument(skip(ecx), level = "debug", ret)]
-fn create_pointee_place<'tcx>(
+fn create_valtree_place<'tcx>(
     ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>,
-    ty: Ty<'tcx>,
+    layout: TyAndLayout<'tcx>,
     valtree: ty::ValTree<'tcx>,
 ) -> MPlaceTy<'tcx> {
-    let layout = ecx.layout_of(ty).unwrap();
     let meta = reconstruct_place_meta(layout, valtree, ecx.tcx.tcx);
     ecx.allocate_dyn(layout, MemoryKind::Stack, meta).unwrap()
 }
@@ -216,11 +215,6 @@ pub fn valtree_to_const_value<'tcx>(
     // FIXME Does this need an example?
 
     let (param_env, ty) = param_env_ty.into_parts();
-    let mut ecx: crate::interpret::InterpCx<
-        '_,
-        '_,
-        crate::const_eval::CompileTimeInterpreter<'_, '_>,
-    > = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
 
     match ty.kind() {
         ty::FnDef(..) => {
@@ -233,33 +227,44 @@ pub fn valtree_to_const_value<'tcx>(
                 "ValTrees for Bool, Int, Uint, Float or Char should have the form ValTree::Leaf"
             ),
         },
-        ty::Ref(_, _, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Adt(..) => {
-            let place = match ty.kind() {
-                ty::Ref(_, inner_ty, _) => {
-                    // Need to create a place for the pointee (the reference itself will be an immediate)
-                    create_pointee_place(&mut ecx, *inner_ty, valtree)
-                }
-                _ => {
-                    // Need to create a place for this valtree.
-                    create_pointee_place(&mut ecx, ty, valtree)
+        ty::Ref(_, inner_ty, _) => {
+            let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
+            let imm = valtree_to_ref(&mut ecx, valtree, *inner_ty);
+            let imm = ImmTy::from_immediate(imm, tcx.layout_of(param_env_ty).unwrap());
+            op_to_const(&ecx, &imm.into())
+        }
+        ty::Tuple(_) | ty::Array(_, _) | ty::Adt(..) => {
+            let layout = tcx.layout_of(param_env_ty).unwrap();
+            if layout.is_zst() {
+                // Fast path to avoid some allocations.
+                return ConstValue::ZeroSized;
+            }
+            if layout.abi.is_scalar()
+                && (matches!(ty.kind(), ty::Tuple(_))
+                    || matches!(ty.kind(), ty::Adt(def, _) if def.is_struct()))
+            {
+                // A Scalar tuple/struct; we can avoid creating an allocation.
+                let branches = valtree.unwrap_branch();
+                // Find the non-ZST field. (There can be aligned ZST!)
+                for (i, &inner_valtree) in branches.iter().enumerate() {
+                    let field = layout.field(&LayoutCx { tcx, param_env }, i);
+                    if !field.is_zst() {
+                        return valtree_to_const_value(tcx, param_env.and(field.ty), inner_valtree);
+                    }
                 }
-            };
-            debug!(?place);
+                bug!("could not find non-ZST field during in {layout:#?}");
+            }
+
+            let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
+
+            // Need to create a place for this valtree.
+            let place = create_valtree_place(&mut ecx, layout, valtree);
 
             valtree_into_mplace(&mut ecx, &place, valtree);
             dump_place(&ecx, &place);
             intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap();
 
-            match ty.kind() {
-                ty::Ref(_, _, _) => {
-                    let ref_place = place.to_ref(&tcx);
-                    let imm =
-                        ImmTy::from_immediate(ref_place, tcx.layout_of(param_env_ty).unwrap());
-
-                    op_to_const(&ecx, &imm.into())
-                }
-                _ => op_to_const(&ecx, &place.into()),
-            }
+            op_to_const(&ecx, &place.into())
         }
         ty::Never
         | ty::Error(_)
@@ -283,6 +288,22 @@ pub fn valtree_to_const_value<'tcx>(
     }
 }
 
+/// Put a valtree into memory and return a reference to that.
+fn valtree_to_ref<'tcx>(
+    ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>,
+    valtree: ty::ValTree<'tcx>,
+    pointee_ty: Ty<'tcx>,
+) -> Immediate {
+    let pointee_place = create_valtree_place(ecx, ecx.layout_of(pointee_ty).unwrap(), valtree);
+    debug!(?pointee_place);
+
+    valtree_into_mplace(ecx, &pointee_place, valtree);
+    dump_place(ecx, &pointee_place);
+    intern_const_alloc_recursive(ecx, InternKind::Constant, &pointee_place).unwrap();
+
+    pointee_place.to_ref(&ecx.tcx)
+}
+
 #[instrument(skip(ecx), level = "debug")]
 fn valtree_into_mplace<'tcx>(
     ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>,
@@ -292,7 +313,6 @@ fn valtree_into_mplace<'tcx>(
     // This will match on valtree and write the value(s) corresponding to the ValTree
     // inside the place recursively.
 
-    let tcx = ecx.tcx.tcx;
     let ty = place.layout.ty;
 
     match ty.kind() {
@@ -305,27 +325,8 @@ fn valtree_into_mplace<'tcx>(
             ecx.write_immediate(Immediate::Scalar(scalar_int.into()), place).unwrap();
         }
         ty::Ref(_, inner_ty, _) => {
-            let pointee_place = create_pointee_place(ecx, *inner_ty, valtree);
-            debug!(?pointee_place);
-
-            valtree_into_mplace(ecx, &pointee_place, valtree);
-            dump_place(ecx, &pointee_place);
-            intern_const_alloc_recursive(ecx, InternKind::Constant, &pointee_place).unwrap();
-
-            let imm = match inner_ty.kind() {
-                ty::Slice(_) | ty::Str => {
-                    let len = valtree.unwrap_branch().len();
-                    let len_scalar = Scalar::from_target_usize(len as u64, &tcx);
-
-                    Immediate::ScalarPair(
-                        Scalar::from_maybe_pointer(pointee_place.ptr(), &tcx),
-                        len_scalar,
-                    )
-                }
-                _ => pointee_place.to_ref(&tcx),
-            };
+            let imm = valtree_to_ref(ecx, valtree, *inner_ty);
             debug!(?imm);
-
             ecx.write_immediate(imm, place).unwrap();
         }
         ty::Adt(_, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Str | ty::Slice(_) => {
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index c74fed0e47f..c3c6cbe3991 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -482,6 +482,9 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
         use UndefinedBehaviorInfo::*;
         match self {
             Ub(msg) => msg.clone().into(),
+            Custom(x) => (x.msg)(),
+            ValidationError(e) => e.diagnostic_message(),
+
             Unreachable => const_eval_unreachable,
             BoundsCheckFailed { .. } => const_eval_bounds_check_failed,
             DivisionByZero => const_eval_division_by_zero,
@@ -513,8 +516,8 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
             ScalarSizeMismatch(_) => const_eval_scalar_size_mismatch,
             UninhabitedEnumVariantWritten(_) => const_eval_uninhabited_enum_variant_written,
             UninhabitedEnumVariantRead(_) => const_eval_uninhabited_enum_variant_read,
-            ValidationError(e) => e.diagnostic_message(),
-            Custom(x) => (x.msg)(),
+            AbiMismatchArgument { .. } => const_eval_incompatible_types,
+            AbiMismatchReturn { .. } => const_eval_incompatible_return_types,
         }
     }
 
@@ -525,8 +528,15 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
     ) {
         use UndefinedBehaviorInfo::*;
         match self {
-            Ub(_)
-            | Unreachable
+            Ub(_) => {}
+            Custom(custom) => {
+                (custom.add_args)(&mut |name, value| {
+                    builder.set_arg(name, value);
+                });
+            }
+            ValidationError(e) => e.add_args(handler, builder),
+
+            Unreachable
             | DivisionByZero
             | RemainderByZero
             | DivisionOverflow
@@ -593,11 +603,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
                 builder.set_arg("target_size", info.target_size);
                 builder.set_arg("data_size", info.data_size);
             }
-            ValidationError(e) => e.add_args(handler, builder),
-            Custom(custom) => {
-                (custom.add_args)(&mut |name, value| {
-                    builder.set_arg(name, value);
-                });
+            AbiMismatchArgument { caller_ty, callee_ty }
+            | AbiMismatchReturn { caller_ty, callee_ty } => {
+                builder.set_arg("caller_ty", caller_ty.to_string());
+                builder.set_arg("callee_ty", callee_ty.to_string());
             }
         }
     }
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index 25c74b98611..4c826239eca 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -84,7 +84,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                         )
                         .ok_or_else(|| err_inval!(TooGeneric))?;
 
-                        let fn_ptr = self.create_fn_alloc_ptr(FnVal::Instance(instance));
+                        let fn_ptr = self.fn_ptr(FnVal::Instance(instance));
                         self.write_pointer(fn_ptr, dest)?;
                     }
                     _ => span_bug!(self.cur_span(), "reify fn pointer on {:?}", src.layout.ty),
@@ -116,7 +116,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                             ty::ClosureKind::FnOnce,
                         )
                         .ok_or_else(|| err_inval!(TooGeneric))?;
-                        let fn_ptr = self.create_fn_alloc_ptr(FnVal::Instance(instance));
+                        let fn_ptr = self.fn_ptr(FnVal::Instance(instance));
                         self.write_pointer(fn_ptr, dest)?;
                     }
                     _ => span_bug!(self.cur_span(), "closure fn pointer on {:?}", src.layout.ty),
diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs
index 6c35fb01a93..440ee06e7fe 100644
--- a/compiler/rustc_const_eval/src/interpret/discriminant.rs
+++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs
@@ -247,9 +247,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         &self,
         layout: TyAndLayout<'tcx>,
         variant: VariantIdx,
-    ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
+    ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
         let discr_layout = self.layout_of(layout.ty.discriminant_ty(*self.tcx))?;
-        Ok(match layout.ty.discriminant_for_variant(*self.tcx, variant) {
+        let discr_value = match layout.ty.discriminant_for_variant(*self.tcx, variant) {
             Some(discr) => {
                 // This type actually has discriminants.
                 assert_eq!(discr.ty, discr_layout.ty);
@@ -260,6 +260,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 assert_eq!(variant.as_u32(), 0);
                 Scalar::from_uint(variant.as_u32(), discr_layout.size)
             }
-        })
+        };
+        Ok(ImmTy::from_scalar(discr_value, discr_layout))
     }
 }
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index db1eaf58621..966ce66c7ad 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -749,10 +749,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         self.stack_mut().push(frame);
 
         // Make sure all the constants required by this frame evaluate successfully (post-monomorphization check).
-        for ct in &body.required_consts {
-            let span = ct.span;
-            let ct = self.subst_from_current_frame_and_normalize_erasing_regions(ct.literal)?;
-            self.eval_mir_constant(&ct, Some(span), None)?;
+        if M::POST_MONO_CHECKS {
+            for ct in &body.required_consts {
+                let span = ct.span;
+                let ct = self.subst_from_current_frame_and_normalize_erasing_regions(ct.literal)?;
+                self.eval_mir_constant(&ct, Some(span), None)?;
+            }
         }
 
         // done
diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs
index 562f7c610fd..8c0009cfdfd 100644
--- a/compiler/rustc_const_eval/src/interpret/intern.rs
+++ b/compiler/rustc_const_eval/src/interpret/intern.rs
@@ -24,8 +24,8 @@ use rustc_middle::ty::{self, layout::TyAndLayout, Ty};
 use rustc_ast::Mutability;
 
 use super::{
-    AllocId, Allocation, ConstAllocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy,
-    Projectable, ValueVisitor,
+    AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, Projectable,
+    ValueVisitor,
 };
 use crate::const_eval;
 use crate::errors::{DanglingPtrInFinal, UnsupportedUntypedPointer};
@@ -455,7 +455,7 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>>
 {
     /// A helper function that allocates memory for the layout given and gives you access to mutate
     /// it. Once your own mutation code is done, the backing `Allocation` is removed from the
-    /// current `Memory` and returned.
+    /// current `Memory` and interned as read-only into the global memory.
     pub fn intern_with_temp_alloc(
         &mut self,
         layout: TyAndLayout<'tcx>,
@@ -463,11 +463,15 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>>
             &mut InterpCx<'mir, 'tcx, M>,
             &PlaceTy<'tcx, M::Provenance>,
         ) -> InterpResult<'tcx, ()>,
-    ) -> InterpResult<'tcx, ConstAllocation<'tcx>> {
+    ) -> InterpResult<'tcx, AllocId> {
+        // `allocate` picks a fresh AllocId that we will associate with its data below.
         let dest = self.allocate(layout, MemoryKind::Stack)?;
         f(self, &dest.clone().into())?;
         let mut alloc = self.memory.alloc_map.remove(&dest.ptr().provenance.unwrap()).unwrap().1;
         alloc.mutability = Mutability::Not;
-        Ok(self.tcx.mk_const_alloc(alloc))
+        let alloc = self.tcx.mk_const_alloc(alloc);
+        let alloc_id = dest.ptr().provenance.unwrap(); // this was just allocated, it must have provenance
+        self.tcx.set_alloc_id_memory(alloc_id, alloc);
+        Ok(alloc_id)
     }
 }
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 7feed74ceae..3b58f66353b 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -222,7 +222,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let place = self.deref_pointer(&args[0])?;
                 let variant = self.read_discriminant(&place)?;
                 let discr = self.discriminant_for_variant(place.layout, variant)?;
-                self.write_scalar(discr, dest)?;
+                self.write_immediate(*discr, dest)?;
             }
             sym::exact_div => {
                 let l = self.read_immediate(&args[0])?;
diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs
index 9fda6b037c8..c9fd2102418 100644
--- a/compiler/rustc_const_eval/src/interpret/machine.rs
+++ b/compiler/rustc_const_eval/src/interpret/machine.rs
@@ -130,6 +130,9 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
     /// Should the machine panic on allocation failures?
     const PANIC_ON_ALLOC_FAIL: bool;
 
+    /// Should post-monomorphization checks be run when a stack frame is pushed?
+    const POST_MONO_CHECKS: bool = true;
+
     /// Whether memory accesses should be alignment-checked.
     fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment;
 
@@ -552,7 +555,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
         def_id: DefId,
     ) -> InterpResult<$tcx, Pointer> {
         // Use the `AllocId` associated with the `DefId`. Any actual *access* will fail.
-        Ok(Pointer::new(ecx.tcx.create_static_alloc(def_id), Size::ZERO))
+        Ok(Pointer::new(ecx.tcx.reserve_and_set_static_alloc(def_id), Size::ZERO))
     }
 
     #[inline(always)]
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 11bffedf54a..436c4d521a5 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -176,12 +176,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         M::adjust_alloc_base_pointer(self, ptr)
     }
 
-    pub fn create_fn_alloc_ptr(
-        &mut self,
-        fn_val: FnVal<'tcx, M::ExtraFnVal>,
-    ) -> Pointer<M::Provenance> {
+    pub fn fn_ptr(&mut self, fn_val: FnVal<'tcx, M::ExtraFnVal>) -> Pointer<M::Provenance> {
         let id = match fn_val {
-            FnVal::Instance(instance) => self.tcx.create_fn_alloc(instance),
+            FnVal::Instance(instance) => self.tcx.reserve_and_set_fn_alloc(instance),
             FnVal::Other(extra) => {
                 // FIXME(RalfJung): Should we have a cache here?
                 let id = self.tcx.reserve_alloc_id();
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index 26dd9098bb5..3d42fda1f68 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -136,7 +136,11 @@ impl<Prov: Provenance> std::fmt::Display for ImmTy<'_, Prov> {
 
 impl<Prov: Provenance> std::fmt::Debug for ImmTy<'_, Prov> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("ImmTy").field("imm", &self.imm).field("ty", &self.layout.ty).finish()
+        // Printing `layout` results in too much noise; just print a nice version of the type.
+        f.debug_struct("ImmTy")
+            .field("imm", &self.imm)
+            .field("ty", &format_args!("{}", self.layout.ty))
+            .finish()
     }
 }
 
@@ -305,7 +309,11 @@ pub struct OpTy<'tcx, Prov: Provenance = AllocId> {
 
 impl<Prov: Provenance> std::fmt::Debug for OpTy<'_, Prov> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("OpTy").field("op", &self.op).field("ty", &self.layout.ty).finish()
+        // Printing `layout` results in too much noise; just print a nice version of the type.
+        f.debug_struct("OpTy")
+            .field("op", &self.op)
+            .field("ty", &format_args!("{}", self.layout.ty))
+            .finish()
     }
 }
 
@@ -756,11 +764,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         };
         let layout = from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty))?;
         let op = match val_val {
-            ConstValue::ByRef { alloc, offset } => {
-                let id = self.tcx.create_memory_alloc(alloc);
+            ConstValue::Indirect { alloc_id, offset } => {
                 // We rely on mutability being set correctly in that allocation to prevent writes
                 // where none should happen.
-                let ptr = self.global_base_pointer(Pointer::new(id, offset))?;
+                let ptr = self.global_base_pointer(Pointer::new(alloc_id, offset))?;
                 Operand::Indirect(MemPlace::from_ptr(ptr.into()))
             }
             ConstValue::Scalar(x) => Operand::Immediate(adjust_scalar(x)?.into()),
@@ -769,7 +776,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // We rely on mutability being set correctly in `data` to prevent writes
                 // where none should happen.
                 let ptr = Pointer::new(
-                    self.tcx.create_memory_alloc(data),
+                    self.tcx.reserve_and_set_memory_alloc(data),
                     Size::from_bytes(start), // offset: `start`
                 );
                 Operand::Immediate(Immediate::new_slice(
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index d8ad82d3da0..0f66df5c30d 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -117,9 +117,10 @@ pub struct MPlaceTy<'tcx, Prov: Provenance = AllocId> {
 
 impl<Prov: Provenance> std::fmt::Debug for MPlaceTy<'_, Prov> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        // Printing `layout` results in too much noise; just print a nice version of the type.
         f.debug_struct("MPlaceTy")
             .field("mplace", &self.mplace)
-            .field("ty", &self.layout.ty)
+            .field("ty", &format_args!("{}", self.layout.ty))
             .finish()
     }
 }
@@ -237,7 +238,11 @@ pub struct PlaceTy<'tcx, Prov: Provenance = AllocId> {
 
 impl<Prov: Provenance> std::fmt::Debug for PlaceTy<'_, Prov> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("PlaceTy").field("place", &self.place).field("ty", &self.layout.ty).finish()
+        // Printing `layout` results in too much noise; just print a nice version of the type.
+        f.debug_struct("PlaceTy")
+            .field("place", &self.place)
+            .field("ty", &format_args!("{}", self.layout.ty))
+            .finish()
     }
 }
 
@@ -796,6 +801,13 @@ where
         dest: &impl Writeable<'tcx, M::Provenance>,
         allow_transmute: bool,
     ) -> InterpResult<'tcx> {
+        // Generally for transmutation, data must be valid both at the old and new type.
+        // But if the types are the same, the 2nd validation below suffices.
+        if src.layout().ty != dest.layout().ty && M::enforce_validity(self, src.layout()) {
+            self.validate_operand(&src.to_op(self)?)?;
+        }
+
+        // Do the actual copy.
         self.copy_op_no_validate(src, dest, allow_transmute)?;
 
         if M::enforce_validity(self, dest.layout()) {
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index 57f99bd8f61..cf1f7ff75e1 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -301,7 +301,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let op = self.eval_place_to_op(place, None)?;
                 let variant = self.read_discriminant(&op)?;
                 let discr = self.discriminant_for_variant(op.layout, variant)?;
-                self.write_scalar(discr, &dest)?;
+                self.write_immediate(*discr, &dest)?;
             }
         }
 
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index bc4edf1c4b6..e15499bc68d 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -6,12 +6,16 @@ use rustc_middle::{
     mir,
     ty::{
         self,
-        layout::{FnAbiOf, LayoutOf, TyAndLayout},
-        Instance, Ty,
+        layout::{FnAbiOf, IntegerExt, LayoutOf, TyAndLayout},
+        AdtDef, Instance, Ty,
     },
 };
-use rustc_target::abi::call::{ArgAbi, ArgAttribute, ArgAttributes, FnAbi, PassMode};
+use rustc_span::sym;
 use rustc_target::abi::{self, FieldIdx};
+use rustc_target::abi::{
+    call::{ArgAbi, FnAbi, PassMode},
+    Integer,
+};
 use rustc_target::spec::abi::Abi;
 
 use super::{
@@ -255,147 +259,169 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
     /// Find the wrapped inner type of a transparent wrapper.
     /// Must not be called on 1-ZST (as they don't have a uniquely defined "wrapped field").
-    fn unfold_transparent(&self, layout: TyAndLayout<'tcx>) -> TyAndLayout<'tcx> {
+    ///
+    /// We work with `TyAndLayout` here since that makes it much easier to iterate over all fields.
+    fn unfold_transparent(
+        &self,
+        layout: TyAndLayout<'tcx>,
+        may_unfold: impl Fn(AdtDef<'tcx>) -> bool,
+    ) -> TyAndLayout<'tcx> {
         match layout.ty.kind() {
-            ty::Adt(adt_def, _) if adt_def.repr().transparent() => {
+            ty::Adt(adt_def, _) if adt_def.repr().transparent() && may_unfold(*adt_def) => {
                 assert!(!adt_def.is_enum());
-                // Find the non-1-ZST field.
-                let mut non_1zst_fields = (0..layout.fields.count()).filter_map(|idx| {
-                    let field = layout.field(self, idx);
-                    if field.is_1zst() { None } else { Some(field) }
-                });
-                let first = non_1zst_fields.next().expect("`unfold_transparent` called on 1-ZST");
-                assert!(
-                    non_1zst_fields.next().is_none(),
-                    "more than one non-1-ZST field in a transparent type"
-                );
-
-                // Found it!
-                self.unfold_transparent(first)
+                // Find the non-1-ZST field, and recurse.
+                let (_, field) = layout.non_1zst_field(self).unwrap();
+                self.unfold_transparent(field, may_unfold)
             }
             // Not a transparent type, no further unfolding.
             _ => layout,
         }
     }
 
+    /// Unwrap types that are guaranteed a null-pointer-optimization
+    fn unfold_npo(&self, layout: TyAndLayout<'tcx>) -> InterpResult<'tcx, TyAndLayout<'tcx>> {
+        // Check if this is `Option` wrapping some type.
+        let inner = match layout.ty.kind() {
+            ty::Adt(def, args) if self.tcx.is_diagnostic_item(sym::Option, def.did()) => {
+                args[0].as_type().unwrap()
+            }
+            _ => {
+                // Not an `Option`.
+                return Ok(layout);
+            }
+        };
+        let inner = self.layout_of(inner)?;
+        // Check if the inner type is one of the NPO-guaranteed ones.
+        // For that we first unpeel transparent *structs* (but not unions).
+        let is_npo = |def: AdtDef<'tcx>| {
+            self.tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed)
+        };
+        let inner = self.unfold_transparent(inner, /* may_unfold */ |def| {
+            // Stop at NPO tpyes so that we don't miss that attribute in the check below!
+            def.is_struct() && !is_npo(def)
+        });
+        Ok(match inner.ty.kind() {
+            ty::Ref(..) | ty::FnPtr(..) => {
+                // Option<&T> behaves like &T, and same for fn()
+                inner
+            }
+            ty::Adt(def, _) if is_npo(*def) => {
+                // Once we found a `nonnull_optimization_guaranteed` type, further strip off
+                // newtype structs from it to find the underlying ABI type.
+                self.unfold_transparent(inner, /* may_unfold */ |def| def.is_struct())
+            }
+            _ => {
+                // Everything else we do not unfold.
+                layout
+            }
+        })
+    }
+
     /// Check if these two layouts look like they are fn-ABI-compatible.
     /// (We also compare the `PassMode`, so this doesn't have to check everything. But it turns out
     /// that only checking the `PassMode` is insufficient.)
     fn layout_compat(
         &self,
-        caller_layout: TyAndLayout<'tcx>,
-        callee_layout: TyAndLayout<'tcx>,
-    ) -> bool {
-        if caller_layout.ty == callee_layout.ty {
-            // Fast path: equal types are definitely compatible.
-            return true;
+        caller: TyAndLayout<'tcx>,
+        callee: TyAndLayout<'tcx>,
+    ) -> InterpResult<'tcx, bool> {
+        // Fast path: equal types are definitely compatible.
+        if caller.ty == callee.ty {
+            return Ok(true);
         }
-
-        match (caller_layout.abi, callee_layout.abi) {
-            // If both sides have Scalar/Vector/ScalarPair ABI, we can easily directly compare them.
-            // Different valid ranges are okay (the validity check will complain if this leads to
-            // invalid transmutes). Different signs are *not* okay on some targets (e.g. `extern
-            // "C"` on `s390x` where small integers are passed zero/sign-extended in large
-            // registers), so we generally reject them to increase portability.
-            // NOTE: this is *not* a stable guarantee! It just reflects a property of our current
-            // ABIs. It's also fragile; the same pair of types might be considered ABI-compatible
-            // when used directly by-value but not considered compatible as a struct field or array
-            // element.
-            (abi::Abi::Scalar(caller), abi::Abi::Scalar(callee)) => {
-                caller.primitive() == callee.primitive()
-            }
-            (
-                abi::Abi::Vector { element: caller_element, count: caller_count },
-                abi::Abi::Vector { element: callee_element, count: callee_count },
-            ) => {
-                caller_element.primitive() == callee_element.primitive()
-                    && caller_count == callee_count
-            }
-            (abi::Abi::ScalarPair(caller1, caller2), abi::Abi::ScalarPair(callee1, callee2)) => {
-                caller1.primitive() == callee1.primitive()
-                    && caller2.primitive() == callee2.primitive()
-            }
-            (abi::Abi::Aggregate { .. }, abi::Abi::Aggregate { .. }) => {
-                // Aggregates are compatible only if they newtype-wrap the same type, or if they are both 1-ZST.
-                // (The latter part is needed to ensure e.g. that `struct Zst` is compatible with `struct Wrap((), Zst)`.)
-                // This is conservative, but also means that our check isn't quite so heavily dependent on the `PassMode`,
-                // which means having ABI-compatibility on one target is much more likely to imply compatibility for other targets.
-                if caller_layout.is_1zst() || callee_layout.is_1zst() {
-                    // If either is a 1-ZST, both must be.
-                    caller_layout.is_1zst() && callee_layout.is_1zst()
-                } else {
-                    // Neither is a 1-ZST, so we can check what they are wrapping.
-                    self.unfold_transparent(caller_layout).ty
-                        == self.unfold_transparent(callee_layout).ty
+        // 1-ZST are compatible with all 1-ZST (and with nothing else).
+        if caller.is_1zst() || callee.is_1zst() {
+            return Ok(caller.is_1zst() && callee.is_1zst());
+        }
+        // Unfold newtypes and NPO optimizations.
+        let unfold = |layout: TyAndLayout<'tcx>| {
+            self.unfold_npo(self.unfold_transparent(layout, /* may_unfold */ |_def| true))
+        };
+        let caller = unfold(caller)?;
+        let callee = unfold(callee)?;
+        // Now see if these inner types are compatible.
+
+        // Compatible pointer types. For thin pointers, we have to accept even non-`repr(transparent)`
+        // things as compatible due to `DispatchFromDyn`. For instance, `Rc<i32>` and `*mut i32`
+        // must be compatible. So we just accept everything with Pointer ABI as compatible,
+        // even if this will accept some code that is not stably guaranteed to work.
+        // This also handles function pointers.
+        let thin_pointer = |layout: TyAndLayout<'tcx>| match layout.abi {
+            abi::Abi::Scalar(s) => match s.primitive() {
+                abi::Primitive::Pointer(addr_space) => Some(addr_space),
+                _ => None,
+            },
+            _ => None,
+        };
+        if let (Some(caller), Some(callee)) = (thin_pointer(caller), thin_pointer(callee)) {
+            return Ok(caller == callee);
+        }
+        // For wide pointers we have to get the pointee type.
+        let pointee_ty = |ty: Ty<'tcx>| -> InterpResult<'tcx, Option<Ty<'tcx>>> {
+            // We cannot use `builtin_deref` here since we need to reject `Box<T, MyAlloc>`.
+            Ok(Some(match ty.kind() {
+                ty::Ref(_, ty, _) => *ty,
+                ty::RawPtr(mt) => mt.ty,
+                // We should only accept `Box` with the default allocator.
+                // It's hard to test for that though so we accept every 1-ZST allocator.
+                ty::Adt(def, args)
+                    if def.is_box()
+                        && self.layout_of(args[1].expect_ty()).is_ok_and(|l| l.is_1zst()) =>
+                {
+                    args[0].expect_ty()
                 }
-            }
-            // What remains is `Abi::Uninhabited` (which can never be passed anyway) and
-            // mismatching ABIs, that should all be rejected.
-            _ => false,
+                _ => return Ok(None),
+            }))
+        };
+        if let (Some(caller), Some(callee)) = (pointee_ty(caller.ty)?, pointee_ty(callee.ty)?) {
+            // This is okay if they have the same metadata type.
+            let meta_ty = |ty: Ty<'tcx>| {
+                let (meta, only_if_sized) = ty.ptr_metadata_ty(*self.tcx, |ty| ty);
+                assert!(
+                    !only_if_sized,
+                    "there should be no more 'maybe has that metadata' types during interpretation"
+                );
+                meta
+            };
+            return Ok(meta_ty(caller) == meta_ty(callee));
         }
+
+        // Compatible integer types (in particular, usize vs ptr-sized-u32/u64).
+        let int_ty = |ty: Ty<'tcx>| {
+            Some(match ty.kind() {
+                ty::Int(ity) => (Integer::from_int_ty(&self.tcx, *ity), /* signed */ true),
+                ty::Uint(uty) => (Integer::from_uint_ty(&self.tcx, *uty), /* signed */ false),
+                _ => return None,
+            })
+        };
+        if let (Some(caller), Some(callee)) = (int_ty(caller.ty), int_ty(callee.ty)) {
+            // This is okay if they are the same integer type.
+            return Ok(caller == callee);
+        }
+
+        // Fall back to exact equality.
+        // FIXME: We are missing the rules for "repr(C) wrapping compatible types".
+        Ok(caller == callee)
     }
 
     fn check_argument_compat(
         &self,
         caller_abi: &ArgAbi<'tcx, Ty<'tcx>>,
         callee_abi: &ArgAbi<'tcx, Ty<'tcx>>,
-    ) -> bool {
-        // When comparing the PassMode, we have to be smart about comparing the attributes.
-        let arg_attr_compat = |a1: &ArgAttributes, a2: &ArgAttributes| {
-            // There's only one regular attribute that matters for the call ABI: InReg.
-            // Everything else is things like noalias, dereferenceable, nonnull, ...
-            // (This also applies to pointee_size, pointee_align.)
-            if a1.regular.contains(ArgAttribute::InReg) != a2.regular.contains(ArgAttribute::InReg)
-            {
-                return false;
-            }
-            // We also compare the sign extension mode -- this could let the callee make assumptions
-            // about bits that conceptually were not even passed.
-            if a1.arg_ext != a2.arg_ext {
-                return false;
-            }
-            return true;
-        };
-        let mode_compat = || match (&caller_abi.mode, &callee_abi.mode) {
-            (PassMode::Ignore, PassMode::Ignore) => true, // can still be reached for the return type
-            (PassMode::Direct(a1), PassMode::Direct(a2)) => arg_attr_compat(a1, a2),
-            (PassMode::Pair(a1, b1), PassMode::Pair(a2, b2)) => {
-                arg_attr_compat(a1, a2) && arg_attr_compat(b1, b2)
-            }
-            (PassMode::Cast(c1, pad1), PassMode::Cast(c2, pad2)) => c1 == c2 && pad1 == pad2,
-            (
-                PassMode::Indirect { attrs: a1, extra_attrs: None, on_stack: s1 },
-                PassMode::Indirect { attrs: a2, extra_attrs: None, on_stack: s2 },
-            ) => arg_attr_compat(a1, a2) && s1 == s2,
-            (
-                PassMode::Indirect { attrs: a1, extra_attrs: Some(e1), on_stack: s1 },
-                PassMode::Indirect { attrs: a2, extra_attrs: Some(e2), on_stack: s2 },
-            ) => arg_attr_compat(a1, a2) && arg_attr_compat(e1, e2) && s1 == s2,
-            _ => false,
-        };
-
-        // Ideally `PassMode` would capture everything there is about argument passing, but that is
-        // not the case: in `FnAbi::llvm_type`, also parts of the layout and type information are
-        // used. So we need to check that *both* sufficiently agree to ensures the arguments are
-        // compatible.
-        // For instance, `layout_compat` is needed to reject `i32` vs `f32`, which is not reflected
-        // in `PassMode`. `mode_compat` is needed to reject `u8` vs `bool`, which have the same
-        // `abi::Primitive` but different `arg_ext`.
-        if self.layout_compat(caller_abi.layout, callee_abi.layout) && mode_compat() {
-            // Something went very wrong if our checks don't even imply that the layout is the same.
-            assert!(
-                caller_abi.layout.size == callee_abi.layout.size
-                    && caller_abi.layout.align.abi == callee_abi.layout.align.abi
-                    && caller_abi.layout.is_sized() == callee_abi.layout.is_sized()
-            );
-            return true;
+    ) -> InterpResult<'tcx, bool> {
+        // We do not want to accept things as ABI-compatible that just "happen to be" compatible on the current target,
+        // so we implement a type-based check that reflects the guaranteed rules for ABI compatibility.
+        if self.layout_compat(caller_abi.layout, callee_abi.layout)? {
+            // Ensure that our checks imply actual ABI compatibility for this concrete call.
+            assert!(caller_abi.eq_abi(&callee_abi));
+            return Ok(true);
         } else {
             trace!(
                 "check_argument_compat: incompatible ABIs:\ncaller: {:?}\ncallee: {:?}",
                 caller_abi,
                 callee_abi
             );
-            return false;
+            return Ok(false);
         }
     }
 
@@ -414,6 +440,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         'tcx: 'x,
         'tcx: 'y,
     {
+        assert_eq!(callee_ty, callee_abi.layout.ty);
         if matches!(callee_abi.mode, PassMode::Ignore) {
             // This one is skipped. Still must be made live though!
             if !already_live {
@@ -425,15 +452,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         let Some((caller_arg, caller_abi)) = caller_args.next() else {
             throw_ub_custom!(fluent::const_eval_not_enough_caller_args);
         };
+        assert_eq!(caller_arg.layout().layout, caller_abi.layout.layout);
+        // Sadly we cannot assert that `caller_arg.layout().ty` and `caller_abi.layout.ty` are
+        // equal; in closures the types sometimes differ. We just hope that `caller_abi` is the
+        // right type to print to the user.
+
         // Check compatibility
-        if !self.check_argument_compat(caller_abi, callee_abi) {
-            let callee_ty = format!("{}", callee_ty);
-            let caller_ty = format!("{}", caller_arg.layout().ty);
-            throw_ub_custom!(
-                fluent::const_eval_incompatible_types,
-                callee_ty = callee_ty,
-                caller_ty = caller_ty,
-            )
+        if !self.check_argument_compat(caller_abi, callee_abi)? {
+            throw_ub!(AbiMismatchArgument {
+                caller_ty: caller_abi.layout.ty,
+                callee_ty: callee_abi.layout.ty
+            });
         }
         // We work with a copy of the argument for now; if this is in-place argument passing, we
         // will later protect the source it comes from. This means the callee cannot observe if we
@@ -637,7 +666,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     // taking into account the `spread_arg`. If we could write
                     // this is a single iterator (that handles `spread_arg`), then
                     // `pass_argument` would be the loop body. It takes care to
-                    // not advance `caller_iter` for ZSTs.
+                    // not advance `caller_iter` for ignored arguments.
                     let mut callee_args_abis = callee_fn_abi.args.iter();
                     for local in body.args_iter() {
                         // Construct the destination place for this argument. At this point all
@@ -699,14 +728,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                         throw_ub_custom!(fluent::const_eval_too_many_caller_args);
                     }
                     // Don't forget to check the return type!
-                    if !self.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret) {
-                        let callee_ty = format!("{}", callee_fn_abi.ret.layout.ty);
-                        let caller_ty = format!("{}", caller_fn_abi.ret.layout.ty);
-                        throw_ub_custom!(
-                            fluent::const_eval_incompatible_return_types,
-                            callee_ty = callee_ty,
-                            caller_ty = caller_ty,
-                        )
+                    if !self.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret)? {
+                        throw_ub!(AbiMismatchReturn {
+                            caller_ty: caller_fn_abi.ret.layout.ty,
+                            callee_ty: callee_fn_abi.ret.layout.ty
+                        });
                     }
                     // Ensure the return place is aligned and dereferenceable, and protect it for
                     // in-place return value passing.
@@ -728,7 +754,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     Ok(()) => Ok(()),
                 }
             }
-            // cannot use the shim here, because that will only result in infinite recursion
+            // `InstanceDef::Virtual` does not have callable MIR. Calls to `Virtual` instances must be
+            // codegen'd / interpreted as virtual calls through the vtable.
             ty::InstanceDef::Virtual(def_id, idx) => {
                 let mut args = args.to_vec();
                 // We have to implement all "object safe receivers". So we have to go search for a
@@ -760,25 +787,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                         _ => {
                             // Not there yet, search for the only non-ZST field.
                             // (The rules for `DispatchFromDyn` ensure there's exactly one such field.)
-                            let mut non_zst_field = None;
-                            for i in 0..receiver.layout.fields.count() {
-                                let field = self.project_field(&receiver, i)?;
-                                let zst = field.layout.is_1zst();
-                                if !zst {
-                                    assert!(
-                                        non_zst_field.is_none(),
-                                        "multiple non-1-ZST fields in dyn receiver type {}",
-                                        receiver.layout.ty
-                                    );
-                                    non_zst_field = Some(field);
-                                }
-                            }
-                            receiver = non_zst_field.unwrap_or_else(|| {
-                                panic!(
-                                    "no non-1-ZST fields in dyn receiver type {}",
-                                    receiver.layout.ty
-                                )
-                            });
+                            let (idx, _) = receiver.layout.non_1zst_field(self).expect(
+                                "not exactly one non-1-ZST field in a `DispatchFromDyn` type",
+                            );
+                            receiver = self.project_field(&receiver, idx)?;
                         }
                     }
                 };
@@ -852,18 +864,26 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 }
 
                 // Adjust receiver argument. Layout can be any (thin) ptr.
+                let receiver_ty = Ty::new_mut_ptr(self.tcx.tcx, dyn_ty);
                 args[0] = FnArg::Copy(
                     ImmTy::from_immediate(
                         Scalar::from_maybe_pointer(adjusted_receiver, self).into(),
-                        self.layout_of(Ty::new_mut_ptr(self.tcx.tcx, dyn_ty))?,
+                        self.layout_of(receiver_ty)?,
                     )
                     .into(),
                 );
                 trace!("Patched receiver operand to {:#?}", args[0]);
+                // Need to also adjust the type in the ABI. Strangely, the layout there is actually
+                // already fine! Just the type is bogus. This is due to what `force_thin_self_ptr`
+                // does in `fn_abi_new_uncached`; supposedly, codegen relies on having the bogus
+                // type, so we just patch this up locally.
+                let mut caller_fn_abi = caller_fn_abi.clone();
+                caller_fn_abi.args[0].layout.ty = receiver_ty;
+
                 // recurse with concrete function
                 self.eval_fn_call(
                     FnVal::Instance(fn_inst),
-                    (caller_abi, caller_fn_abi),
+                    (caller_abi, &caller_fn_abi),
                     &args,
                     with_caller_location,
                     destination,
diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs
index fa15d466ac1..a9ca268a2a9 100644
--- a/compiler/rustc_const_eval/src/interpret/traits.rs
+++ b/compiler/rustc_const_eval/src/interpret/traits.rs
@@ -27,7 +27,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         ensure_monomorphic_enough(*self.tcx, ty)?;
         ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?;
 
-        let vtable_symbolic_allocation = self.tcx.create_vtable_alloc(ty, poly_trait_ref);
+        let vtable_symbolic_allocation = self.tcx.reserve_and_set_vtable_alloc(ty, poly_trait_ref);
         let vtable_ptr = self.global_base_pointer(Pointer::from(vtable_symbolic_allocation))?;
         Ok(vtable_ptr.into())
     }
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 770c3f7f02c..2f5f2ad6534 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -6,13 +6,7 @@ use rustc_index::IndexVec;
 use rustc_infer::traits::Reveal;
 use rustc_middle::mir::interpret::Scalar;
 use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
-use rustc_middle::mir::{
-    traversal, BasicBlock, BinOp, Body, BorrowKind, CastKind, CopyNonOverlapping, Local, Location,
-    MirPass, MirPhase, NonDivergingIntrinsic, NullOp, Operand, Place, PlaceElem, PlaceRef,
-    ProjectionElem, RetagKind, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind,
-    Terminator, TerminatorKind, UnOp, UnwindAction, UnwindTerminateReason, VarDebugInfo,
-    VarDebugInfoContents, START_BLOCK,
-};
+use rustc_middle::mir::*;
 use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitableExt};
 use rustc_mir_dataflow::impls::MaybeStorageLive;
 use rustc_mir_dataflow::storage::always_storage_live_locals;
@@ -757,37 +751,37 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
     }
 
     fn visit_var_debug_info(&mut self, debuginfo: &VarDebugInfo<'tcx>) {
-        let check_place = |this: &mut Self, place: Place<'_>| {
-            if place.projection.iter().any(|p| !p.can_use_in_debuginfo()) {
-                this.fail(
+        if let Some(box VarDebugInfoFragment { ty, ref projection }) = debuginfo.composite {
+            if ty.is_union() || ty.is_enum() {
+                self.fail(
                     START_BLOCK.start_location(),
-                    format!("illegal place {:?} in debuginfo for {:?}", place, debuginfo.name),
+                    format!("invalid type {ty:?} in debuginfo for {:?}", debuginfo.name),
                 );
             }
-        };
+            if projection.is_empty() {
+                self.fail(
+                    START_BLOCK.start_location(),
+                    format!("invalid empty projection in debuginfo for {:?}", debuginfo.name),
+                );
+            }
+            if projection.iter().any(|p| !matches!(p, PlaceElem::Field(..))) {
+                self.fail(
+                    START_BLOCK.start_location(),
+                    format!(
+                        "illegal projection {:?} in debuginfo for {:?}",
+                        projection, debuginfo.name
+                    ),
+                );
+            }
+        }
         match debuginfo.value {
             VarDebugInfoContents::Const(_) => {}
             VarDebugInfoContents::Place(place) => {
-                check_place(self, place);
-            }
-            VarDebugInfoContents::Composite { ty, ref fragments } => {
-                for f in fragments {
-                    check_place(self, f.contents);
-                    if ty.is_union() || ty.is_enum() {
-                        self.fail(
-                            START_BLOCK.start_location(),
-                            format!("invalid type {ty:?} for composite debuginfo"),
-                        );
-                    }
-                    if f.projection.iter().any(|p| !matches!(p, PlaceElem::Field(..))) {
-                        self.fail(
-                            START_BLOCK.start_location(),
-                            format!(
-                                "illegal projection {:?} in debuginfo for {:?}",
-                                f.projection, debuginfo.name
-                            ),
-                        );
-                    }
+                if place.projection.iter().any(|p| !p.can_use_in_debuginfo()) {
+                    self.fail(
+                        START_BLOCK.start_location(),
+                        format!("illegal place {:?} in debuginfo for {:?}", place, debuginfo.name),
+                    );
                 }
             }
         }
diff --git a/compiler/rustc_data_structures/src/sharded.rs b/compiler/rustc_data_structures/src/sharded.rs
index 0f769c1f3bf..29516fffd6a 100644
--- a/compiler/rustc_data_structures/src/sharded.rs
+++ b/compiler/rustc_data_structures/src/sharded.rs
@@ -1,7 +1,7 @@
 use crate::fx::{FxHashMap, FxHasher};
 #[cfg(parallel_compiler)]
 use crate::sync::{is_dyn_thread_safe, CacheAligned};
-use crate::sync::{Lock, LockGuard};
+use crate::sync::{Lock, LockGuard, Mode};
 #[cfg(parallel_compiler)]
 use itertools::Either;
 use std::borrow::Borrow;
@@ -73,6 +73,56 @@ impl<T> Sharded<T> {
         }
     }
 
+    /// The shard is selected by hashing `val` with `FxHasher`.
+    #[inline]
+    #[track_caller]
+    pub fn lock_shard_by_value<K: Hash + ?Sized>(&self, _val: &K) -> LockGuard<'_, T> {
+        match self {
+            Self::Single(single) => {
+                // Syncronization is disabled so use the `lock_assume_no_sync` method optimized
+                // for that case.
+
+                // SAFETY: We know `is_dyn_thread_safe` was false when creating the lock thus
+                // `might_be_dyn_thread_safe` was also false.
+                unsafe { single.lock_assume(Mode::NoSync) }
+            }
+            #[cfg(parallel_compiler)]
+            Self::Shards(..) => self.lock_shard_by_hash(make_hash(_val)),
+        }
+    }
+
+    #[inline]
+    #[track_caller]
+    pub fn lock_shard_by_hash(&self, hash: u64) -> LockGuard<'_, T> {
+        self.lock_shard_by_index(get_shard_hash(hash))
+    }
+
+    #[inline]
+    #[track_caller]
+    pub fn lock_shard_by_index(&self, _i: usize) -> LockGuard<'_, T> {
+        match self {
+            Self::Single(single) => {
+                // Syncronization is disabled so use the `lock_assume_no_sync` method optimized
+                // for that case.
+
+                // SAFETY: We know `is_dyn_thread_safe` was false when creating the lock thus
+                // `might_be_dyn_thread_safe` was also false.
+                unsafe { single.lock_assume(Mode::NoSync) }
+            }
+            #[cfg(parallel_compiler)]
+            Self::Shards(shards) => {
+                // Syncronization is enabled so use the `lock_assume_sync` method optimized
+                // for that case.
+
+                // SAFETY (get_unchecked): The index gets ANDed with the shard mask, ensuring it is
+                // always inbounds.
+                // SAFETY (lock_assume_sync): We know `is_dyn_thread_safe` was true when creating
+                // the lock thus `might_be_dyn_thread_safe` was also true.
+                unsafe { shards.get_unchecked(_i & (SHARDS - 1)).0.lock_assume(Mode::Sync) }
+            }
+        }
+    }
+
     #[inline]
     pub fn lock_shards(&self) -> impl Iterator<Item = LockGuard<'_, T>> {
         match self {
@@ -124,7 +174,7 @@ impl<K: Eq + Hash + Copy> ShardedHashMap<K, ()> {
         Q: Hash + Eq,
     {
         let hash = make_hash(value);
-        let mut shard = self.get_shard_by_hash(hash).lock();
+        let mut shard = self.lock_shard_by_hash(hash);
         let entry = shard.raw_entry_mut().from_key_hashed_nocheck(hash, value);
 
         match entry {
@@ -144,7 +194,7 @@ impl<K: Eq + Hash + Copy> ShardedHashMap<K, ()> {
         Q: Hash + Eq,
     {
         let hash = make_hash(&value);
-        let mut shard = self.get_shard_by_hash(hash).lock();
+        let mut shard = self.lock_shard_by_hash(hash);
         let entry = shard.raw_entry_mut().from_key_hashed_nocheck(hash, &value);
 
         match entry {
@@ -166,7 +216,7 @@ pub trait IntoPointer {
 impl<K: Eq + Hash + Copy + IntoPointer> ShardedHashMap<K, ()> {
     pub fn contains_pointer_to<T: Hash + IntoPointer>(&self, value: &T) -> bool {
         let hash = make_hash(&value);
-        let shard = self.get_shard_by_hash(hash).lock();
+        let shard = self.lock_shard_by_hash(hash);
         let value = value.into_pointer();
         shard.raw_entry().from_hash(hash, |entry| entry.into_pointer() == value).is_some()
     }
diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs
index 8eda9043e35..cca043ba0d1 100644
--- a/compiler/rustc_data_structures/src/sync.rs
+++ b/compiler/rustc_data_structures/src/sync.rs
@@ -41,19 +41,21 @@
 //! [^2] `MTLockRef` is a typedef.
 
 pub use crate::marker::*;
-use parking_lot::Mutex;
-use std::any::Any;
 use std::collections::HashMap;
 use std::hash::{BuildHasher, Hash};
 use std::ops::{Deref, DerefMut};
-use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
 
 mod lock;
-pub use lock::{Lock, LockGuard};
+pub use lock::{Lock, LockGuard, Mode};
 
 mod worker_local;
 pub use worker_local::{Registry, WorkerLocal};
 
+mod parallel;
+#[cfg(parallel_compiler)]
+pub use parallel::scope;
+pub use parallel::{join, par_for_each_in, par_map, parallel_guard};
+
 pub use std::sync::atomic::Ordering;
 pub use std::sync::atomic::Ordering::SeqCst;
 
@@ -86,7 +88,6 @@ mod mode {
 
     // Whether thread safety might be enabled.
     #[inline]
-    #[cfg(parallel_compiler)]
     pub fn might_be_dyn_thread_safe() -> bool {
         DYN_THREAD_SAFE_MODE.load(Ordering::Relaxed) != DYN_NOT_THREAD_SAFE
     }
@@ -108,37 +109,6 @@ mod mode {
 
 pub use mode::{is_dyn_thread_safe, set_dyn_thread_safe_mode};
 
-/// A guard used to hold panics that occur during a parallel section to later by unwound.
-/// This is used for the parallel compiler to prevent fatal errors from non-deterministically
-/// hiding errors by ensuring that everything in the section has completed executing before
-/// continuing with unwinding. It's also used for the non-parallel code to ensure error message
-/// output match the parallel compiler for testing purposes.
-pub struct ParallelGuard {
-    panic: Mutex<Option<Box<dyn Any + std::marker::Send + 'static>>>,
-}
-
-impl ParallelGuard {
-    pub fn run<R>(&self, f: impl FnOnce() -> R) -> Option<R> {
-        catch_unwind(AssertUnwindSafe(f))
-            .map_err(|err| {
-                *self.panic.lock() = Some(err);
-            })
-            .ok()
-    }
-}
-
-/// This gives access to a fresh parallel guard in the closure and will unwind any panics
-/// caught in it after the closure returns.
-#[inline]
-pub fn parallel_guard<R>(f: impl FnOnce(&ParallelGuard) -> R) -> R {
-    let guard = ParallelGuard { panic: Mutex::new(None) };
-    let ret = f(&guard);
-    if let Some(panic) = guard.panic.into_inner() {
-        resume_unwind(panic);
-    }
-    ret
-}
-
 cfg_if! {
     if #[cfg(not(parallel_compiler))] {
         use std::ops::Add;
@@ -230,44 +200,6 @@ cfg_if! {
         pub type AtomicU32 = Atomic<u32>;
         pub type AtomicU64 = Atomic<u64>;
 
-        pub fn join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
-            where A: FnOnce() -> RA,
-                  B: FnOnce() -> RB
-        {
-            let (a, b) = parallel_guard(|guard| {
-                let a = guard.run(oper_a);
-                let b = guard.run(oper_b);
-                (a, b)
-            });
-            (a.unwrap(), b.unwrap())
-        }
-
-        #[macro_export]
-        macro_rules! parallel {
-            ($($blocks:block),*) => {{
-                $crate::sync::parallel_guard(|guard| {
-                    $(guard.run(|| $blocks);)*
-                });
-            }}
-        }
-
-        pub fn par_for_each_in<T: IntoIterator>(t: T, mut for_each: impl FnMut(T::Item) + Sync + Send) {
-            parallel_guard(|guard| {
-                t.into_iter().for_each(|i| {
-                    guard.run(|| for_each(i));
-                });
-            })
-        }
-
-        pub fn par_map<T: IntoIterator, R, C: FromIterator<R>>(
-            t: T,
-            mut map: impl FnMut(<<T as IntoIterator>::IntoIter as Iterator>::Item) -> R,
-        ) -> C {
-            parallel_guard(|guard| {
-                t.into_iter().filter_map(|i| guard.run(|| map(i))).collect()
-            })
-        }
-
         pub use std::rc::Rc as Lrc;
         pub use std::rc::Weak as Weak;
         pub use std::cell::Ref as ReadGuard;
@@ -373,105 +305,6 @@ cfg_if! {
 
         use std::thread;
 
-        #[inline]
-        pub fn join<A, B, RA: DynSend, RB: DynSend>(oper_a: A, oper_b: B) -> (RA, RB)
-        where
-            A: FnOnce() -> RA + DynSend,
-            B: FnOnce() -> RB + DynSend,
-        {
-            if mode::is_dyn_thread_safe() {
-                let oper_a = FromDyn::from(oper_a);
-                let oper_b = FromDyn::from(oper_b);
-                let (a, b) = rayon::join(move || FromDyn::from(oper_a.into_inner()()), move || FromDyn::from(oper_b.into_inner()()));
-                (a.into_inner(), b.into_inner())
-            } else {
-                let (a, b) = parallel_guard(|guard| {
-                    let a = guard.run(oper_a);
-                    let b = guard.run(oper_b);
-                    (a, b)
-                });
-                (a.unwrap(), b.unwrap())
-            }
-        }
-
-        // This function only works when `mode::is_dyn_thread_safe()`.
-        pub fn scope<'scope, OP, R>(op: OP) -> R
-        where
-            OP: FnOnce(&rayon::Scope<'scope>) -> R + DynSend,
-            R: DynSend,
-        {
-            let op = FromDyn::from(op);
-            rayon::scope(|s| FromDyn::from(op.into_inner()(s))).into_inner()
-        }
-
-        /// Runs a list of blocks in parallel. The first block is executed immediately on
-        /// the current thread. Use that for the longest running block.
-        #[macro_export]
-        macro_rules! parallel {
-            (impl $fblock:block [$($c:expr,)*] [$block:expr $(, $rest:expr)*]) => {
-                parallel!(impl $fblock [$block, $($c,)*] [$($rest),*])
-            };
-            (impl $fblock:block [$($blocks:expr,)*] []) => {
-                ::rustc_data_structures::sync::scope(|s| {
-                    $(let block = rustc_data_structures::sync::FromDyn::from(|| $blocks);
-                    s.spawn(move |_| block.into_inner()());)*
-                    (|| $fblock)();
-                });
-            };
-            ($fblock:block, $($blocks:block),*) => {
-                if rustc_data_structures::sync::is_dyn_thread_safe() {
-                    // Reverse the order of the later blocks since Rayon executes them in reverse order
-                    // when using a single thread. This ensures the execution order matches that
-                    // of a single threaded rustc.
-                    parallel!(impl $fblock [] [$($blocks),*]);
-                } else {
-                    $crate::sync::parallel_guard(|guard| {
-                        guard.run(|| $fblock);
-                        $(guard.run(|| $blocks);)*
-                    });
-                }
-            };
-        }
-
-        use rayon::iter::{FromParallelIterator, IntoParallelIterator, ParallelIterator};
-
-        pub fn par_for_each_in<I, T: IntoIterator<Item = I> + IntoParallelIterator<Item = I>>(
-            t: T,
-            for_each: impl Fn(I) + DynSync + DynSend
-        ) {
-            parallel_guard(|guard| {
-                if mode::is_dyn_thread_safe() {
-                    let for_each = FromDyn::from(for_each);
-                    t.into_par_iter().for_each(|i| {
-                        guard.run(|| for_each(i));
-                    });
-                } else {
-                    t.into_iter().for_each(|i| {
-                        guard.run(|| for_each(i));
-                    });
-                }
-            });
-        }
-
-        pub fn par_map<
-            I,
-            T: IntoIterator<Item = I> + IntoParallelIterator<Item = I>,
-            R: std::marker::Send,
-            C: FromIterator<R> + FromParallelIterator<R>
-        >(
-            t: T,
-            map: impl Fn(I) -> R + DynSync + DynSend
-        ) -> C {
-            parallel_guard(|guard| {
-                if mode::is_dyn_thread_safe() {
-                    let map = FromDyn::from(map);
-                    t.into_par_iter().filter_map(|i| guard.run(|| map(i))).collect()
-                } else {
-                    t.into_iter().filter_map(|i| guard.run(|| map(i))).collect()
-                }
-            })
-        }
-
         /// This makes locks panic if they are already held.
         /// It is only useful when you are running in a single thread
         const ERROR_CHECKING: bool = false;
diff --git a/compiler/rustc_data_structures/src/sync/freeze.rs b/compiler/rustc_data_structures/src/sync/freeze.rs
index d9f1d72d851..466c44f59bb 100644
--- a/compiler/rustc_data_structures/src/sync/freeze.rs
+++ b/compiler/rustc_data_structures/src/sync/freeze.rs
@@ -3,8 +3,10 @@ use crate::sync::{AtomicBool, ReadGuard, RwLock, WriteGuard};
 use crate::sync::{DynSend, DynSync};
 use std::{
     cell::UnsafeCell,
+    intrinsics::likely,
     marker::PhantomData,
     ops::{Deref, DerefMut},
+    ptr::NonNull,
     sync::atomic::Ordering,
 };
 
@@ -27,7 +29,47 @@ unsafe impl<T: DynSync + DynSend> DynSync for FreezeLock<T> {}
 impl<T> FreezeLock<T> {
     #[inline]
     pub fn new(value: T) -> Self {
-        Self { data: UnsafeCell::new(value), frozen: AtomicBool::new(false), lock: RwLock::new(()) }
+        Self::with(value, false)
+    }
+
+    #[inline]
+    pub fn frozen(value: T) -> Self {
+        Self::with(value, true)
+    }
+
+    #[inline]
+    pub fn with(value: T, frozen: bool) -> Self {
+        Self {
+            data: UnsafeCell::new(value),
+            frozen: AtomicBool::new(frozen),
+            lock: RwLock::new(()),
+        }
+    }
+
+    /// Clones the inner value along with the frozen state.
+    #[inline]
+    pub fn clone(&self) -> Self
+    where
+        T: Clone,
+    {
+        let lock = self.read();
+        Self::with(lock.clone(), self.is_frozen())
+    }
+
+    #[inline]
+    pub fn is_frozen(&self) -> bool {
+        self.frozen.load(Ordering::Acquire)
+    }
+
+    /// Get the inner value if frozen.
+    #[inline]
+    pub fn get(&self) -> Option<&T> {
+        if likely(self.frozen.load(Ordering::Acquire)) {
+            // SAFETY: This is frozen so the data cannot be modified.
+            unsafe { Some(&*self.data.get()) }
+        } else {
+            None
+        }
     }
 
     #[inline]
@@ -38,17 +80,35 @@ impl<T> FreezeLock<T> {
             } else {
                 Some(self.lock.read())
             },
-            lock: self,
+            data: unsafe { NonNull::new_unchecked(self.data.get()) },
         }
     }
 
     #[inline]
+    pub fn borrow(&self) -> FreezeReadGuard<'_, T> {
+        self.read()
+    }
+
+    #[inline]
     #[track_caller]
     pub fn write(&self) -> FreezeWriteGuard<'_, T> {
+        self.try_write().expect("still mutable")
+    }
+
+    #[inline]
+    pub fn try_write(&self) -> Option<FreezeWriteGuard<'_, T>> {
         let _lock_guard = self.lock.write();
         // Use relaxed ordering since we're in the write lock.
-        assert!(!self.frozen.load(Ordering::Relaxed), "still mutable");
-        FreezeWriteGuard { _lock_guard, lock: self, marker: PhantomData }
+        if self.frozen.load(Ordering::Relaxed) {
+            None
+        } else {
+            Some(FreezeWriteGuard {
+                _lock_guard,
+                data: unsafe { NonNull::new_unchecked(self.data.get()) },
+                frozen: &self.frozen,
+                marker: PhantomData,
+            })
+        }
     }
 
     #[inline]
@@ -66,43 +126,75 @@ impl<T> FreezeLock<T> {
 
 /// A guard holding shared access to a `FreezeLock` which is in a locked state or frozen.
 #[must_use = "if unused the FreezeLock may immediately unlock"]
-pub struct FreezeReadGuard<'a, T> {
+pub struct FreezeReadGuard<'a, T: ?Sized> {
     _lock_guard: Option<ReadGuard<'a, ()>>,
-    lock: &'a FreezeLock<T>,
+    data: NonNull<T>,
 }
 
-impl<'a, T: 'a> Deref for FreezeReadGuard<'a, T> {
+impl<'a, T: ?Sized + 'a> Deref for FreezeReadGuard<'a, T> {
     type Target = T;
     #[inline]
     fn deref(&self) -> &T {
-        // SAFETY: If `lock` is not frozen, `_lock_guard` holds the lock to the `UnsafeCell` so
-        // this has shared access until the `FreezeReadGuard` is dropped. If `lock` is frozen,
+        // SAFETY: If the lock is not frozen, `_lock_guard` holds the lock to the `UnsafeCell` so
+        // this has shared access until the `FreezeReadGuard` is dropped. If the lock is frozen,
         // the data cannot be modified and shared access is sound.
-        unsafe { &*self.lock.data.get() }
+        unsafe { &*self.data.as_ptr() }
+    }
+}
+
+impl<'a, T: ?Sized> FreezeReadGuard<'a, T> {
+    #[inline]
+    pub fn map<U: ?Sized>(this: Self, f: impl FnOnce(&T) -> &U) -> FreezeReadGuard<'a, U> {
+        FreezeReadGuard { data: NonNull::from(f(&*this)), _lock_guard: this._lock_guard }
     }
 }
 
 /// A guard holding mutable access to a `FreezeLock` which is in a locked state or frozen.
 #[must_use = "if unused the FreezeLock may immediately unlock"]
-pub struct FreezeWriteGuard<'a, T> {
+pub struct FreezeWriteGuard<'a, T: ?Sized> {
     _lock_guard: WriteGuard<'a, ()>,
-    lock: &'a FreezeLock<T>,
+    frozen: &'a AtomicBool,
+    data: NonNull<T>,
     marker: PhantomData<&'a mut T>,
 }
 
-impl<'a, T: 'a> Deref for FreezeWriteGuard<'a, T> {
+impl<'a, T> FreezeWriteGuard<'a, T> {
+    pub fn freeze(self) -> &'a T {
+        self.frozen.store(true, Ordering::Release);
+
+        // SAFETY: This is frozen so the data cannot be modified and shared access is sound.
+        unsafe { &*self.data.as_ptr() }
+    }
+}
+
+impl<'a, T: ?Sized> FreezeWriteGuard<'a, T> {
+    #[inline]
+    pub fn map<U: ?Sized>(
+        mut this: Self,
+        f: impl FnOnce(&mut T) -> &mut U,
+    ) -> FreezeWriteGuard<'a, U> {
+        FreezeWriteGuard {
+            data: NonNull::from(f(&mut *this)),
+            _lock_guard: this._lock_guard,
+            frozen: this.frozen,
+            marker: PhantomData,
+        }
+    }
+}
+
+impl<'a, T: ?Sized + 'a> Deref for FreezeWriteGuard<'a, T> {
     type Target = T;
     #[inline]
     fn deref(&self) -> &T {
         // SAFETY: `self._lock_guard` holds the lock to the `UnsafeCell` so this has shared access.
-        unsafe { &*self.lock.data.get() }
+        unsafe { &*self.data.as_ptr() }
     }
 }
 
-impl<'a, T: 'a> DerefMut for FreezeWriteGuard<'a, T> {
+impl<'a, T: ?Sized + 'a> DerefMut for FreezeWriteGuard<'a, T> {
     #[inline]
     fn deref_mut(&mut self) -> &mut T {
         // SAFETY: `self._lock_guard` holds the lock to the `UnsafeCell` so this has mutable access.
-        unsafe { &mut *self.lock.data.get() }
+        unsafe { &mut *self.data.as_ptr() }
     }
 }
diff --git a/compiler/rustc_data_structures/src/sync/lock.rs b/compiler/rustc_data_structures/src/sync/lock.rs
index 62cd1b993de..339aebbf81a 100644
--- a/compiler/rustc_data_structures/src/sync/lock.rs
+++ b/compiler/rustc_data_structures/src/sync/lock.rs
@@ -3,224 +3,229 @@
 //!
 //! When `cfg(parallel_compiler)` is not set, the lock is instead a wrapper around `RefCell`.
 
-#[cfg(not(parallel_compiler))]
-use std::cell::RefCell;
-#[cfg(parallel_compiler)]
-use {
-    crate::cold_path,
-    crate::sync::DynSend,
-    crate::sync::DynSync,
-    parking_lot::lock_api::RawMutex,
-    std::cell::Cell,
-    std::cell::UnsafeCell,
-    std::fmt,
-    std::intrinsics::{likely, unlikely},
-    std::marker::PhantomData,
-    std::mem::ManuallyDrop,
-    std::ops::{Deref, DerefMut},
-};
+#![allow(dead_code)]
 
-#[cfg(not(parallel_compiler))]
-pub use std::cell::RefMut as LockGuard;
+use std::fmt;
 
+#[cfg(parallel_compiler)]
+pub use maybe_sync::*;
 #[cfg(not(parallel_compiler))]
-#[derive(Debug)]
-pub struct Lock<T>(RefCell<T>);
+pub use no_sync::*;
 
-#[cfg(not(parallel_compiler))]
-impl<T> Lock<T> {
-    #[inline(always)]
-    pub fn new(inner: T) -> Self {
-        Lock(RefCell::new(inner))
-    }
+#[derive(Clone, Copy, PartialEq)]
+pub enum Mode {
+    NoSync,
+    Sync,
+}
 
-    #[inline(always)]
-    pub fn into_inner(self) -> T {
-        self.0.into_inner()
+mod maybe_sync {
+    use super::Mode;
+    use crate::sync::mode;
+    #[cfg(parallel_compiler)]
+    use crate::sync::{DynSend, DynSync};
+    use parking_lot::lock_api::RawMutex as _;
+    use parking_lot::RawMutex;
+    use std::cell::Cell;
+    use std::cell::UnsafeCell;
+    use std::intrinsics::unlikely;
+    use std::marker::PhantomData;
+    use std::mem::ManuallyDrop;
+    use std::ops::{Deref, DerefMut};
+
+    /// A guard holding mutable access to a `Lock` which is in a locked state.
+    #[must_use = "if unused the Lock will immediately unlock"]
+    pub struct LockGuard<'a, T> {
+        lock: &'a Lock<T>,
+        marker: PhantomData<&'a mut T>,
+
+        /// The syncronization mode of the lock. This is explicitly passed to let LLVM relate it
+        /// to the original lock operation.
+        mode: Mode,
     }
 
-    #[inline(always)]
-    pub fn get_mut(&mut self) -> &mut T {
-        self.0.get_mut()
+    impl<'a, T: 'a> Deref for LockGuard<'a, T> {
+        type Target = T;
+        #[inline]
+        fn deref(&self) -> &T {
+            // SAFETY: We have shared access to the mutable access owned by this type,
+            // so we can give out a shared reference.
+            unsafe { &*self.lock.data.get() }
+        }
     }
 
-    #[inline(always)]
-    pub fn try_lock(&self) -> Option<LockGuard<'_, T>> {
-        self.0.try_borrow_mut().ok()
+    impl<'a, T: 'a> DerefMut for LockGuard<'a, T> {
+        #[inline]
+        fn deref_mut(&mut self) -> &mut T {
+            // SAFETY: We have mutable access to the data so we can give out a mutable reference.
+            unsafe { &mut *self.lock.data.get() }
+        }
     }
 
-    #[inline(always)]
-    #[track_caller]
-    pub fn lock(&self) -> LockGuard<'_, T> {
-        self.0.borrow_mut()
+    impl<'a, T: 'a> Drop for LockGuard<'a, T> {
+        #[inline]
+        fn drop(&mut self) {
+            // SAFETY (union access): We get `self.mode` from the lock operation so it is consistent
+            // with the `lock.mode` state. This means we access the right union fields.
+            match self.mode {
+                Mode::NoSync => {
+                    let cell = unsafe { &self.lock.mode_union.no_sync };
+                    debug_assert_eq!(cell.get(), true);
+                    cell.set(false);
+                }
+                // SAFETY (unlock): We know that the lock is locked as this type is a proof of that.
+                Mode::Sync => unsafe { self.lock.mode_union.sync.unlock() },
+            }
+        }
     }
-}
 
-/// A guard holding mutable access to a `Lock` which is in a locked state.
-#[cfg(parallel_compiler)]
-#[must_use = "if unused the Lock will immediately unlock"]
-pub struct LockGuard<'a, T> {
-    lock: &'a Lock<T>,
-    marker: PhantomData<&'a mut T>,
-}
+    union ModeUnion {
+        /// Indicates if the cell is locked. Only used if `Lock.mode` is `NoSync`.
+        no_sync: ManuallyDrop<Cell<bool>>,
 
-#[cfg(parallel_compiler)]
-impl<'a, T: 'a> Deref for LockGuard<'a, T> {
-    type Target = T;
-    #[inline]
-    fn deref(&self) -> &T {
-        // SAFETY: We have shared access to the mutable access owned by this type,
-        // so we can give out a shared reference.
-        unsafe { &*self.lock.data.get() }
+        /// A lock implementation that's only used if `Lock.mode` is `Sync`.
+        sync: ManuallyDrop<RawMutex>,
     }
-}
 
-#[cfg(parallel_compiler)]
-impl<'a, T: 'a> DerefMut for LockGuard<'a, T> {
-    #[inline]
-    fn deref_mut(&mut self) -> &mut T {
-        // SAFETY: We have mutable access to the data so we can give out a mutable reference.
-        unsafe { &mut *self.lock.data.get() }
-    }
-}
+    /// The value representing a locked state for the `Cell`.
+    const LOCKED: bool = true;
 
-#[cfg(parallel_compiler)]
-impl<'a, T: 'a> Drop for LockGuard<'a, T> {
-    #[inline]
-    fn drop(&mut self) {
-        // SAFETY: We know that the lock is in a locked
-        // state because it is a invariant of this type.
-        unsafe { self.lock.raw.unlock() };
-    }
-}
+    /// A lock which only uses synchronization if `might_be_dyn_thread_safe` is true.
+    /// It implements `DynSend` and `DynSync` instead of the typical `Send` and `Sync`.
+    pub struct Lock<T> {
+        /// Indicates if synchronization is used via `mode_union.sync` if it's `Sync`, or if a
+        /// not thread safe cell is used via `mode_union.no_sync` if it's `NoSync`.
+        /// This is set on initialization and never changed.
+        mode: Mode,
 
-#[cfg(parallel_compiler)]
-union LockRawUnion {
-    /// Indicates if the cell is locked. Only used if `LockRaw.sync` is false.
-    cell: ManuallyDrop<Cell<bool>>,
+        mode_union: ModeUnion,
+        data: UnsafeCell<T>,
+    }
 
-    /// A lock implementation that's only used if `LockRaw.sync` is true.
-    lock: ManuallyDrop<parking_lot::RawMutex>,
-}
+    impl<T> Lock<T> {
+        #[inline(always)]
+        pub fn new(inner: T) -> Self {
+            let (mode, mode_union) = if unlikely(mode::might_be_dyn_thread_safe()) {
+                // Create the lock with synchronization enabled using the `RawMutex` type.
+                (Mode::Sync, ModeUnion { sync: ManuallyDrop::new(RawMutex::INIT) })
+            } else {
+                // Create the lock with synchronization disabled.
+                (Mode::NoSync, ModeUnion { no_sync: ManuallyDrop::new(Cell::new(!LOCKED)) })
+            };
+            Lock { mode, mode_union, data: UnsafeCell::new(inner) }
+        }
 
-/// A raw lock which only uses synchronization if `might_be_dyn_thread_safe` is true.
-/// It contains no associated data and is used in the implementation of `Lock` which does have such data.
-///
-/// A manual implementation of a tagged union is used with the `sync` field and the `LockRawUnion` instead
-/// of using enums as it results in better code generation.
-#[cfg(parallel_compiler)]
-struct LockRaw {
-    /// Indicates if synchronization is used via `opt.lock` if true,
-    /// or if a non-thread safe cell is used via `opt.cell`. This is set on initialization and never changed.
-    sync: bool,
-    opt: LockRawUnion,
-}
+        #[inline(always)]
+        pub fn into_inner(self) -> T {
+            self.data.into_inner()
+        }
 
-#[cfg(parallel_compiler)]
-impl LockRaw {
-    fn new() -> Self {
-        if unlikely(super::mode::might_be_dyn_thread_safe()) {
-            // Create the lock with synchronization enabled using the `RawMutex` type.
-            LockRaw {
-                sync: true,
-                opt: LockRawUnion { lock: ManuallyDrop::new(parking_lot::RawMutex::INIT) },
-            }
-        } else {
-            // Create the lock with synchronization disabled.
-            LockRaw { sync: false, opt: LockRawUnion { cell: ManuallyDrop::new(Cell::new(false)) } }
+        #[inline(always)]
+        pub fn get_mut(&mut self) -> &mut T {
+            self.data.get_mut()
         }
-    }
 
-    #[inline(always)]
-    fn try_lock(&self) -> bool {
-        // SAFETY: This is safe since the union fields are used in accordance with `self.sync`.
-        unsafe {
-            if likely(!self.sync) {
-                if self.opt.cell.get() {
-                    false
-                } else {
-                    self.opt.cell.set(true);
-                    true
+        #[inline(always)]
+        pub fn try_lock(&self) -> Option<LockGuard<'_, T>> {
+            let mode = self.mode;
+            // SAFETY: This is safe since the union fields are used in accordance with `self.mode`.
+            match mode {
+                Mode::NoSync => {
+                    let cell = unsafe { &self.mode_union.no_sync };
+                    let was_unlocked = cell.get() != LOCKED;
+                    if was_unlocked {
+                        cell.set(LOCKED);
+                    }
+                    was_unlocked
                 }
-            } else {
-                self.opt.lock.try_lock()
+                Mode::Sync => unsafe { self.mode_union.sync.try_lock() },
             }
+            .then(|| LockGuard { lock: self, marker: PhantomData, mode })
         }
-    }
 
-    #[inline(always)]
-    fn lock(&self) {
-        if super::ERROR_CHECKING {
-            // We're in the debugging mode, so assert that the lock is not held so we
-            // get a panic instead of waiting for the lock.
-            assert_eq!(self.try_lock(), true, "lock must not be hold");
-        } else {
-            // SAFETY: This is safe since the union fields are used in accordance with `self.sync`.
+        /// This acquires the lock assuming syncronization is in a specific mode.
+        ///
+        /// Safety
+        /// This method must only be called with `Mode::Sync` if `might_be_dyn_thread_safe` was
+        /// true on lock creation.
+        #[inline(always)]
+        #[track_caller]
+        pub unsafe fn lock_assume(&self, mode: Mode) -> LockGuard<'_, T> {
+            #[inline(never)]
+            #[track_caller]
+            #[cold]
+            fn lock_held() -> ! {
+                panic!("lock was already held")
+            }
+
+            // SAFETY: This is safe since the union fields are used in accordance with `mode`
+            // which also must match `self.mode` due to the safety precondition.
             unsafe {
-                if likely(!self.sync) {
-                    if unlikely(self.opt.cell.replace(true)) {
-                        cold_path(|| panic!("lock was already held"))
+                match mode {
+                    Mode::NoSync => {
+                        if unlikely(self.mode_union.no_sync.replace(LOCKED) == LOCKED) {
+                            lock_held()
+                        }
                     }
-                } else {
-                    self.opt.lock.lock();
+                    Mode::Sync => self.mode_union.sync.lock(),
                 }
             }
+            LockGuard { lock: self, marker: PhantomData, mode }
         }
-    }
 
-    /// This unlocks the lock.
-    ///
-    /// Safety
-    /// This method may only be called if the lock is currently held.
-    #[inline(always)]
-    unsafe fn unlock(&self) {
-        // SAFETY: The union use is safe since the union fields are used in accordance with
-        // `self.sync` and the `unlock` method precondition is upheld by the caller.
-        unsafe {
-            if likely(!self.sync) {
-                debug_assert_eq!(self.opt.cell.get(), true);
-                self.opt.cell.set(false);
-            } else {
-                self.opt.lock.unlock();
-            }
+        #[inline(always)]
+        #[track_caller]
+        pub fn lock(&self) -> LockGuard<'_, T> {
+            unsafe { self.lock_assume(self.mode) }
         }
     }
-}
 
-/// A lock which only uses synchronization if `might_be_dyn_thread_safe` is true.
-/// It implements `DynSend` and `DynSync` instead of the typical `Send` and `Sync`.
-#[cfg(parallel_compiler)]
-pub struct Lock<T> {
-    raw: LockRaw,
-    data: UnsafeCell<T>,
+    #[cfg(parallel_compiler)]
+    unsafe impl<T: DynSend> DynSend for Lock<T> {}
+    #[cfg(parallel_compiler)]
+    unsafe impl<T: DynSend> DynSync for Lock<T> {}
 }
 
-#[cfg(parallel_compiler)]
-impl<T> Lock<T> {
-    #[inline(always)]
-    pub fn new(inner: T) -> Self {
-        Lock { raw: LockRaw::new(), data: UnsafeCell::new(inner) }
-    }
+mod no_sync {
+    use super::Mode;
+    use std::cell::RefCell;
 
-    #[inline(always)]
-    pub fn into_inner(self) -> T {
-        self.data.into_inner()
-    }
+    pub use std::cell::RefMut as LockGuard;
 
-    #[inline(always)]
-    pub fn get_mut(&mut self) -> &mut T {
-        self.data.get_mut()
-    }
+    pub struct Lock<T>(RefCell<T>);
 
-    #[inline(always)]
-    pub fn try_lock(&self) -> Option<LockGuard<'_, T>> {
-        if self.raw.try_lock() { Some(LockGuard { lock: self, marker: PhantomData }) } else { None }
-    }
+    impl<T> Lock<T> {
+        #[inline(always)]
+        pub fn new(inner: T) -> Self {
+            Lock(RefCell::new(inner))
+        }
 
-    #[inline(always)]
-    pub fn lock(&self) -> LockGuard<'_, T> {
-        self.raw.lock();
-        LockGuard { lock: self, marker: PhantomData }
+        #[inline(always)]
+        pub fn into_inner(self) -> T {
+            self.0.into_inner()
+        }
+
+        #[inline(always)]
+        pub fn get_mut(&mut self) -> &mut T {
+            self.0.get_mut()
+        }
+
+        #[inline(always)]
+        pub fn try_lock(&self) -> Option<LockGuard<'_, T>> {
+            self.0.try_borrow_mut().ok()
+        }
+
+        #[inline(always)]
+        #[track_caller]
+        // This is unsafe to match the API for the `parallel_compiler` case.
+        pub unsafe fn lock_assume(&self, _mode: Mode) -> LockGuard<'_, T> {
+            self.0.borrow_mut()
+        }
+
+        #[inline(always)]
+        #[track_caller]
+        pub fn lock(&self) -> LockGuard<'_, T> {
+            self.0.borrow_mut()
+        }
     }
 }
 
@@ -244,12 +249,13 @@ impl<T> Lock<T> {
     }
 }
 
-#[cfg(parallel_compiler)]
-unsafe impl<T: DynSend> DynSend for Lock<T> {}
-#[cfg(parallel_compiler)]
-unsafe impl<T: DynSend> DynSync for Lock<T> {}
+impl<T: Default> Default for Lock<T> {
+    #[inline]
+    fn default() -> Self {
+        Lock::new(T::default())
+    }
+}
 
-#[cfg(parallel_compiler)]
 impl<T: fmt::Debug> fmt::Debug for Lock<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self.try_lock() {
@@ -267,10 +273,3 @@ impl<T: fmt::Debug> fmt::Debug for Lock<T> {
         }
     }
 }
-
-impl<T: Default> Default for Lock<T> {
-    #[inline]
-    fn default() -> Self {
-        Lock::new(T::default())
-    }
-}
diff --git a/compiler/rustc_data_structures/src/sync/parallel.rs b/compiler/rustc_data_structures/src/sync/parallel.rs
new file mode 100644
index 00000000000..1944ddfb710
--- /dev/null
+++ b/compiler/rustc_data_structures/src/sync/parallel.rs
@@ -0,0 +1,188 @@
+//! This module defines parallel operations that are implemented in
+//! one way for the serial compiler, and another way the parallel compiler.
+
+#![allow(dead_code)]
+
+use parking_lot::Mutex;
+use std::any::Any;
+use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
+
+#[cfg(not(parallel_compiler))]
+pub use disabled::*;
+#[cfg(parallel_compiler)]
+pub use enabled::*;
+
+/// A guard used to hold panics that occur during a parallel section to later by unwound.
+/// This is used for the parallel compiler to prevent fatal errors from non-deterministically
+/// hiding errors by ensuring that everything in the section has completed executing before
+/// continuing with unwinding. It's also used for the non-parallel code to ensure error message
+/// output match the parallel compiler for testing purposes.
+pub struct ParallelGuard {
+    panic: Mutex<Option<Box<dyn Any + Send + 'static>>>,
+}
+
+impl ParallelGuard {
+    pub fn run<R>(&self, f: impl FnOnce() -> R) -> Option<R> {
+        catch_unwind(AssertUnwindSafe(f))
+            .map_err(|err| {
+                *self.panic.lock() = Some(err);
+            })
+            .ok()
+    }
+}
+
+/// This gives access to a fresh parallel guard in the closure and will unwind any panics
+/// caught in it after the closure returns.
+#[inline]
+pub fn parallel_guard<R>(f: impl FnOnce(&ParallelGuard) -> R) -> R {
+    let guard = ParallelGuard { panic: Mutex::new(None) };
+    let ret = f(&guard);
+    if let Some(panic) = guard.panic.into_inner() {
+        resume_unwind(panic);
+    }
+    ret
+}
+
+mod disabled {
+    use crate::sync::parallel_guard;
+
+    #[macro_export]
+    #[cfg(not(parallel_compiler))]
+    macro_rules! parallel {
+        ($($blocks:block),*) => {{
+            $crate::sync::parallel_guard(|guard| {
+                $(guard.run(|| $blocks);)*
+            });
+        }}
+    }
+
+    pub fn join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
+    where
+        A: FnOnce() -> RA,
+        B: FnOnce() -> RB,
+    {
+        let (a, b) = parallel_guard(|guard| {
+            let a = guard.run(oper_a);
+            let b = guard.run(oper_b);
+            (a, b)
+        });
+        (a.unwrap(), b.unwrap())
+    }
+
+    pub fn par_for_each_in<T: IntoIterator>(t: T, mut for_each: impl FnMut(T::Item)) {
+        parallel_guard(|guard| {
+            t.into_iter().for_each(|i| {
+                guard.run(|| for_each(i));
+            });
+        })
+    }
+
+    pub fn par_map<T: IntoIterator, R, C: FromIterator<R>>(
+        t: T,
+        mut map: impl FnMut(<<T as IntoIterator>::IntoIter as Iterator>::Item) -> R,
+    ) -> C {
+        parallel_guard(|guard| t.into_iter().filter_map(|i| guard.run(|| map(i))).collect())
+    }
+}
+
+#[cfg(parallel_compiler)]
+mod enabled {
+    use crate::sync::{mode, parallel_guard, DynSend, DynSync, FromDyn};
+
+    /// Runs a list of blocks in parallel. The first block is executed immediately on
+    /// the current thread. Use that for the longest running block.
+    #[macro_export]
+    macro_rules! parallel {
+        (impl $fblock:block [$($c:expr,)*] [$block:expr $(, $rest:expr)*]) => {
+            parallel!(impl $fblock [$block, $($c,)*] [$($rest),*])
+        };
+        (impl $fblock:block [$($blocks:expr,)*] []) => {
+            ::rustc_data_structures::sync::scope(|s| {
+                $(let block = rustc_data_structures::sync::FromDyn::from(|| $blocks);
+                s.spawn(move |_| block.into_inner()());)*
+                (|| $fblock)();
+            });
+        };
+        ($fblock:block, $($blocks:block),*) => {
+            if rustc_data_structures::sync::is_dyn_thread_safe() {
+                // Reverse the order of the later blocks since Rayon executes them in reverse order
+                // when using a single thread. This ensures the execution order matches that
+                // of a single threaded rustc.
+                parallel!(impl $fblock [] [$($blocks),*]);
+            } else {
+                $crate::sync::parallel_guard(|guard| {
+                    guard.run(|| $fblock);
+                    $(guard.run(|| $blocks);)*
+                });
+            }
+        };
+    }
+
+    // This function only works when `mode::is_dyn_thread_safe()`.
+    pub fn scope<'scope, OP, R>(op: OP) -> R
+    where
+        OP: FnOnce(&rayon::Scope<'scope>) -> R + DynSend,
+        R: DynSend,
+    {
+        let op = FromDyn::from(op);
+        rayon::scope(|s| FromDyn::from(op.into_inner()(s))).into_inner()
+    }
+
+    #[inline]
+    pub fn join<A, B, RA: DynSend, RB: DynSend>(oper_a: A, oper_b: B) -> (RA, RB)
+    where
+        A: FnOnce() -> RA + DynSend,
+        B: FnOnce() -> RB + DynSend,
+    {
+        if mode::is_dyn_thread_safe() {
+            let oper_a = FromDyn::from(oper_a);
+            let oper_b = FromDyn::from(oper_b);
+            let (a, b) = rayon::join(
+                move || FromDyn::from(oper_a.into_inner()()),
+                move || FromDyn::from(oper_b.into_inner()()),
+            );
+            (a.into_inner(), b.into_inner())
+        } else {
+            super::disabled::join(oper_a, oper_b)
+        }
+    }
+
+    use rayon::iter::{FromParallelIterator, IntoParallelIterator, ParallelIterator};
+
+    pub fn par_for_each_in<I, T: IntoIterator<Item = I> + IntoParallelIterator<Item = I>>(
+        t: T,
+        for_each: impl Fn(I) + DynSync + DynSend,
+    ) {
+        parallel_guard(|guard| {
+            if mode::is_dyn_thread_safe() {
+                let for_each = FromDyn::from(for_each);
+                t.into_par_iter().for_each(|i| {
+                    guard.run(|| for_each(i));
+                });
+            } else {
+                t.into_iter().for_each(|i| {
+                    guard.run(|| for_each(i));
+                });
+            }
+        });
+    }
+
+    pub fn par_map<
+        I,
+        T: IntoIterator<Item = I> + IntoParallelIterator<Item = I>,
+        R: std::marker::Send,
+        C: FromIterator<R> + FromParallelIterator<R>,
+    >(
+        t: T,
+        map: impl Fn(I) -> R + DynSync + DynSend,
+    ) -> C {
+        parallel_guard(|guard| {
+            if mode::is_dyn_thread_safe() {
+                let map = FromDyn::from(map);
+                t.into_par_iter().filter_map(|i| guard.run(|| map(i))).collect()
+            } else {
+                t.into_iter().filter_map(|i| guard.run(|| map(i))).collect()
+            }
+        })
+    }
+}
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 1a16759d7f9..c02ae70166d 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -162,9 +162,10 @@ pub fn abort_on_err<T>(result: Result<T, ErrorGuaranteed>, sess: &Session) -> T
 pub trait Callbacks {
     /// Called before creating the compiler instance
     fn config(&mut self, _config: &mut interface::Config) {}
-    /// Called after parsing. Return value instructs the compiler whether to
+    /// Called after parsing the crate root. Submodules are not yet parsed when
+    /// this callback is called. Return value instructs the compiler whether to
     /// continue the compilation afterwards (defaults to `Compilation::Continue`)
-    fn after_parsing<'tcx>(
+    fn after_crate_root_parsing<'tcx>(
         &mut self,
         _compiler: &interface::Compiler,
         _queries: &'tcx Queries<'tcx>,
@@ -184,7 +185,6 @@ pub trait Callbacks {
     /// continue the compilation afterwards (defaults to `Compilation::Continue`)
     fn after_analysis<'tcx>(
         &mut self,
-        _handler: &EarlyErrorHandler,
         _compiler: &interface::Compiler,
         _queries: &'tcx Queries<'tcx>,
     ) -> Compilation {
@@ -313,6 +313,7 @@ fn run_compiler(
         override_queries: None,
         make_codegen_backend,
         registry: diagnostics_registry(),
+        expanded_args: args,
     };
 
     match make_input(&early_error_handler, &matches.free) {
@@ -406,7 +407,7 @@ fn run_compiler(
                 return early_exit();
             }
 
-            if callbacks.after_parsing(compiler, queries) == Compilation::Stop {
+            if callbacks.after_crate_root_parsing(compiler, queries) == Compilation::Stop {
                 return early_exit();
             }
 
@@ -444,7 +445,7 @@ fn run_compiler(
 
             queries.global_ctxt()?.enter(|tcx| tcx.analysis(()))?;
 
-            if callbacks.after_analysis(&handler, compiler, queries) == Compilation::Stop {
+            if callbacks.after_analysis(compiler, queries) == Compilation::Stop {
                 return early_exit();
             }
 
@@ -699,12 +700,14 @@ pub fn list_metadata(
     sess: &Session,
     metadata_loader: &dyn MetadataLoader,
 ) -> Compilation {
-    if sess.opts.unstable_opts.ls {
+    let ls_kinds = &sess.opts.unstable_opts.ls;
+    if !ls_kinds.is_empty() {
         match sess.io.input {
             Input::File(ref ifile) => {
                 let path = &(*ifile);
                 let mut v = Vec::new();
-                locator::list_file_metadata(&sess.target, path, metadata_loader, &mut v).unwrap();
+                locator::list_file_metadata(&sess.target, path, metadata_loader, &mut v, ls_kinds)
+                    .unwrap();
                 safe_println!("{}", String::from_utf8(v).unwrap());
             }
             Input::Str { .. } => {
diff --git a/compiler/rustc_error_codes/src/error_codes/E0401.md b/compiler/rustc_error_codes/src/error_codes/E0401.md
index 4c93053d5f8..45d083681e5 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0401.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0401.md
@@ -1,4 +1,4 @@
-Inner items do not inherit type or const parameters from the functions
+Inner items do not inherit the generic parameters from the items
 they are embedded in.
 
 Erroneous code example:
@@ -32,8 +32,8 @@ fn foo<T>(x: T) {
 }
 ```
 
-Items inside functions are basically just like top-level items, except
-that they can only be used from the function they are in.
+Items nested inside other items are basically just like top-level items, except
+that they can only be used from the item they are in.
 
 There are a couple of solutions for this.
 
diff --git a/compiler/rustc_error_codes/src/error_codes/E0788.md b/compiler/rustc_error_codes/src/error_codes/E0788.md
index d26f9b59455..d655e51fa66 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0788.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0788.md
@@ -1,4 +1,4 @@
-A `#[no_coverage]` attribute was applied to something which does not show up
+A `#[coverage]` attribute was applied to something which does not show up
 in code coverage, or is too granular to be excluded from the coverage report.
 
 For now, this attribute can only be applied to function, method, and closure
@@ -9,18 +9,18 @@ will just emit an `unused_attributes` lint instead of this error.
 Example of erroneous code:
 
 ```compile_fail,E0788
-#[no_coverage]
+#[coverage(off)]
 struct Foo;
 
-#[no_coverage]
+#[coverage(on)]
 const FOO: Foo = Foo;
 ```
 
-`#[no_coverage]` tells the compiler to not generate coverage instrumentation for
-a piece of code when the `-C instrument-coverage` flag is passed. Things like
-structs and consts are not coverable code, and thus cannot do anything with this
-attribute.
+`#[coverage(off)]` tells the compiler to not generate coverage instrumentation
+for a piece of code when the `-C instrument-coverage` flag is passed. Things
+like structs and consts are not coverable code, and thus cannot do anything
+with this attribute.
 
 If you wish to apply this attribute to all methods in an impl or module,
 manually annotate each method; it is not possible to annotate the entire impl
-with a `#[no_coverage]` attribute.
+with a `#[coverage]` attribute.
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
index d7a008f9a57..5d3b2f45166 100644
--- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
+++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -169,7 +169,7 @@ impl AnnotateSnippetEmitterWriter {
                         .map(|line| {
                             // Ensure the source file is present before we try
                             // to load a string from it.
-                            source_map.ensure_source_file_source_present(file.clone());
+                            source_map.ensure_source_file_source_present(&file);
                             (
                                 format!("{}", source_map.filename_for_diagnostics(&file.name)),
                                 source_string(file.clone(), &line),
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index da108327ae7..58be74f887b 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -1193,7 +1193,7 @@ impl EmitterWriter {
         let will_be_emitted = |span: Span| {
             !span.is_dummy() && {
                 let file = sm.lookup_source_file(span.hi());
-                sm.ensure_source_file_source_present(file)
+                sm.ensure_source_file_source_present(&file)
             }
         };
 
@@ -1388,7 +1388,7 @@ impl EmitterWriter {
         // Print out the annotate source lines that correspond with the error
         for annotated_file in annotated_files {
             // we can't annotate anything if the source is unavailable.
-            if !sm.ensure_source_file_source_present(annotated_file.file.clone()) {
+            if !sm.ensure_source_file_source_present(&annotated_file.file) {
                 if !self.short_message {
                     // We'll just print an unannotated message.
                     for (annotation_id, line) in annotated_file.lines.iter().enumerate() {
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index 390bf28df09..38667c5ff81 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -558,7 +558,7 @@ impl DiagnosticSpanLine {
             .span_to_lines(span)
             .map(|lines| {
                 // We can't get any lines if the source is unavailable.
-                if !je.sm.ensure_source_file_source_present(lines.file.clone()) {
+                if !je.sm.ensure_source_file_source_present(&lines.file) {
                     return vec![];
                 }
 
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 55c4ec66cd9..990bd2d1cc9 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -44,7 +44,7 @@ use rustc_fluent_macro::fluent_messages;
 pub use rustc_lint_defs::{pluralize, Applicability};
 use rustc_span::source_map::SourceMap;
 pub use rustc_span::ErrorGuaranteed;
-use rustc_span::{Loc, Span};
+use rustc_span::{Loc, Span, DUMMY_SP};
 
 use std::borrow::Cow;
 use std::error::Report;
@@ -273,7 +273,7 @@ impl CodeSuggestion {
                 assert!(!lines.lines.is_empty() || bounding_span.is_dummy());
 
                 // We can't splice anything if the source is unavailable.
-                if !sm.ensure_source_file_source_present(lines.file.clone()) {
+                if !sm.ensure_source_file_source_present(&lines.file) {
                     return None;
                 }
 
@@ -1754,7 +1754,7 @@ impl DelayedDiagnostic {
             BacktraceStatus::Captured => {
                 let inner = &self.inner;
                 self.inner.subdiagnostic(DelayedAtWithNewline {
-                    span: inner.span.primary_span().unwrap(),
+                    span: inner.span.primary_span().unwrap_or(DUMMY_SP),
                     emitted_at: inner.emitted_at.clone(),
                     note: self.note,
                 });
@@ -1764,7 +1764,7 @@ impl DelayedDiagnostic {
             _ => {
                 let inner = &self.inner;
                 self.inner.subdiagnostic(DelayedAtWithoutNewline {
-                    span: inner.span.primary_span().unwrap(),
+                    span: inner.span.primary_span().unwrap_or(DUMMY_SP),
                     emitted_at: inner.emitted_at.clone(),
                     note: self.note,
                 });
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 34d16bf00cd..f87f4aba2b9 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -587,7 +587,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 .resolver
                 .visit_ast_fragment_with_placeholders(self.cx.current_expansion.id, &fragment);
 
-            if self.cx.sess.opts.incremental_relative_spans() {
+            if self.cx.sess.opts.incremental.is_some() {
                 for (invoc, _) in invocations.iter_mut() {
                     let expn_id = invoc.expansion_data.id;
                     let parent_def = self.cx.resolver.invocation_parent(expn_id);
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 1941390dc4a..6c338be99b6 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -398,6 +398,11 @@ declare_features! (
     (active, const_trait_impl, "1.42.0", Some(67792), None),
     /// Allows the `?` operator in const contexts.
     (active, const_try, "1.56.0", Some(74935), None),
+    /// Allows function attribute `#[coverage(on/off)]`, to control coverage
+    /// instrumentation of that function.
+    (active, coverage_attribute, "CURRENT_RUSTC_VERSION", Some(84605), None),
+    /// Allows users to provide classes for fenced code block using `class:classname`.
+    (active, custom_code_classes_in_docs, "CURRENT_RUSTC_VERSION", Some(79483), None),
     /// Allows non-builtin attributes in inner attribute position.
     (active, custom_inner_attributes, "1.30.0", Some(54726), None),
     /// Allows custom test frameworks with `#![test_runner]` and `#[test_case]`.
@@ -509,9 +514,6 @@ declare_features! (
     (active, never_type_fallback, "1.41.0", Some(65992), None),
     /// Allows `#![no_core]`.
     (active, no_core, "1.3.0", Some(29639), None),
-    /// Allows function attribute `#[no_coverage]`, to bypass coverage
-    /// instrumentation of that function.
-    (active, no_coverage, "1.53.0", Some(84605), None),
     /// Allows the use of `no_sanitize` attribute.
     (active, no_sanitize, "1.42.0", Some(39699), None),
     /// Allows using the `non_exhaustive_omitted_patterns` lint.
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index e745ef1ec07..04ebe22a9eb 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -395,7 +395,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         template!(List: "address, kcfi, memory, thread"), DuplicatesOk,
         experimental!(no_sanitize)
     ),
-    gated!(no_coverage, Normal, template!(Word), WarnFollowing, experimental!(no_coverage)),
+    gated!(coverage, Normal, template!(Word, List: "on|off"), WarnFollowing, coverage_attribute, experimental!(coverage)),
 
     ungated!(
         doc, Normal, template!(List: "hidden|inline|...", NameValueStr: "string"), DuplicatesOk
@@ -700,6 +700,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         "#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference."
     ),
     rustc_attr!(
+        rustc_never_returns_null_ptr, Normal, template!(Word), ErrorFollowing,
+        "#[rustc_never_returns_null_ptr] is used to mark functions returning non-null pointers."
+    ),
+    rustc_attr!(
         rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, @only_local: true,
         "#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`."
     ),
diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs
index ed5d76b861a..da18cb2a239 100644
--- a/compiler/rustc_feature/src/removed.rs
+++ b/compiler/rustc_feature/src/removed.rs
@@ -136,6 +136,9 @@ declare_features! (
      Some("subsumed by `#![feature(allocator_internals)]`")),
     /// Allows use of unary negate on unsigned integers, e.g., -e for e: u8
     (removed, negate_unsigned, "1.0.0", Some(29645), None, None),
+    /// Allows `#[no_coverage]` on functions.
+    /// The feature was renamed to `coverage_attribute` and the attribute to `#[coverage(on|off)]`
+    (removed, no_coverage, "CURRENT_RUSTC_VERSION", Some(84605), None, Some("renamed to `coverage_attribute`")),
     /// Allows `#[no_debug]`.
     (removed, no_debug, "1.43.0", Some(29721), None, Some("removed due to lack of demand")),
     /// Allows using `#[on_unimplemented(..)]` on traits.
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index a9b25a390b3..cb78da42597 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -19,6 +19,7 @@ use rustc_macros::HashStable_Generic;
 use rustc_span::hygiene::MacroKind;
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
+use rustc_span::ErrorGuaranteed;
 use rustc_span::{def_id::LocalDefId, BytePos, Span, DUMMY_SP};
 use rustc_target::asm::InlineAsmRegOrRegClass;
 use rustc_target::spec::abi::Abi;
@@ -1415,6 +1416,9 @@ pub struct Let<'hir> {
     pub pat: &'hir Pat<'hir>,
     pub ty: Option<&'hir Ty<'hir>>,
     pub init: &'hir Expr<'hir>,
+    /// `Some` when this let expressions is not in a syntanctically valid location.
+    /// Used to prevent building MIR in such situations.
+    pub is_recovered: Option<ErrorGuaranteed>,
 }
 
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 597cae6ff33..1a38657dff4 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -95,6 +95,34 @@ hir_analysis_impl_not_marked_default = `{$ident}` specializes an item from a par
 hir_analysis_impl_not_marked_default_err = `{$ident}` specializes an item from a parent `impl`, but that item is not marked `default`
     .note = parent implementation is in crate `{$cname}`
 
+hir_analysis_inherent_dyn = cannot define inherent `impl` for a dyn auto trait
+    .label = impl requires at least one non-auto trait
+    .note = define and implement a new trait or type instead
+
+hir_analysis_inherent_nominal = no nominal type found for inherent implementation
+    .label = impl requires a nominal type
+    .note = either implement a trait on it or create a newtype to wrap it instead
+hir_analysis_inherent_primitive_ty = cannot define inherent `impl` for primitive types
+    .help = consider using an extension trait instead
+
+hir_analysis_inherent_primitive_ty_note = you could also try moving the reference to uses of `{$subty}` (such as `self`) within the implementation
+
+hir_analysis_inherent_ty_outside = cannot define inherent `impl` for a type outside of the crate where the type is defined
+    .help = consider moving this inherent impl into the crate defining the type if possible
+    .span_help = alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type and `#[rustc_allow_incoherent_impl]` to the relevant impl items
+
+hir_analysis_inherent_ty_outside_new = cannot define inherent `impl` for a type outside of the crate where the type is defined
+    .label = impl for type defined outside of crate.
+    .note = define and implement a trait or new type instead
+
+hir_analysis_inherent_ty_outside_primitive = cannot define inherent `impl` for primitive types outside of `core`
+    .help = consider moving this inherent impl into `core` if possible
+    .span_help = alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items
+
+hir_analysis_inherent_ty_outside_relevant = cannot define inherent `impl` for a type outside of the crate where the type is defined
+    .help = consider moving this inherent impl into the crate defining the type if possible
+    .span_help = alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items
+
 hir_analysis_invalid_union_field =
     field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
     .note = union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`
@@ -222,6 +250,12 @@ hir_analysis_return_type_notation_on_non_rpitit =
     .note = function returns `{$ty}`, which is not compatible with associated type return bounds
     .label = this function must be `async` or return `impl Trait`
 
+hir_analysis_rpitit_refined = impl trait in impl method signature does not match trait method signature
+    .suggestion = replace the return type so that it matches the trait
+    .label = return type from trait method defined here
+    .unmatched_bound_label = this bound is stronger than that defined on the trait
+    .note = add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
+
 hir_analysis_self_in_impl_self =
     `Self` is not valid in the self type of an impl block
     .note = replace `Self` with a different type
diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs
index 6082d446979..ed4dde419c4 100644
--- a/compiler/rustc_hir_analysis/src/astconv/errors.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs
@@ -110,16 +110,22 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
     {
         // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a
         // valid span, so we point at the whole path segment instead.
-        let span = if assoc_name.span != DUMMY_SP { assoc_name.span } else { span };
+        let is_dummy = assoc_name.span == DUMMY_SP;
+
         let mut err = struct_span_err!(
             self.tcx().sess,
-            span,
+            if is_dummy { span } else { assoc_name.span },
             E0220,
             "associated type `{}` not found for `{}`",
             assoc_name,
             ty_param_name
         );
 
+        if is_dummy {
+            err.span_label(span, format!("associated type `{assoc_name}` not found"));
+            return err.emit();
+        }
+
         let all_candidate_names: Vec<_> = all_candidates()
             .flat_map(|r| self.tcx().associated_items(r.def_id()).in_definition_order())
             .filter_map(|item| {
@@ -131,10 +137,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             })
             .collect();
 
-        if let (Some(suggested_name), true) = (
-            find_best_match_for_name(&all_candidate_names, assoc_name.name, None),
-            assoc_name.span != DUMMY_SP,
-        ) {
+        if let Some(suggested_name) =
+            find_best_match_for_name(&all_candidate_names, assoc_name.name, None)
+        {
             err.span_suggestion(
                 assoc_name.span,
                 "there is an associated type with a similar name",
@@ -172,10 +177,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             })
             .collect();
 
-        if let (Some(suggested_name), true) = (
-            find_best_match_for_name(&wider_candidate_names, assoc_name.name, None),
-            assoc_name.span != DUMMY_SP,
-        ) {
+        if let Some(suggested_name) =
+            find_best_match_for_name(&wider_candidate_names, assoc_name.name, None)
+        {
             if let [best_trait] = visible_traits
                 .iter()
                 .filter(|trait_def_id| {
@@ -197,7 +201,28 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             }
         }
 
-        err.span_label(span, format!("associated type `{assoc_name}` not found"));
+        // If we still couldn't find any associated type, and only one associated type exists,
+        // suggests using it.
+
+        if all_candidate_names.len() == 1 {
+            // this should still compile, except on `#![feature(associated_type_defaults)]`
+            // where it could suggests `type A = Self::A`, thus recursing infinitely
+            let applicability = if self.tcx().features().associated_type_defaults {
+                Applicability::Unspecified
+            } else {
+                Applicability::MaybeIncorrect
+            };
+
+            err.span_suggestion(
+                assoc_name.span,
+                format!("`{ty_param_name}` has the following associated type"),
+                all_candidate_names.first().unwrap().to_string(),
+                applicability,
+            );
+        } else {
+            err.span_label(assoc_name.span, format!("associated type `{assoc_name}` not found"));
+        }
+
         err.emit()
     }
 
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index 90f64e18632..acfd8dcb112 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -523,7 +523,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                             Ty::new_misc_error(tcx).into()
                         }
                     }
-                    GenericParamDefKind::Const { has_default } => {
+                    GenericParamDefKind::Const { has_default, .. } => {
                         let ty = tcx
                             .at(self.span)
                             .type_of(param.def_id)
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index c0cf1ea34cf..9a57cc6dbab 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -461,7 +461,15 @@ fn check_opaque_meets_bounds<'tcx>(
     }
     match origin {
         // Checked when type checking the function containing them.
-        hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {}
+        hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {
+            // HACK: this should also fall through to the hidden type check below, but the original
+            // implementation had a bug where equivalent lifetimes are not identical. This caused us
+            // to reject existing stable code that is otherwise completely fine. The real fix is to
+            // compare the hidden types via our type equivalence/relation infra instead of doing an
+            // identity check.
+            let _ = infcx.take_opaque_types();
+            return Ok(());
+        }
         // Nested opaque types occur only in associated types:
         // ` type Opaque<T> = impl Trait<&'static T, AssocTy = impl Nested>; `
         // They can only be referenced as `<Opaque<T> as Trait<&'static T>>::AssocTy`.
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 bd0ab6463f0..92cc9759304 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -28,6 +28,8 @@ use rustc_trait_selection::traits::{
 use std::borrow::Cow;
 use std::iter;
 
+mod refine;
+
 /// Checks that a method from an impl conforms to the signature of
 /// the same method as declared in the trait.
 ///
@@ -53,6 +55,12 @@ pub(super) fn compare_impl_method<'tcx>(
             impl_trait_ref,
             CheckImpliedWfMode::Check,
         )?;
+        refine::check_refining_return_position_impl_trait_in_trait(
+            tcx,
+            impl_m,
+            trait_m,
+            impl_trait_ref,
+        );
     };
 }
 
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
new file mode 100644
index 00000000000..a8149b634ef
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
@@ -0,0 +1,311 @@
+use rustc_data_structures::fx::FxIndexSet;
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_infer::infer::{outlives::env::OutlivesEnvironment, TyCtxtInferExt};
+use rustc_lint_defs::builtin::REFINING_IMPL_TRAIT;
+use rustc_middle::traits::{ObligationCause, Reveal};
+use rustc_middle::ty::{
+    self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor,
+};
+use rustc_span::{Span, DUMMY_SP};
+use rustc_trait_selection::traits::{
+    elaborate, normalize_param_env_or_error, outlives_bounds::InferCtxtExt, ObligationCtxt,
+};
+use std::ops::ControlFlow;
+
+/// Check that an implementation does not refine an RPITIT from a trait method signature.
+pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    impl_m: ty::AssocItem,
+    trait_m: ty::AssocItem,
+    impl_trait_ref: ty::TraitRef<'tcx>,
+) {
+    if !tcx.impl_method_has_trait_impl_trait_tys(impl_m.def_id) {
+        return;
+    }
+    // crate-private traits don't have any library guarantees, there's no need to do this check.
+    if !tcx.visibility(trait_m.container_id(tcx)).is_public() {
+        return;
+    }
+
+    // If a type in the trait ref is private, then there's also no reason to to do this check.
+    let impl_def_id = impl_m.container_id(tcx);
+    for arg in impl_trait_ref.args {
+        if let Some(ty) = arg.as_type()
+            && let Some(self_visibility) = type_visibility(tcx, ty)
+            && !self_visibility.is_public()
+        {
+            return;
+        }
+    }
+
+    let impl_m_args = ty::GenericArgs::identity_for_item(tcx, impl_m.def_id);
+    let trait_m_to_impl_m_args = impl_m_args.rebase_onto(tcx, impl_def_id, impl_trait_ref.args);
+    let bound_trait_m_sig = tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_m_to_impl_m_args);
+    let trait_m_sig = tcx.liberate_late_bound_regions(impl_m.def_id, bound_trait_m_sig);
+    // replace the self type of the trait ref with `Self` so that diagnostics render better.
+    let trait_m_sig_with_self_for_diag = tcx.liberate_late_bound_regions(
+        impl_m.def_id,
+        tcx.fn_sig(trait_m.def_id).instantiate(
+            tcx,
+            tcx.mk_args_from_iter(
+                [tcx.types.self_param.into()]
+                    .into_iter()
+                    .chain(trait_m_to_impl_m_args.iter().skip(1)),
+            ),
+        ),
+    );
+
+    let Ok(hidden_tys) = tcx.collect_return_position_impl_trait_in_trait_tys(impl_m.def_id) else {
+        // Error already emitted, no need to delay another.
+        return;
+    };
+
+    let mut collector = ImplTraitInTraitCollector { tcx, types: FxIndexSet::default() };
+    trait_m_sig.visit_with(&mut collector);
+
+    // Bound that we find on RPITITs in the trait signature.
+    let mut trait_bounds = vec![];
+    // Bounds that we find on the RPITITs in the impl signature.
+    let mut impl_bounds = vec![];
+
+    for trait_projection in collector.types.into_iter().rev() {
+        let impl_opaque_args = trait_projection.args.rebase_onto(tcx, trait_m.def_id, impl_m_args);
+        let hidden_ty = hidden_tys[&trait_projection.def_id].instantiate(tcx, impl_opaque_args);
+
+        // If the hidden type is not an opaque, then we have "refined" the trait signature.
+        let ty::Alias(ty::Opaque, impl_opaque) = *hidden_ty.kind() else {
+            report_mismatched_rpitit_signature(
+                tcx,
+                trait_m_sig_with_self_for_diag,
+                trait_m.def_id,
+                impl_m.def_id,
+                None,
+            );
+            return;
+        };
+
+        // This opaque also needs to be from the impl method -- otherwise,
+        // it's a refinement to a TAIT.
+        if !tcx.hir().get_if_local(impl_opaque.def_id).map_or(false, |node| {
+            matches!(
+                node.expect_item().expect_opaque_ty().origin,
+                hir::OpaqueTyOrigin::AsyncFn(def_id)  | hir::OpaqueTyOrigin::FnReturn(def_id)
+                    if def_id == impl_m.def_id.expect_local()
+            )
+        }) {
+            report_mismatched_rpitit_signature(
+                tcx,
+                trait_m_sig_with_self_for_diag,
+                trait_m.def_id,
+                impl_m.def_id,
+                None,
+            );
+            return;
+        }
+
+        trait_bounds.extend(
+            tcx.item_bounds(trait_projection.def_id).iter_instantiated(tcx, trait_projection.args),
+        );
+        impl_bounds.extend(elaborate(
+            tcx,
+            tcx.explicit_item_bounds(impl_opaque.def_id)
+                .iter_instantiated_copied(tcx, impl_opaque.args),
+        ));
+    }
+
+    let hybrid_preds = tcx
+        .predicates_of(impl_def_id)
+        .instantiate_identity(tcx)
+        .into_iter()
+        .chain(tcx.predicates_of(trait_m.def_id).instantiate_own(tcx, trait_m_to_impl_m_args))
+        .map(|(clause, _)| clause);
+    let param_env = ty::ParamEnv::new(tcx.mk_clauses_from_iter(hybrid_preds), Reveal::UserFacing);
+    let param_env = normalize_param_env_or_error(tcx, param_env, ObligationCause::dummy());
+
+    let ref infcx = tcx.infer_ctxt().build();
+    let ocx = ObligationCtxt::new(infcx);
+
+    // Normalize the bounds. This has two purposes:
+    //
+    // 1. Project the RPITIT projections from the trait to the opaques on the impl,
+    //    which means that they don't need to be mapped manually.
+    //
+    // 2. Project any other projections that show up in the bound. That makes sure that
+    //    we don't consider `tests/ui/async-await/in-trait/async-associated-types.rs`
+    //    to be refining.
+    let (trait_bounds, impl_bounds) =
+        ocx.normalize(&ObligationCause::dummy(), param_env, (trait_bounds, impl_bounds));
+
+    // Since we've normalized things, we need to resolve regions, since we'll
+    // possibly have introduced region vars during projection. We don't expect
+    // this resolution to have incurred any region errors -- but if we do, then
+    // just delay a bug.
+    let mut implied_wf_types = FxIndexSet::default();
+    implied_wf_types.extend(trait_m_sig.inputs_and_output);
+    implied_wf_types.extend(ocx.normalize(
+        &ObligationCause::dummy(),
+        param_env,
+        trait_m_sig.inputs_and_output,
+    ));
+    if !ocx.select_all_or_error().is_empty() {
+        tcx.sess.delay_span_bug(
+            DUMMY_SP,
+            "encountered errors when checking RPITIT refinement (selection)",
+        );
+        return;
+    }
+    let outlives_env = OutlivesEnvironment::with_bounds(
+        param_env,
+        infcx.implied_bounds_tys(param_env, impl_m.def_id.expect_local(), implied_wf_types),
+    );
+    let errors = infcx.resolve_regions(&outlives_env);
+    if !errors.is_empty() {
+        tcx.sess.delay_span_bug(
+            DUMMY_SP,
+            "encountered errors when checking RPITIT refinement (regions)",
+        );
+        return;
+    }
+    // Resolve any lifetime variables that may have been introduced during normalization.
+    let Ok((trait_bounds, impl_bounds)) = infcx.fully_resolve((trait_bounds, impl_bounds)) else {
+        tcx.sess.delay_span_bug(
+            DUMMY_SP,
+            "encountered errors when checking RPITIT refinement (resolution)",
+        );
+        return;
+    };
+
+    // For quicker lookup, use an `IndexSet`
+    // (we don't use one earlier because it's not foldable..)
+    let trait_bounds = FxIndexSet::from_iter(trait_bounds);
+
+    // Find any clauses that are present in the impl's RPITITs that are not
+    // present in the trait's RPITITs. This will trigger on trivial predicates,
+    // too, since we *do not* use the trait solver to prove that the RPITIT's
+    // bounds are not stronger -- we're doing a simple, syntactic compatibility
+    // check between bounds. This is strictly forwards compatible, though.
+    for (clause, span) in impl_bounds {
+        if !trait_bounds.contains(&clause) {
+            report_mismatched_rpitit_signature(
+                tcx,
+                trait_m_sig_with_self_for_diag,
+                trait_m.def_id,
+                impl_m.def_id,
+                Some(span),
+            );
+            return;
+        }
+    }
+}
+
+struct ImplTraitInTraitCollector<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    types: FxIndexSet<ty::AliasTy<'tcx>>,
+}
+
+impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitCollector<'tcx> {
+    type BreakTy = !;
+
+    fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow<Self::BreakTy> {
+        if let ty::Alias(ty::Projection, proj) = *ty.kind()
+            && self.tcx.is_impl_trait_in_trait(proj.def_id)
+        {
+            if self.types.insert(proj) {
+                for (pred, _) in self
+                    .tcx
+                    .explicit_item_bounds(proj.def_id)
+                    .iter_instantiated_copied(self.tcx, proj.args)
+                {
+                    pred.visit_with(self)?;
+                }
+            }
+            ControlFlow::Continue(())
+        } else {
+            ty.super_visit_with(self)
+        }
+    }
+}
+
+fn report_mismatched_rpitit_signature<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    trait_m_sig: ty::FnSig<'tcx>,
+    trait_m_def_id: DefId,
+    impl_m_def_id: DefId,
+    unmatched_bound: Option<Span>,
+) {
+    let mapping = std::iter::zip(
+        tcx.fn_sig(trait_m_def_id).skip_binder().bound_vars(),
+        tcx.fn_sig(impl_m_def_id).skip_binder().bound_vars(),
+    )
+    .filter_map(|(impl_bv, trait_bv)| {
+        if let ty::BoundVariableKind::Region(impl_bv) = impl_bv
+            && let ty::BoundVariableKind::Region(trait_bv) = trait_bv
+        {
+            Some((impl_bv, trait_bv))
+        } else {
+            None
+        }
+    })
+    .collect();
+
+    let mut return_ty =
+        trait_m_sig.output().fold_with(&mut super::RemapLateBound { tcx, mapping: &mapping });
+
+    if tcx.asyncness(impl_m_def_id).is_async() && tcx.asyncness(trait_m_def_id).is_async() {
+        let ty::Alias(ty::Projection, future_ty) = return_ty.kind() else {
+            bug!();
+        };
+        let Some(future_output_ty) = tcx
+            .explicit_item_bounds(future_ty.def_id)
+            .iter_instantiated_copied(tcx, future_ty.args)
+            .find_map(|(clause, _)| match clause.kind().no_bound_vars()? {
+                ty::ClauseKind::Projection(proj) => proj.term.ty(),
+                _ => None,
+            })
+        else {
+            bug!()
+        };
+        return_ty = future_output_ty;
+    }
+
+    let (span, impl_return_span, pre, post) =
+        match tcx.hir().get_by_def_id(impl_m_def_id.expect_local()).fn_decl().unwrap().output {
+            hir::FnRetTy::DefaultReturn(span) => (tcx.def_span(impl_m_def_id), span, "-> ", " "),
+            hir::FnRetTy::Return(ty) => (ty.span, ty.span, "", ""),
+        };
+    let trait_return_span =
+        tcx.hir().get_if_local(trait_m_def_id).map(|node| match node.fn_decl().unwrap().output {
+            hir::FnRetTy::DefaultReturn(_) => tcx.def_span(trait_m_def_id),
+            hir::FnRetTy::Return(ty) => ty.span,
+        });
+
+    let span = unmatched_bound.unwrap_or(span);
+    tcx.emit_spanned_lint(
+        REFINING_IMPL_TRAIT,
+        tcx.local_def_id_to_hir_id(impl_m_def_id.expect_local()),
+        span,
+        crate::errors::ReturnPositionImplTraitInTraitRefined {
+            impl_return_span,
+            trait_return_span,
+            pre,
+            post,
+            return_ty,
+            unmatched_bound,
+        },
+    );
+}
+
+fn type_visibility<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<ty::Visibility<DefId>> {
+    match *ty.kind() {
+        ty::Ref(_, ty, _) => type_visibility(tcx, ty),
+        ty::Adt(def, args) => {
+            if def.is_fundamental() {
+                type_visibility(tcx, args.type_at(0))
+            } else {
+                Some(tcx.visibility(def.did()))
+            }
+        }
+        _ => None,
+    }
+}
diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs
index 5bd6fcb9612..463fab93e3f 100644
--- a/compiler/rustc_hir_analysis/src/check/region.rs
+++ b/compiler/rustc_hir_analysis/src/check/region.rs
@@ -149,7 +149,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
                     // From now on, we continue normally.
                     visitor.cx = prev_cx;
                 }
-                hir::StmtKind::Local(..) | hir::StmtKind::Item(..) => {
+                hir::StmtKind::Local(..) => {
                     // Each declaration introduces a subscope for bindings
                     // introduced by the declaration; this subscope covers a
                     // suffix of the block. Each subscope in a block has the
@@ -163,6 +163,10 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
                     visitor.cx.var_parent = visitor.cx.parent;
                     visitor.visit_stmt(statement)
                 }
+                hir::StmtKind::Item(..) => {
+                    // Don't create scopes for items, since they won't be
+                    // lowered to THIR and MIR.
+                }
                 hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => visitor.visit_stmt(statement),
             }
         }
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index f5beefc47f3..b97e0a80fe6 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -1255,7 +1255,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
 
     let is_our_default = |def: &ty::GenericParamDef| match def.kind {
         GenericParamDefKind::Type { has_default, .. }
-        | GenericParamDefKind::Const { has_default } => {
+        | GenericParamDefKind::Const { has_default, .. } => {
             has_default && def.index >= generics.parent_count as u32
         }
         GenericParamDefKind::Lifetime => unreachable!(),
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index 67aef0a7c43..94f3e8706fc 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -157,6 +157,14 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef
     let infcx = tcx.infer_ctxt().build();
     let cause = ObligationCause::misc(span, impl_did);
 
+    // Later parts of the compiler rely on all DispatchFromDyn types to be ABI-compatible with raw
+    // pointers. This is enforced here: we only allow impls for references, raw pointers, and things
+    // that are effectively repr(transparent) newtypes around types that already hav a
+    // DispatchedFromDyn impl. We cannot literally use repr(transparent) on those tpyes since some
+    // of them support an allocator, but we ensure that for the cases where the type implements this
+    // trait, they *do* satisfy the repr(transparent) rules, and then we assume that everything else
+    // in the compiler (in particular, all the call ABI logic) will treat them as repr(transparent)
+    // even if they do not carry that attribute.
     use rustc_type_ir::sty::TyKind::*;
     match (source.kind(), target.kind()) {
         (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b))
diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
index a94c75f918a..aa7c9e504c1 100644
--- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
@@ -7,7 +7,6 @@
 //! `tcx.inherent_impls(def_id)`). That value, however,
 //! is computed by selecting an idea from this table.
 
-use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -15,6 +14,8 @@ use rustc_middle::ty::fast_reject::{simplify_type, SimplifiedType, TreatParams};
 use rustc_middle::ty::{self, CrateInherentImpls, Ty, TyCtxt};
 use rustc_span::symbol::sym;
 
+use crate::errors;
+
 /// On-demand query: yields a map containing all types mapped to their inherent impls.
 pub fn crate_inherent_impls(tcx: TyCtxt<'_>, (): ()) -> CrateInherentImpls {
     let mut collect = InherentCollect { tcx, impls_map: Default::default() };
@@ -45,14 +46,6 @@ struct InherentCollect<'tcx> {
     impls_map: CrateInherentImpls,
 }
 
-const INTO_CORE: &str = "consider moving this inherent impl into `core` if possible";
-const INTO_DEFINING_CRATE: &str =
-    "consider moving this inherent impl into the crate defining the type if possible";
-const ADD_ATTR_TO_TY: &str = "alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type \
-     and `#[rustc_allow_incoherent_impl]` to the relevant impl items";
-const ADD_ATTR: &str =
-    "alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items";
-
 impl<'tcx> InherentCollect<'tcx> {
     fn check_def_id(&mut self, impl_def_id: LocalDefId, self_ty: Ty<'tcx>, ty_def_id: DefId) {
         if let Some(ty_def_id) = ty_def_id.as_local() {
@@ -69,30 +62,17 @@ impl<'tcx> InherentCollect<'tcx> {
 
             if !self.tcx.has_attr(ty_def_id, sym::rustc_has_incoherent_inherent_impls) {
                 let impl_span = self.tcx.def_span(impl_def_id);
-                struct_span_err!(
-                    self.tcx.sess,
-                    impl_span,
-                    E0390,
-                    "cannot define inherent `impl` for a type outside of the crate where the type is defined",
-                )
-                .help(INTO_DEFINING_CRATE)
-                .span_help(impl_span, ADD_ATTR_TO_TY)
-                .emit();
+                self.tcx.sess.emit_err(errors::InherentTyOutside { span: impl_span });
                 return;
             }
 
             for &impl_item in items {
                 if !self.tcx.has_attr(impl_item, sym::rustc_allow_incoherent_impl) {
                     let impl_span = self.tcx.def_span(impl_def_id);
-                    struct_span_err!(
-                        self.tcx.sess,
-                        impl_span,
-                        E0390,
-                        "cannot define inherent `impl` for a type outside of the crate where the type is defined",
-                    )
-                    .help(INTO_DEFINING_CRATE)
-                    .span_help(self.tcx.def_span(impl_item), ADD_ATTR)
-                    .emit();
+                    self.tcx.sess.emit_err(errors::InherentTyOutsideRelevant {
+                        span: impl_span,
+                        help_span: self.tcx.def_span(impl_item),
+                    });
                     return;
                 }
             }
@@ -104,16 +84,7 @@ impl<'tcx> InherentCollect<'tcx> {
             }
         } else {
             let impl_span = self.tcx.def_span(impl_def_id);
-            struct_span_err!(
-                self.tcx.sess,
-                impl_span,
-                E0116,
-                "cannot define inherent `impl` for a type outside of the crate \
-                              where the type is defined"
-            )
-            .span_label(impl_span, "impl for type defined outside of crate.")
-            .note("define and implement a trait or new type instead")
-            .emit();
+            self.tcx.sess.emit_err(errors::InherentTyOutsideNew { span: impl_span });
         }
     }
 
@@ -124,34 +95,20 @@ impl<'tcx> InherentCollect<'tcx> {
                 for &impl_item in items {
                     if !self.tcx.has_attr(impl_item, sym::rustc_allow_incoherent_impl) {
                         let span = self.tcx.def_span(impl_def_id);
-                        struct_span_err!(
-                            self.tcx.sess,
+                        self.tcx.sess.emit_err(errors::InherentTyOutsidePrimitive {
                             span,
-                            E0390,
-                            "cannot define inherent `impl` for primitive types outside of `core`",
-                        )
-                        .help(INTO_CORE)
-                        .span_help(self.tcx.def_span(impl_item), ADD_ATTR)
-                        .emit();
+                            help_span: self.tcx.def_span(impl_item),
+                        });
                         return;
                     }
                 }
             } else {
                 let span = self.tcx.def_span(impl_def_id);
-                let mut err = struct_span_err!(
-                    self.tcx.sess,
-                    span,
-                    E0390,
-                    "cannot define inherent `impl` for primitive types",
-                );
-                err.help("consider using an extension trait instead");
+                let mut note = None;
                 if let ty::Ref(_, subty, _) = ty.kind() {
-                    err.note(format!(
-                        "you could also try moving the reference to \
-                            uses of `{subty}` (such as `self`) within the implementation"
-                    ));
+                    note = Some(errors::InherentPrimitiveTyNote { subty: *subty });
                 }
-                err.emit();
+                self.tcx.sess.emit_err(errors::InherentPrimitiveTy { span, note });
                 return;
             }
         }
@@ -178,15 +135,7 @@ impl<'tcx> InherentCollect<'tcx> {
                 self.check_def_id(id, self_ty, data.principal_def_id().unwrap());
             }
             ty::Dynamic(..) => {
-                struct_span_err!(
-                    self.tcx.sess,
-                    item_span,
-                    E0785,
-                    "cannot define inherent `impl` for a dyn auto trait"
-                )
-                .span_label(item_span, "impl requires at least one non-auto trait")
-                .note("define and implement a new trait or type instead")
-                .emit();
+                self.tcx.sess.emit_err(errors::InherentDyn { span: item_span });
             }
             ty::Bool
             | ty::Char
@@ -202,17 +151,7 @@ impl<'tcx> InherentCollect<'tcx> {
             | ty::FnPtr(_)
             | ty::Tuple(..) => self.check_primitive_impl(id, self_ty),
             ty::Alias(..) | ty::Param(_) => {
-                let mut err = struct_span_err!(
-                    self.tcx.sess,
-                    item_span,
-                    E0118,
-                    "no nominal type found for inherent implementation"
-                );
-
-                err.span_label(item_span, "impl requires a nominal type")
-                    .note("either implement a trait on it or create a newtype to wrap it instead");
-
-                err.emit();
+                self.tcx.sess.emit_err(errors::InherentNominal { span: item_span });
             }
             ty::FnDef(..)
             | ty::Closure(..)
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index 4842008279a..3d60c57b9d5 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -328,7 +328,10 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
                 name: param.name.ident().name,
                 def_id: param.def_id.to_def_id(),
                 pure_wrt_drop: param.pure_wrt_drop,
-                kind: ty::GenericParamDefKind::Const { has_default: default.is_some() },
+                kind: ty::GenericParamDefKind::Const {
+                    has_default: default.is_some(),
+                    is_host_effect: is_host_param,
+                },
             })
         }
     }));
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 9471ad9ca90..b83ef8d07db 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -919,6 +919,22 @@ pub struct UnusedAssociatedTypeBounds {
     pub span: Span,
 }
 
+#[derive(LintDiagnostic)]
+#[diag(hir_analysis_rpitit_refined)]
+#[note]
+pub(crate) struct ReturnPositionImplTraitInTraitRefined<'tcx> {
+    #[suggestion(applicability = "maybe-incorrect", code = "{pre}{return_ty}{post}")]
+    pub impl_return_span: Span,
+    #[label]
+    pub trait_return_span: Option<Span>,
+    #[label(hir_analysis_unmatched_bound_label)]
+    pub unmatched_bound: Option<Span>,
+
+    pub pre: &'static str,
+    pub post: &'static str,
+    pub return_ty: Ty<'tcx>,
+}
+
 #[derive(Diagnostic)]
 #[diag(hir_analysis_assoc_bound_on_const)]
 #[note]
@@ -927,3 +943,75 @@ pub struct AssocBoundOnConst {
     pub span: Span,
     pub descr: &'static str,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_inherent_ty_outside, code = "E0390")]
+#[help]
+pub struct InherentTyOutside {
+    #[primary_span]
+    #[help(hir_analysis_span_help)]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_inherent_ty_outside_relevant, code = "E0390")]
+#[help]
+pub struct InherentTyOutsideRelevant {
+    #[primary_span]
+    pub span: Span,
+    #[help(hir_analysis_span_help)]
+    pub help_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_inherent_ty_outside_new, code = "E0116")]
+#[note]
+pub struct InherentTyOutsideNew {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_inherent_ty_outside_primitive, code = "E0390")]
+#[help]
+pub struct InherentTyOutsidePrimitive {
+    #[primary_span]
+    pub span: Span,
+    #[help(hir_analysis_span_help)]
+    pub help_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_inherent_primitive_ty, code = "E0390")]
+#[help]
+pub struct InherentPrimitiveTy<'a> {
+    #[primary_span]
+    pub span: Span,
+    #[subdiagnostic]
+    pub note: Option<InherentPrimitiveTyNote<'a>>,
+}
+
+#[derive(Subdiagnostic)]
+#[note(hir_analysis_inherent_primitive_ty_note)]
+pub struct InherentPrimitiveTyNote<'a> {
+    pub subty: Ty<'a>,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_inherent_dyn, code = "E0785")]
+#[note]
+pub struct InherentDyn {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_inherent_nominal, code = "E0118")]
+#[note]
+pub struct InherentNominal {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index b0f333b79ca..ef788935efb 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -117,7 +117,7 @@ use rustc_hir::def::DefKind;
 fluent_messages! { "../messages.ftl" }
 
 fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) {
-    const CONVENTIONS_UNSTABLE: &str = "`C`, `cdecl`, `win64`, `sysv64` or `efiapi`";
+    const CONVENTIONS_UNSTABLE: &str = "`C`, `cdecl`, `aapcs`, `win64`, `sysv64` or `efiapi`";
     const CONVENTIONS_STABLE: &str = "`C` or `cdecl`";
     const UNSTABLE_EXPLAIN: &str =
         "using calling conventions other than `C` or `cdecl` for varargs functions is unstable";
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl
index 2281343e250..034ffc17a7a 100644
--- a/compiler/rustc_hir_typeck/messages.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -81,6 +81,12 @@ hir_typeck_option_result_asref = use `{$def_path}::as_ref` to convert `{$expecte
 hir_typeck_option_result_cloned = use `{$def_path}::cloned` to clone the value inside the `{$def_path}`
 hir_typeck_option_result_copied = use `{$def_path}::copied` to copy the value inside the `{$def_path}`
 
+hir_typeck_remove_semi_for_coerce = you might have meant to return the `match` expression
+hir_typeck_remove_semi_for_coerce_expr = this could be implicitly returned but it is a statement, not a tail expression
+hir_typeck_remove_semi_for_coerce_ret = the `match` arms can conform to this return type
+hir_typeck_remove_semi_for_coerce_semi = the `match` is a statement because of this semicolon, consider removing it
+hir_typeck_remove_semi_for_coerce_suggestion = remove this semicolon
+
 hir_typeck_return_stmt_outside_of_fn_body =
     {$statement_kind} statement outside of function body
     .encl_body_label = the {$statement_kind} is part of this body...
diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs
index 7ad9f51ba70..3319b52e246 100644
--- a/compiler/rustc_hir_typeck/src/_match.rs
+++ b/compiler/rustc_hir_typeck/src/_match.rs
@@ -1,6 +1,6 @@
 use crate::coercion::{AsCoercionSite, CoerceMany};
 use crate::{Diverges, Expectation, FnCtxt, Needs};
-use rustc_errors::{Applicability, Diagnostic, MultiSpan};
+use rustc_errors::Diagnostic;
 use rustc_hir::{self as hir, ExprKind};
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::traits::Obligation;
@@ -225,24 +225,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return;
         }
 
-        let semi_span = expr.span.shrink_to_hi().with_hi(semi_span.hi());
-        let mut ret_span: MultiSpan = semi_span.into();
-        ret_span.push_span_label(
-            expr.span,
-            "this could be implicitly returned but it is a statement, not a tail expression",
-        );
-        ret_span.push_span_label(ret, "the `match` arms can conform to this return type");
-        ret_span.push_span_label(
-            semi_span,
-            "the `match` is a statement because of this semicolon, consider removing it",
-        );
-        diag.span_note(ret_span, "you might have meant to return the `match` expression");
-        diag.tool_only_span_suggestion(
-            semi_span,
-            "remove this semicolon",
-            "",
-            Applicability::MaybeIncorrect,
-        );
+        let semi = expr.span.shrink_to_hi().with_hi(semi_span.hi());
+        let sugg = crate::errors::RemoveSemiForCoerce { expr: expr.span, ret, semi };
+        diag.subdiagnostic(sugg);
     }
 
     /// When the previously checked expression (the scrutinee) diverges,
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 31a03fabe4f..e8d4e6b447f 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -321,8 +321,13 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                 .emit();
             }
             CastError::CastToBool => {
-                let mut err =
-                    struct_span_err!(fcx.tcx.sess, self.span, E0054, "cannot cast as `bool`");
+                let mut err = struct_span_err!(
+                    fcx.tcx.sess,
+                    self.span,
+                    E0054,
+                    "cannot cast `{}` as `bool`",
+                    self.expr_ty
+                );
 
                 if self.expr_ty.is_numeric() {
                     match fcx.tcx.sess.source_map().span_to_snippet(self.expr_span) {
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 054d23c71d4..255fe5e0206 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -292,6 +292,32 @@ pub enum OptionResultRefMismatch {
     // },
 }
 
+pub struct RemoveSemiForCoerce {
+    pub expr: Span,
+    pub ret: Span,
+    pub semi: Span,
+}
+
+impl AddToDiagnostic for RemoveSemiForCoerce {
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+    where
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+    {
+        let mut multispan: MultiSpan = self.semi.into();
+        multispan.push_span_label(self.expr, fluent::hir_typeck_remove_semi_for_coerce_expr);
+        multispan.push_span_label(self.ret, fluent::hir_typeck_remove_semi_for_coerce_ret);
+        multispan.push_span_label(self.semi, fluent::hir_typeck_remove_semi_for_coerce_semi);
+        diag.span_note(multispan, fluent::hir_typeck_remove_semi_for_coerce);
+
+        diag.tool_only_span_suggestion(
+            self.semi,
+            fluent::hir_typeck_remove_semi_for_coerce_suggestion,
+            "",
+            Applicability::MaybeIncorrect,
+        );
+    }
+}
+
 #[derive(Diagnostic)]
 #[diag(hir_typeck_const_select_must_be_const)]
 #[help]
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index a3b8c391e02..70d1dad8a6f 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -1203,7 +1203,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // otherwise check exactly as a let statement
         self.check_decl(let_expr.into());
         // but return a bool, for this is a boolean expression
-        self.tcx.types.bool
+        if let Some(error_guaranteed) = let_expr.is_recovered {
+            self.set_tainted_by_errors(error_guaranteed);
+            Ty::new_error(self.tcx, error_guaranteed)
+        } else {
+            self.tcx.types.bool
+        }
     }
 
     fn check_expr_loop(
diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs
index 5b5986a349f..952b90d6a35 100644
--- a/compiler/rustc_hir_typeck/src/fallback.rs
+++ b/compiler/rustc_hir_typeck/src/fallback.rs
@@ -4,6 +4,7 @@ use rustc_data_structures::{
     graph::{iterate::DepthFirstSearch, vec_graph::VecGraph},
     unord::{UnordBag, UnordMap, UnordSet},
 };
+use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
 use rustc_middle::ty::{self, Ty};
 
 impl<'tcx> FnCtxt<'_, 'tcx> {
@@ -23,20 +24,10 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
             self.fulfillment_cx.borrow_mut().pending_obligations()
         );
 
-        // Check if we have any unsolved variables. If not, no need for fallback.
-        let unsolved_variables = self.unsolved_variables();
-        if unsolved_variables.is_empty() {
-            return;
-        }
+        let fallback_occured = self.fallback_types() | self.fallback_effects();
 
-        let diverging_fallback = self.calculate_diverging_fallback(&unsolved_variables);
-
-        // We do fallback in two passes, to try to generate
-        // better error messages.
-        // The first time, we do *not* replace opaque types.
-        for ty in unsolved_variables {
-            debug!("unsolved_variable = {:?}", ty);
-            self.fallback_if_possible(ty, &diverging_fallback);
+        if !fallback_occured {
+            return;
         }
 
         // We now see if we can make progress. This might cause us to
@@ -65,6 +56,53 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         self.select_obligations_where_possible(|_| {});
     }
 
+    fn fallback_types(&self) -> bool {
+        // Check if we have any unsolved variables. If not, no need for fallback.
+        let unsolved_variables = self.unsolved_variables();
+
+        if unsolved_variables.is_empty() {
+            return false;
+        }
+
+        let diverging_fallback = self.calculate_diverging_fallback(&unsolved_variables);
+
+        // We do fallback in two passes, to try to generate
+        // better error messages.
+        // The first time, we do *not* replace opaque types.
+        for ty in unsolved_variables {
+            debug!("unsolved_variable = {:?}", ty);
+            self.fallback_if_possible(ty, &diverging_fallback);
+        }
+
+        true
+    }
+
+    fn fallback_effects(&self) -> bool {
+        let unsolved_effects = self.unsolved_effects();
+
+        if unsolved_effects.is_empty() {
+            return false;
+        }
+
+        // not setting `fallback_has_occured` here because that field is only used for type fallback
+        // diagnostics.
+
+        for effect in unsolved_effects {
+            let expected = self.tcx.consts.true_;
+            let cause = self.misc(rustc_span::DUMMY_SP);
+            match self.at(&cause, self.param_env).eq(DefineOpaqueTypes::Yes, expected, effect) {
+                Ok(InferOk { obligations, value: () }) => {
+                    self.register_predicates(obligations);
+                }
+                Err(e) => {
+                    bug!("cannot eq unsolved effect: {e:?}")
+                }
+            }
+        }
+
+        true
+    }
+
     // Tries to apply a fallback to `ty` if it is an unsolved variable.
     //
     // - Unconstrained ints are replaced with `i32`.
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 28fe2e062e5..c94cfde0670 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -1295,17 +1295,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => {
                         self.fcx.ty_infer(Some(param), inf.span).into()
                     }
-                    (GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => {
+                    (
+                        &GenericParamDefKind::Const { has_default, is_host_effect },
+                        GenericArg::Infer(inf),
+                    ) => {
                         let tcx = self.fcx.tcx();
-                        self.fcx
-                            .ct_infer(
-                                tcx.type_of(param.def_id)
-                                    .no_bound_vars()
-                                    .expect("const parameter types cannot be generic"),
-                                Some(param),
-                                inf.span,
-                            )
-                            .into()
+
+                        if has_default && is_host_effect {
+                            self.fcx.var_for_effect(param)
+                        } else {
+                            self.fcx
+                                .ct_infer(
+                                    tcx.type_of(param.def_id)
+                                        .no_bound_vars()
+                                        .expect("const parameter types cannot be generic"),
+                                    Some(param),
+                                    inf.span,
+                                )
+                                .into()
+                        }
                     }
                     _ => unreachable!(),
                 }
@@ -1324,7 +1332,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                     GenericParamDefKind::Type { has_default, .. } => {
                         if !infer_args && has_default {
-                            // If we have a default, then we it doesn't matter that we're not
+                            // If we have a default, then it doesn't matter that we're not
                             // inferring the type arguments: we provide the default where any
                             // is missing.
                             tcx.type_of(param.def_id).instantiate(tcx, args.unwrap()).into()
@@ -1336,17 +1344,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             self.fcx.var_for_def(self.span, param)
                         }
                     }
-                    GenericParamDefKind::Const { has_default } => {
-                        if !infer_args
-                            && has_default
-                            && !tcx.has_attr(param.def_id, sym::rustc_host)
-                        {
-                            tcx.const_param_default(param.def_id)
-                                .instantiate(tcx, args.unwrap())
-                                .into()
-                        } else {
-                            self.fcx.var_for_def(self.span, param)
+                    GenericParamDefKind::Const { has_default, is_host_effect } => {
+                        if has_default {
+                            // N.B. this is a bit of a hack. `infer_args` is passed depending on
+                            // whether the user has provided generic args. E.g. for `Vec::new`
+                            // we would have to infer the generic types. However, for `Vec::<T>::new`
+                            // where the allocator param `A` has a default we will *not* infer. But
+                            // for effect params this is a different story: if the user has not written
+                            // anything explicit for the effect param, we always need to try to infer
+                            // it before falling back to default, such that a `const fn` such as
+                            // `needs_drop::<()>` can still be called in const contexts. (if we defaulted
+                            // instead of inferred, typeck would error)
+                            if is_host_effect {
+                                return self.fcx.var_for_effect(param);
+                            } else if !infer_args {
+                                return tcx
+                                    .const_param_default(param.def_id)
+                                    .instantiate(tcx, args.unwrap())
+                                    .into();
+                            }
                         }
+
+                        self.fcx.var_for_def(self.span, param)
                     }
                 }
             }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index 7707cc6de54..4237b4488ca 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -266,7 +266,14 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
         param: Option<&ty::GenericParamDef>,
         span: Span,
     ) -> Const<'tcx> {
+        // FIXME ideally this shouldn't use unwrap
         match param {
+            Some(
+                param @ ty::GenericParamDef {
+                    kind: ty::GenericParamDefKind::Const { is_host_effect: true, .. },
+                    ..
+                },
+            ) => self.var_for_effect(param).as_const().unwrap(),
             Some(param) => self.var_for_def(span, param).as_const().unwrap(),
             None => self.next_const_var(
                 ty,
diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs
index ed4c63f171c..0ad2c1d9284 100644
--- a/compiler/rustc_hir_typeck/src/gather_locals.rs
+++ b/compiler/rustc_hir_typeck/src/gather_locals.rs
@@ -50,7 +50,7 @@ impl<'a> From<&'a hir::Local<'a>> for Declaration<'a> {
 
 impl<'a> From<&'a hir::Let<'a>> for Declaration<'a> {
     fn from(let_expr: &'a hir::Let<'a>) -> Self {
-        let hir::Let { hir_id, pat, ty, span, init } = *let_expr;
+        let hir::Let { hir_id, pat, ty, span, init, is_recovered: _ } = *let_expr;
         Declaration { hir_id, pat, ty, span, init: Some(init), origin: DeclOrigin::LetExpr }
     }
 }
diff --git a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
index 6a817122491..566dc09cdd2 100644
--- a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
+++ b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
@@ -643,17 +643,14 @@ fn check_must_not_suspend_ty<'tcx>(
         }
         ty::Array(ty, len) => {
             let descr_pre = &format!("{}array{} of ", data.descr_pre, plural_suffix);
+            let target_usize =
+                len.try_eval_target_usize(fcx.tcx, fcx.param_env).unwrap_or(0) as usize;
+            let plural_len = target_usize.saturating_add(1);
             check_must_not_suspend_ty(
                 fcx,
                 ty,
                 hir_id,
-                SuspendCheckData {
-                    descr_pre,
-                    plural_len: len.try_eval_target_usize(fcx.tcx, fcx.param_env).unwrap_or(0)
-                        as usize
-                        + 1,
-                    ..data
-                },
+                SuspendCheckData { descr_pre, plural_len, ..data },
             )
         }
         // If drop tracking is enabled, we want to look through references, since the referent
diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index 1a41786d251..ba469bd029d 100644
--- a/compiler/rustc_hir_typeck/src/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -195,7 +195,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         assert_eq!(self.tcx.hir().body_owner_def_id(body.id()), closure_def_id);
         let mut delegate = InferBorrowKind {
-            fcx: self,
             closure_def_id,
             capture_information: Default::default(),
             fake_reads: Default::default(),
@@ -1607,34 +1606,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 /// Truncate the capture so that the place being borrowed is in accordance with RFC 1240,
 /// which states that it's unsafe to take a reference into a struct marked `repr(packed)`.
 fn restrict_repr_packed_field_ref_capture<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
     mut place: Place<'tcx>,
     mut curr_borrow_kind: ty::UpvarCapture,
 ) -> (Place<'tcx>, ty::UpvarCapture) {
     let pos = place.projections.iter().enumerate().position(|(i, p)| {
         let ty = place.ty_before_projection(i);
 
-        // Return true for fields of packed structs, unless those fields have alignment 1.
+        // Return true for fields of packed structs.
         match p.kind {
             ProjectionKind::Field(..) => match ty.kind() {
                 ty::Adt(def, _) if def.repr().packed() => {
-                    // We erase regions here because they cannot be hashed
-                    match tcx.layout_of(param_env.and(tcx.erase_regions(p.ty))) {
-                        Ok(layout) if layout.align.abi.bytes() == 1 => {
-                            // if the alignment is 1, the type can't be further
-                            // disaligned.
-                            debug!(
-                                "restrict_repr_packed_field_ref_capture: ({:?}) - align = 1",
-                                place
-                            );
-                            false
-                        }
-                        _ => {
-                            debug!("restrict_repr_packed_field_ref_capture: ({:?}) - true", place);
-                            true
-                        }
-                    }
+                    // We stop here regardless of field alignment. Field alignment can change as
+                    // types change, including the types of private fields in other crates, and that
+                    // shouldn't affect how we compute our captures.
+                    true
                 }
 
                 _ => false,
@@ -1689,9 +1674,7 @@ fn drop_location_span(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> Span {
     tcx.sess.source_map().end_point(owner_span)
 }
 
-struct InferBorrowKind<'a, 'tcx> {
-    fcx: &'a FnCtxt<'a, 'tcx>,
-
+struct InferBorrowKind<'tcx> {
     // The def-id of the closure whose kind and upvar accesses are being inferred.
     closure_def_id: LocalDefId,
 
@@ -1725,7 +1708,7 @@ struct InferBorrowKind<'a, 'tcx> {
     fake_reads: Vec<(Place<'tcx>, FakeReadCause, hir::HirId)>,
 }
 
-impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
+impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> {
     fn fake_read(
         &mut self,
         place: &PlaceWithHirId<'tcx>,
@@ -1740,12 +1723,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
 
         let (place, _) = restrict_capture_precision(place.place.clone(), dummy_capture_kind);
 
-        let (place, _) = restrict_repr_packed_field_ref_capture(
-            self.fcx.tcx,
-            self.fcx.param_env,
-            place,
-            dummy_capture_kind,
-        );
+        let (place, _) = restrict_repr_packed_field_ref_capture(place, dummy_capture_kind);
         self.fake_reads.push((place, cause, diag_expr_id));
     }
 
@@ -1780,12 +1758,8 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
         // We only want repr packed restriction to be applied to reading references into a packed
         // struct, and not when the data is being moved. Therefore we call this method here instead
         // of in `restrict_capture_precision`.
-        let (place, mut capture_kind) = restrict_repr_packed_field_ref_capture(
-            self.fcx.tcx,
-            self.fcx.param_env,
-            place_with_id.place.clone(),
-            capture_kind,
-        );
+        let (place, mut capture_kind) =
+            restrict_repr_packed_field_ref_capture(place_with_id.place.clone(), capture_kind);
 
         // Raw pointers don't inherit mutability
         if place_with_id.place.deref_tys().any(Ty::is_unsafe_ptr) {
diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs
index 0cfaf583774..7719482890e 100644
--- a/compiler/rustc_incremental/src/persist/save.rs
+++ b/compiler/rustc_incremental/src/persist/save.rs
@@ -48,18 +48,6 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) {
 
         join(
             move || {
-                sess.time("incr_comp_persist_result_cache", || {
-                    // Drop the memory map so that we can remove the file and write to it.
-                    if let Some(odc) = &tcx.query_system.on_disk_cache {
-                        odc.drop_serialized_data(tcx);
-                    }
-
-                    file_format::save_in(sess, query_cache_path, "query cache", |e| {
-                        encode_query_cache(tcx, e)
-                    });
-                });
-            },
-            move || {
                 sess.time("incr_comp_persist_dep_graph", || {
                     if let Err(err) = tcx.dep_graph.encode(&tcx.sess.prof) {
                         sess.emit_err(errors::WriteDepGraph { path: &staging_dep_graph_path, err });
@@ -73,6 +61,20 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) {
                     }
                 });
             },
+            move || {
+                // We execute this after `incr_comp_persist_dep_graph` for the serial compiler
+                // to catch any potential query execution writing to the dep graph.
+                sess.time("incr_comp_persist_result_cache", || {
+                    // Drop the memory map so that we can remove the file and write to it.
+                    if let Some(odc) = &tcx.query_system.on_disk_cache {
+                        odc.drop_serialized_data(tcx);
+                    }
+
+                    file_format::save_in(sess, query_cache_path, "query cache", |e| {
+                        encode_query_cache(tcx, e)
+                    });
+                });
+            },
         );
     })
 }
diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
index 9d7a9fefd08..99bd296d0d5 100644
--- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
+++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
@@ -522,6 +522,17 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
                     }
                 }
             }
+            ty::ConstKind::Infer(InferConst::EffectVar(vid)) => {
+                match self.infcx.probe_effect_var(vid) {
+                    Some(value) => return self.fold_const(value.as_const(self.infcx.tcx)),
+                    None => {
+                        return self.canonicalize_const_var(
+                            CanonicalVarInfo { kind: CanonicalVarKind::Effect },
+                            ct,
+                        );
+                    }
+                }
+            }
             ty::ConstKind::Infer(InferConst::Fresh(_)) => {
                 bug!("encountered a fresh const during canonicalization")
             }
@@ -690,7 +701,8 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
             .iter()
             .map(|v| CanonicalVarInfo {
                 kind: match v.kind {
-                    CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => {
+                    CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float)
+                    | CanonicalVarKind::Effect => {
                         return *v;
                     }
                     CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => {
diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs
index 8ca2e403043..41787ee2942 100644
--- a/compiler/rustc_infer/src/infer/canonical/mod.rs
+++ b/compiler/rustc_infer/src/infer/canonical/mod.rs
@@ -151,7 +151,11 @@ impl<'tcx> InferCtxt<'tcx> {
                     universe_map(ui),
                 )
                 .into(),
-
+            CanonicalVarKind::Effect => {
+                let vid = self.inner.borrow_mut().effect_unification_table().new_key(None);
+                ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(vid), self.tcx.types.bool)
+                    .into()
+            }
             CanonicalVarKind::PlaceholderConst(ty::PlaceholderConst { universe, bound }, ty) => {
                 let universe_mapped = universe_map(universe);
                 let placeholder_mapped = ty::PlaceholderConst { universe: universe_mapped, bound };
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs
index ddc8e7e50eb..ee13eb0271e 100644
--- a/compiler/rustc_infer/src/infer/combine.rs
+++ b/compiler/rustc_infer/src/infer/combine.rs
@@ -30,7 +30,7 @@ use super::{DefineOpaqueTypes, InferCtxt, TypeTrace};
 use crate::infer::generalize::{self, CombineDelegate, Generalization};
 use crate::traits::{Obligation, PredicateObligations};
 use rustc_middle::infer::canonical::OriginalQueryValues;
-use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
+use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue, EffectVarValue};
 use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::relate::{RelateResult, TypeRelation};
@@ -91,7 +91,7 @@ impl<'tcx> InferCtxt<'tcx> {
                     .borrow_mut()
                     .float_unification_table()
                     .unify_var_var(a_id, b_id)
-                    .map_err(|e| float_unification_error(relation.a_is_expected(), e))?;
+                    .map_err(|e| float_unification_error(a_is_expected, e))?;
                 Ok(a)
             }
             (&ty::Infer(ty::FloatVar(v_id)), &ty::Float(v)) => {
@@ -210,10 +210,30 @@ impl<'tcx> InferCtxt<'tcx> {
                 return Ok(a);
             }
 
+            (
+                ty::ConstKind::Infer(InferConst::EffectVar(a_vid)),
+                ty::ConstKind::Infer(InferConst::EffectVar(b_vid)),
+            ) => {
+                self.inner
+                    .borrow_mut()
+                    .effect_unification_table()
+                    .unify_var_var(a_vid, b_vid)
+                    .map_err(|a| effect_unification_error(self.tcx, relation.a_is_expected(), a))?;
+                return Ok(a);
+            }
+
             // All other cases of inference with other variables are errors.
-            (ty::ConstKind::Infer(InferConst::Var(_)), ty::ConstKind::Infer(_))
-            | (ty::ConstKind::Infer(_), ty::ConstKind::Infer(InferConst::Var(_))) => {
-                bug!("tried to combine ConstKind::Infer/ConstKind::Infer(InferConst::Var)")
+            (
+                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)), _) => {
@@ -223,6 +243,23 @@ impl<'tcx> InferCtxt<'tcx> {
             (_, ty::ConstKind::Infer(InferConst::Var(vid))) => {
                 return self.unify_const_variable(vid, a, relation.param_env());
             }
+
+            (ty::ConstKind::Infer(InferConst::EffectVar(vid)), _) => {
+                return self.unify_effect_variable(
+                    relation.a_is_expected(),
+                    vid,
+                    EffectVarValue::Const(b),
+                );
+            }
+
+            (_, ty::ConstKind::Infer(InferConst::EffectVar(vid))) => {
+                return self.unify_effect_variable(
+                    !relation.a_is_expected(),
+                    vid,
+                    EffectVarValue::Const(a),
+                );
+            }
+
             (ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..))
                 if self.tcx.features().generic_const_exprs || self.next_trait_solver() =>
             {
@@ -340,6 +377,20 @@ impl<'tcx> InferCtxt<'tcx> {
             .map_err(|e| float_unification_error(vid_is_expected, e))?;
         Ok(Ty::new_float(self.tcx, val))
     }
+
+    fn unify_effect_variable(
+        &self,
+        vid_is_expected: bool,
+        vid: ty::EffectVid<'tcx>,
+        val: EffectVarValue<'tcx>,
+    ) -> RelateResult<'tcx, ty::Const<'tcx>> {
+        self.inner
+            .borrow_mut()
+            .effect_unification_table()
+            .unify_var_value(vid, Some(val))
+            .map_err(|e| effect_unification_error(self.tcx, vid_is_expected, e))?;
+        Ok(val.as_const(self.tcx))
+    }
 }
 
 impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
@@ -493,3 +544,11 @@ fn float_unification_error<'tcx>(
     let (ty::FloatVarValue(a), ty::FloatVarValue(b)) = v;
     TypeError::FloatMismatch(ExpectedFound::new(a_is_expected, a, b))
 }
+
+fn effect_unification_error<'tcx>(
+    _tcx: TyCtxt<'tcx>,
+    _a_is_expected: bool,
+    (_a, _b): (EffectVarValue<'tcx>, EffectVarValue<'tcx>),
+) -> TypeError<'tcx> {
+    bug!("unexpected effect unification error")
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index ac5468f3dfd..6dba5d2945d 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -1642,8 +1642,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     ValuePairs::Terms(infer::ExpectedFound { expected, found }) => {
                         match (expected.unpack(), found.unpack()) {
                             (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => {
-                                let is_simple_err =
-                                    expected.is_simple_text() && found.is_simple_text();
+                                let is_simple_err = expected.is_simple_text(self.tcx)
+                                    && found.is_simple_text(self.tcx);
                                 OpaqueTypesVisitor::visit_expected_found(
                                     self.tcx, expected, found, span,
                                 )
@@ -1885,7 +1885,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         }
                         s
                     };
-                    if !(values.expected.is_simple_text() && values.found.is_simple_text())
+                    if !(values.expected.is_simple_text(self.tcx) && values.found.is_simple_text(self.tcx))
                         || (exp_found.is_some_and(|ef| {
                             // This happens when the type error is a subset of the expectation,
                             // like when you have two references but one is `usize` and the other
diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs
index 689945d644c..0596ce373cf 100644
--- a/compiler/rustc_infer/src/infer/freshen.rs
+++ b/compiler/rustc_infer/src/infer/freshen.rs
@@ -156,6 +156,21 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for TypeFreshener<'a, 'tcx> {
                     .known();
                 self.freshen_const(opt_ct, ty::InferConst::Var(v), ty::InferConst::Fresh, ct.ty())
             }
+            ty::ConstKind::Infer(ty::InferConst::EffectVar(v)) => {
+                let opt_ct = self
+                    .infcx
+                    .inner
+                    .borrow_mut()
+                    .effect_unification_table()
+                    .probe_value(v)
+                    .map(|effect| effect.as_const(self.infcx.tcx));
+                self.freshen_const(
+                    opt_ct,
+                    ty::InferConst::EffectVar(v),
+                    ty::InferConst::Fresh,
+                    ct.ty(),
+                )
+            }
             ty::ConstKind::Infer(ty::InferConst::Fresh(i)) => {
                 if i >= self.const_freshen_count {
                     bug!(
diff --git a/compiler/rustc_infer/src/infer/generalize.rs b/compiler/rustc_infer/src/infer/generalize.rs
index cf674d5dda6..dd7f8d35441 100644
--- a/compiler/rustc_infer/src/infer/generalize.rs
+++ b/compiler/rustc_infer/src/infer/generalize.rs
@@ -403,6 +403,7 @@ where
                     }
                 }
             }
+            ty::ConstKind::Infer(InferConst::EffectVar(_)) => Ok(c),
             // FIXME: remove this branch once `structurally_relate_consts` is fully
             // structural.
             ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => {
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index aaabf1482e2..3706c9646af 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -21,7 +21,7 @@ use rustc_data_structures::unify as ut;
 use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
-use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
+use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue, EffectVarValue};
 use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType};
 use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult};
 use rustc_middle::mir::ConstraintCategory;
@@ -33,13 +33,14 @@ use rustc_middle::ty::relate::RelateResult;
 use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
 pub use rustc_middle::ty::IntVarValue;
 use rustc_middle::ty::{self, GenericParamDefKind, InferConst, InferTy, Ty, TyCtxt};
-use rustc_middle::ty::{ConstVid, FloatVid, IntVid, TyVid};
+use rustc_middle::ty::{ConstVid, EffectVid, FloatVid, IntVid, TyVid};
 use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgs, GenericArgsRef};
 use rustc_span::symbol::Symbol;
 use rustc_span::Span;
 
 use std::cell::{Cell, RefCell};
 use std::fmt;
+use std::marker::PhantomData;
 
 use self::combine::CombineFields;
 use self::error_reporting::TypeErrCtxt;
@@ -115,6 +116,9 @@ pub struct InferCtxtInner<'tcx> {
     /// Map from floating variable to the kind of float it represents.
     float_unification_storage: ut::UnificationTableStorage<ty::FloatVid>,
 
+    /// Map from effect variable to the effect param it represents.
+    effect_unification_storage: ut::UnificationTableStorage<ty::EffectVid<'tcx>>,
+
     /// Tracks the set of region variables and the constraints between them.
     ///
     /// This is initially `Some(_)` but when
@@ -172,6 +176,7 @@ impl<'tcx> InferCtxtInner<'tcx> {
             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()),
             region_obligations: vec![],
             opaque_type_storage: Default::default(),
@@ -223,6 +228,10 @@ impl<'tcx> InferCtxtInner<'tcx> {
         self.const_unification_storage.with_log(&mut self.undo_log)
     }
 
+    fn effect_unification_table(&mut self) -> UnificationTable<'_, 'tcx, ty::EffectVid<'tcx>> {
+        self.effect_unification_storage.with_log(&mut self.undo_log)
+    }
+
     #[inline]
     pub fn unwrap_region_constraints(&mut self) -> RegionConstraintCollector<'_, 'tcx> {
         self.region_constraint_storage
@@ -356,6 +365,7 @@ impl<'tcx> ty::InferCtxtLike<TyCtxt<'tcx>> for InferCtxt<'tcx> {
                 Err(universe) => Some(universe),
                 Ok(_) => None,
             },
+            EffectVar(_) => None,
             Fresh(_) => None,
         }
     }
@@ -764,19 +774,32 @@ impl<'tcx> InferCtxt<'tcx> {
             .collect();
         vars.extend(
             (0..inner.int_unification_table().len())
-                .map(|i| ty::IntVid { index: i as u32 })
+                .map(|i| ty::IntVid::from_u32(i as u32))
                 .filter(|&vid| inner.int_unification_table().probe_value(vid).is_none())
                 .map(|v| Ty::new_int_var(self.tcx, v)),
         );
         vars.extend(
             (0..inner.float_unification_table().len())
-                .map(|i| ty::FloatVid { index: i as u32 })
+                .map(|i| ty::FloatVid::from_u32(i as u32))
                 .filter(|&vid| inner.float_unification_table().probe_value(vid).is_none())
                 .map(|v| Ty::new_float_var(self.tcx, v)),
         );
         vars
     }
 
+    pub fn unsolved_effects(&self) -> Vec<ty::Const<'tcx>> {
+        let mut inner = self.inner.borrow_mut();
+        let mut table = inner.effect_unification_table();
+
+        (0..table.len())
+            .map(|i| ty::EffectVid { index: i as u32, phantom: PhantomData })
+            .filter(|&vid| table.probe_value(vid).is_none())
+            .map(|v| {
+                ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(v), self.tcx.types.bool)
+            })
+            .collect()
+    }
+
     fn combine_fields<'a>(
         &'a self,
         trace: TypeTrace<'tcx>,
@@ -1158,7 +1181,10 @@ impl<'tcx> InferCtxt<'tcx> {
 
                 Ty::new_var(self.tcx, ty_var_id).into()
             }
-            GenericParamDefKind::Const { .. } => {
+            GenericParamDefKind::Const { is_host_effect, .. } => {
+                if is_host_effect {
+                    return self.var_for_effect(param);
+                }
                 let origin = ConstVariableOrigin {
                     kind: ConstVariableOriginKind::ConstParameterDefinition(
                         param.name,
@@ -1184,6 +1210,17 @@ impl<'tcx> InferCtxt<'tcx> {
         }
     }
 
+    pub fn var_for_effect(&self, param: &ty::GenericParamDef) -> GenericArg<'tcx> {
+        let effect_vid = self.inner.borrow_mut().effect_unification_table().new_key(None);
+        let ty = self
+            .tcx
+            .type_of(param.def_id)
+            .no_bound_vars()
+            .expect("const parameter types cannot be generic");
+        debug_assert_eq!(self.tcx.types.bool, ty);
+        ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(effect_vid), ty).into()
+    }
+
     /// Given a set of generics defined on a type or impl, returns a substitution mapping each
     /// type/region parameter to a fresh inference variable.
     pub fn fresh_args_for_item(&self, span: Span, def_id: DefId) -> GenericArgsRef<'tcx> {
@@ -1298,6 +1335,10 @@ impl<'tcx> InferCtxt<'tcx> {
         self.inner.borrow_mut().const_unification_table().find(var)
     }
 
+    pub fn root_effect_var(&self, var: ty::EffectVid<'tcx>) -> ty::EffectVid<'tcx> {
+        self.inner.borrow_mut().effect_unification_table().find(var)
+    }
+
     /// Resolves an int var to a rigid int type, if it was constrained to one,
     /// or else the root int var in the unification table.
     pub fn opportunistic_resolve_int_var(&self, vid: ty::IntVid) -> Ty<'tcx> {
@@ -1369,6 +1410,10 @@ impl<'tcx> InferCtxt<'tcx> {
         }
     }
 
+    pub fn probe_effect_var(&self, vid: EffectVid<'tcx>) -> Option<EffectVarValue<'tcx>> {
+        self.inner.borrow_mut().effect_unification_table().probe_value(vid)
+    }
+
     /// Attempts to resolve all type/region/const variables in
     /// `value`. Region inference must have been run already (e.g.,
     /// by calling `resolve_regions_and_report_errors`). If some
@@ -1649,6 +1694,14 @@ impl<'tcx> InferCtxt<'tcx> {
                     ConstVariableValue::Known { .. } => true,
                 }
             }
+
+            TyOrConstInferVar::Effect(v) => {
+                // If `probe_value` returns `Some`, it never equals
+                // `ty::ConstKind::Infer(ty::InferConst::Effect(v))`.
+                //
+                // Not `inlined_probe_value(v)` because this call site is colder.
+                self.probe_effect_var(v).is_some()
+            }
         }
     }
 }
@@ -1720,6 +1773,8 @@ pub enum TyOrConstInferVar<'tcx> {
 
     /// Equivalent to `ty::ConstKind::Infer(ty::InferConst::Var(_))`.
     Const(ConstVid<'tcx>),
+    /// Equivalent to `ty::ConstKind::Infer(ty::InferConst::EffectVar(_))`.
+    Effect(EffectVid<'tcx>),
 }
 
 impl<'tcx> TyOrConstInferVar<'tcx> {
@@ -1750,6 +1805,7 @@ impl<'tcx> TyOrConstInferVar<'tcx> {
     fn maybe_from_const(ct: ty::Const<'tcx>) -> Option<Self> {
         match ct.kind() {
             ty::ConstKind::Infer(InferConst::Var(v)) => Some(TyOrConstInferVar::Const(v)),
+            ty::ConstKind::Infer(InferConst::EffectVar(v)) => Some(TyOrConstInferVar::Effect(v)),
             _ => None,
         }
     }
@@ -1793,17 +1849,24 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ShallowResolver<'a, 'tcx> {
     }
 
     fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
-        if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.kind() {
-            self.infcx
+        match ct.kind() {
+            ty::ConstKind::Infer(InferConst::Var(vid)) => self
+                .infcx
                 .inner
                 .borrow_mut()
                 .const_unification_table()
                 .probe_value(vid)
                 .val
                 .known()
-                .unwrap_or(ct)
-        } else {
-            ct
+                .unwrap_or(ct),
+            ty::ConstKind::Infer(InferConst::EffectVar(vid)) => self
+                .infcx
+                .inner
+                .borrow_mut()
+                .effect_unification_table()
+                .probe_value(vid)
+                .map_or(ct, |val| val.as_const(self.infcx.tcx)),
+            _ => ct,
         }
     }
 }
diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs
index 1c3a5c36076..09df93fcc2f 100644
--- a/compiler/rustc_infer/src/infer/opaque_types.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types.rs
@@ -145,7 +145,25 @@ impl<'tcx> InferCtxt<'tcx> {
                             return None;
                         }
                     }
-                    DefiningAnchor::Bubble => {}
+                    DefiningAnchor::Bubble => {
+                        if let ty::Alias(ty::Opaque, _) = b.kind() {
+                            // In bubble mode we don't know which of the two opaque types is supposed to have the other
+                            // as a hidden type (both, none or either one of them could be in its defining scope).
+                            let predicate = ty::PredicateKind::AliasRelate(
+                                a.into(),
+                                b.into(),
+                                ty::AliasRelationDirection::Equate,
+                            );
+                            let obligation = traits::Obligation::new(
+                                self.tcx,
+                                cause.clone(),
+                                param_env,
+                                predicate,
+                            );
+                            let obligations = vec![obligation];
+                            return Some(Ok(InferOk { value: (), obligations }));
+                        }
+                    }
                     DefiningAnchor::Error => return None,
                 };
                 if let ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }) = *b.kind() {
diff --git a/compiler/rustc_infer/src/infer/undo_log.rs b/compiler/rustc_infer/src/infer/undo_log.rs
index 25d06b21ec8..79144b3e643 100644
--- a/compiler/rustc_infer/src/infer/undo_log.rs
+++ b/compiler/rustc_infer/src/infer/undo_log.rs
@@ -24,6 +24,7 @@ pub(crate) enum UndoLog<'tcx> {
     ConstUnificationTable(sv::UndoLog<ut::Delegate<ty::ConstVid<'tcx>>>),
     IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
     FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
+    EffectUnificationTable(sv::UndoLog<ut::Delegate<ty::EffectVid<'tcx>>>),
     RegionConstraintCollector(region_constraints::UndoLog<'tcx>),
     RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
     ProjectionCache(traits::UndoLog<'tcx>),
@@ -55,6 +56,7 @@ impl_from! {
     IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
 
     FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
+    EffectUnificationTable(sv::UndoLog<ut::Delegate<ty::EffectVid<'tcx>>>),
 
     ConstUnificationTable(sv::UndoLog<ut::Delegate<ty::ConstVid<'tcx>>>),
 
@@ -71,6 +73,7 @@ impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> {
             UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo),
             UndoLog::IntUnificationTable(undo) => self.int_unification_storage.reverse(undo),
             UndoLog::FloatUnificationTable(undo) => self.float_unification_storage.reverse(undo),
+            UndoLog::EffectUnificationTable(undo) => self.effect_unification_storage.reverse(undo),
             UndoLog::RegionConstraintCollector(undo) => {
                 self.region_constraint_storage.as_mut().unwrap().reverse(undo)
             }
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index 5b417e008cf..a0f0b530bae 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -279,6 +279,12 @@ pub struct Config {
 
     /// Registry of diagnostics codes.
     pub registry: Registry,
+
+    /// All commandline args used to invoke the compiler, with @file args fully expanded.
+    /// This will only be used within debug info, e.g. in the pdb file on windows
+    /// This is mainly useful for other tools that reads that debuginfo to figure out
+    /// how to call the compiler with the same arguments.
+    pub expanded_args: Vec<String>,
 }
 
 // JUSTIFICATION: before session exists, only config
@@ -317,6 +323,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
                 config.make_codegen_backend,
                 registry.clone(),
                 config.ice_file,
+                config.expanded_args,
             );
 
             if let Some(parse_sess_created) = config.parse_sess_created {
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 9fcaf643179..e5ae6d5b5d6 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -584,7 +584,7 @@ fn resolver_for_lowering<'tcx>(
     let krate = configure_and_expand(krate, &pre_configured_attrs, &mut resolver);
 
     // Make sure we don't mutate the cstore from here on.
-    tcx.untracked().cstore.leak();
+    tcx.untracked().cstore.freeze();
 
     let ty::ResolverOutputs {
         global_ctxt: untracked_resolutions,
diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs
index e0d9998d919..449d7a29590 100644
--- a/compiler/rustc_interface/src/queries.rs
+++ b/compiler/rustc_interface/src/queries.rs
@@ -7,9 +7,7 @@ use rustc_codegen_ssa::traits::CodegenBackend;
 use rustc_codegen_ssa::CodegenResults;
 use rustc_data_structures::steal::Steal;
 use rustc_data_structures::svh::Svh;
-use rustc_data_structures::sync::{
-    AppendOnlyIndexVec, FreezeLock, Lrc, OnceLock, RwLock, WorkerLocal,
-};
+use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, Lrc, OnceLock, WorkerLocal};
 use rustc_hir::def_id::{StableCrateId, CRATE_DEF_ID, LOCAL_CRATE};
 use rustc_hir::definitions::Definitions;
 use rustc_incremental::DepGraphFuture;
@@ -116,6 +114,7 @@ impl<'tcx> Queries<'tcx> {
             .compute(|| passes::parse(self.session()).map_err(|mut parse_error| parse_error.emit()))
     }
 
+    #[deprecated = "pre_configure may be made private in the future. If you need it please open an issue with your use case."]
     pub fn pre_configure(&self) -> Result<QueryResult<'_, (ast::Crate, ast::AttrVec)>> {
         self.pre_configure.compute(|| {
             let mut krate = self.parse()?.steal();
@@ -173,6 +172,7 @@ impl<'tcx> Queries<'tcx> {
     pub fn global_ctxt(&'tcx self) -> Result<QueryResult<'_, &'tcx GlobalCtxt<'tcx>>> {
         self.gcx.compute(|| {
             let sess = self.session();
+            #[allow(deprecated)]
             let (krate, pre_configured_attrs) = self.pre_configure()?.steal();
 
             // parse `#[crate_name]` even if `--crate-name` was passed, to make sure it matches.
@@ -195,7 +195,7 @@ impl<'tcx> Queries<'tcx> {
                 self.compiler.register_lints.as_deref(),
                 &pre_configured_attrs,
             ));
-            let cstore = RwLock::new(Box::new(CStore::new(
+            let cstore = FreezeLock::new(Box::new(CStore::new(
                 self.codegen_backend().metadata_loader(),
                 stable_crate_id,
             )) as _);
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index b53ba251bcd..1746cc8b719 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -68,6 +68,7 @@ fn mk_session(handler: &mut EarlyErrorHandler, matches: getopts::Matches) -> (Se
         None,
         "",
         None,
+        Default::default(),
     );
     (sess, cfg)
 }
@@ -703,7 +704,7 @@ fn test_unstable_options_tracking_hash() {
     untracked!(keep_hygiene_data, true);
     untracked!(link_native_libraries, false);
     untracked!(llvm_time_trace, true);
-    untracked!(ls, true);
+    untracked!(ls, vec!["all".to_owned()]);
     untracked!(macro_backtrace, true);
     untracked!(meta_stats, true);
     untracked!(mir_include_spans, true);
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 5c9b39cdbe3..37e242c6e40 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -71,6 +71,7 @@ pub fn create_session(
     >,
     descriptions: Registry,
     ice_file: Option<PathBuf>,
+    expanded_args: Vec<String>,
 ) -> (Session, Box<dyn CodegenBackend>) {
     let codegen_backend = if let Some(make_codegen_backend) = make_codegen_backend {
         make_codegen_backend(&sopts)
@@ -113,6 +114,7 @@ pub fn create_session(
         target_override,
         rustc_version_str().unwrap_or("unknown"),
         ice_file,
+        expanded_args,
     );
 
     codegen_backend.init(&sess);
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 7f7f36ca170..7377c6e2f35 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -156,15 +156,6 @@ lint_builtin_unused_doc_comment = unused doc comment
 lint_builtin_while_true = denote infinite loops with `loop {"{"} ... {"}"}`
     .suggestion = use `loop`
 
-lint_check_name_deprecated = lint name `{$lint_name}` is deprecated and does not have an effect anymore. Use: {$new_name}
-
-lint_check_name_removed = lint `{$lint_name}` has been removed: {$reason}
-
-lint_check_name_renamed = lint `{$lint_name}` has been renamed to `{$replace}`
-
-lint_check_name_unknown = unknown lint: `{$lint_name}`
-    .help = did you mean: `{$suggestion}`
-
 lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}`
 
 lint_command_line_source = `forbid` lint level was set on command line
@@ -187,6 +178,7 @@ lint_default_source = `forbid` lint level is the default for {$id}
 lint_deprecated_lint_name =
     lint name `{$name}` is deprecated and may not have an effect in the future.
     .suggestion = change it to
+    .help = change it to {$replace}
 
 lint_diag_out_of_impl =
     diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls
@@ -461,6 +453,8 @@ lint_ptr_null_checks_fn_ptr = function pointers are not nullable, so checking th
     .help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
     .label = expression has type `{$orig_ty}`
 
+lint_ptr_null_checks_fn_ret = returned pointer of `{$fn_name}` call is never null, so checking it for null will always return false
+
 lint_ptr_null_checks_ref = references are not nullable, so checking them for null will always return false
     .label = expression has type `{$orig_ty}`
 
@@ -528,6 +522,7 @@ lint_unknown_gated_lint =
 lint_unknown_lint =
     unknown lint: `{$name}`
     .suggestion = did you mean
+    .help = did you mean: `{$replace}`
 
 lint_unknown_tool_in_scoped_lint = unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}`
     .help = add `#![register_tool({$tool_name})]` to the crate root
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 9dfde84b552..7a336a8f694 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -16,10 +16,6 @@
 
 use self::TargetLint::*;
 
-use crate::errors::{
-    CheckNameDeprecated, CheckNameRemoved, CheckNameRenamed, CheckNameUnknown,
-    CheckNameUnknownTool, RequestedLevel, UnsupportedGroup,
-};
 use crate::levels::LintLevelsBuilder;
 use crate::passes::{EarlyLintPassObject, LateLintPassObject};
 use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
@@ -330,58 +326,6 @@ impl LintStore {
         }
     }
 
-    /// Checks the validity of lint names derived from the command line.
-    pub fn check_lint_name_cmdline(
-        &self,
-        sess: &Session,
-        lint_name: &str,
-        level: Level,
-        registered_tools: &RegisteredTools,
-    ) {
-        let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
-        if lint_name_only == crate::WARNINGS.name_lower() && matches!(level, Level::ForceWarn(_)) {
-            sess.emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
-            return;
-        }
-        match self.check_lint_name(lint_name_only, tool_name, registered_tools) {
-            CheckLintNameResult::Renamed(replace) => {
-                sess.emit_warning(CheckNameRenamed {
-                    lint_name,
-                    replace: &replace,
-                    sub: RequestedLevel { level, lint_name },
-                });
-            }
-            CheckLintNameResult::Removed(reason) => {
-                sess.emit_warning(CheckNameRemoved {
-                    lint_name,
-                    reason: &reason,
-                    sub: RequestedLevel { level, lint_name },
-                });
-            }
-            CheckLintNameResult::NoLint(suggestion) => {
-                sess.emit_err(CheckNameUnknown {
-                    lint_name,
-                    suggestion,
-                    sub: RequestedLevel { level, lint_name },
-                });
-            }
-            CheckLintNameResult::Tool(Err((Some(_), new_name))) => {
-                sess.emit_warning(CheckNameDeprecated {
-                    lint_name,
-                    new_name: &new_name,
-                    sub: RequestedLevel { level, lint_name },
-                });
-            }
-            CheckLintNameResult::NoTool => {
-                sess.emit_err(CheckNameUnknownTool {
-                    tool_name: tool_name.unwrap(),
-                    sub: RequestedLevel { level, lint_name },
-                });
-            }
-            _ => {}
-        };
-    }
-
     /// True if this symbol represents a lint group name.
     pub fn is_lint_group(&self, lint_name: Symbol) -> bool {
         debug!(
@@ -1402,14 +1346,3 @@ impl<'tcx> LayoutOfHelpers<'tcx> for LateContext<'tcx> {
         err
     }
 }
-
-pub fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {
-    match lint_name.split_once("::") {
-        Some((tool_name, lint_name)) => {
-            let tool_name = Symbol::intern(tool_name);
-
-            (Some(tool_name), lint_name)
-        }
-        None => (None, lint_name),
-    }
-}
diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs
index 607875b3faa..eccea35c702 100644
--- a/compiler/rustc_lint/src/errors.rs
+++ b/compiler/rustc_lint/src/errors.rs
@@ -1,7 +1,5 @@
 use crate::fluent_generated as fluent;
-use rustc_errors::{
-    AddToDiagnostic, Diagnostic, ErrorGuaranteed, Handler, IntoDiagnostic, SubdiagnosticMessage,
-};
+use rustc_errors::{AddToDiagnostic, Diagnostic, SubdiagnosticMessage};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_session::lint::Level;
 use rustc_span::{Span, Symbol};
@@ -102,29 +100,6 @@ pub struct UnsupportedGroup {
     pub lint_group: String,
 }
 
-pub struct CheckNameUnknown<'a> {
-    pub lint_name: &'a str,
-    pub suggestion: Option<Symbol>,
-    pub sub: RequestedLevel<'a>,
-}
-
-impl IntoDiagnostic<'_> for CheckNameUnknown<'_> {
-    fn into_diagnostic(
-        self,
-        handler: &Handler,
-    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = handler.struct_err(fluent::lint_check_name_unknown);
-        diag.code(rustc_errors::error_code!(E0602));
-        if let Some(suggestion) = self.suggestion {
-            diag.help(fluent::lint_help);
-            diag.set_arg("suggestion", suggestion);
-        }
-        diag.set_arg("lint_name", self.lint_name);
-        diag.subdiagnostic(self.sub);
-        diag
-    }
-}
-
 #[derive(Diagnostic)]
 #[diag(lint_check_name_unknown_tool, code = "E0602")]
 pub struct CheckNameUnknownTool<'a> {
@@ -132,30 +107,3 @@ pub struct CheckNameUnknownTool<'a> {
     #[subdiagnostic]
     pub sub: RequestedLevel<'a>,
 }
-
-#[derive(Diagnostic)]
-#[diag(lint_check_name_renamed)]
-pub struct CheckNameRenamed<'a> {
-    pub lint_name: &'a str,
-    pub replace: &'a str,
-    #[subdiagnostic]
-    pub sub: RequestedLevel<'a>,
-}
-
-#[derive(Diagnostic)]
-#[diag(lint_check_name_removed)]
-pub struct CheckNameRemoved<'a> {
-    pub lint_name: &'a str,
-    pub reason: &'a str,
-    #[subdiagnostic]
-    pub sub: RequestedLevel<'a>,
-}
-
-#[derive(Diagnostic)]
-#[diag(lint_check_name_deprecated)]
-pub struct CheckNameDeprecated<'a> {
-    pub lint_name: &'a str,
-    pub new_name: &'a str,
-    #[subdiagnostic]
-    pub sub: RequestedLevel<'a>,
-}
diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs
index 73af51d9e90..6c8b60c8d74 100644
--- a/compiler/rustc_lint/src/late.rs
+++ b/compiler/rustc_lint/src/late.rs
@@ -21,7 +21,6 @@ use rustc_data_structures::sync::join;
 use rustc_hir as hir;
 use rustc_hir::def_id::{LocalDefId, LocalModDefId};
 use rustc_hir::intravisit as hir_visit;
-use rustc_hir::intravisit::Visitor;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::lint::LintPass;
@@ -61,6 +60,9 @@ impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> {
         self.context.last_node_with_lint_attrs = id;
         debug!("late context: enter_attrs({:?})", attrs);
         lint_callback!(self, enter_lint_attrs, attrs);
+        for attr in attrs {
+            lint_callback!(self, check_attribute, attr);
+        }
         f(self);
         debug!("late context: exit_attrs({:?})", attrs);
         lint_callback!(self, exit_lint_attrs, attrs);
@@ -157,6 +159,10 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
         hir_visit::walk_pat(self, p);
     }
 
+    fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) {
+        self.with_lint_attrs(field.hir_id, |cx| hir_visit::walk_expr_field(cx, field))
+    }
+
     fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
         ensure_sufficient_stack(|| {
             self.with_lint_attrs(e.hir_id, |cx| {
@@ -377,20 +383,18 @@ fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>(
 
     let (module, _span, hir_id) = tcx.hir().get_module(module_def_id);
 
-    // There is no module lint that will have the crate itself as an item, so check it here.
-    if hir_id == hir::CRATE_HIR_ID {
-        lint_callback!(cx, check_crate,);
-    }
+    cx.with_lint_attrs(hir_id, |cx| {
+        // There is no module lint that will have the crate itself as an item, so check it here.
+        if hir_id == hir::CRATE_HIR_ID {
+            lint_callback!(cx, check_crate,);
+        }
 
-    cx.process_mod(module, hir_id);
+        cx.process_mod(module, hir_id);
 
-    // Visit the crate attributes
-    if hir_id == hir::CRATE_HIR_ID {
-        for attr in tcx.hir().attrs(hir::CRATE_HIR_ID).iter() {
-            cx.visit_attribute(attr)
+        if hir_id == hir::CRATE_HIR_ID {
+            lint_callback!(cx, check_crate_post,);
         }
-        lint_callback!(cx, check_crate_post,);
-    }
+    });
 }
 
 fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) {
@@ -431,7 +435,6 @@ fn late_lint_crate_inner<'tcx, T: LateLintPass<'tcx>>(
         // item), warn for it here.
         lint_callback!(cx, check_crate,);
         tcx.hir().walk_toplevel_module(cx);
-        tcx.hir().walk_attributes(cx);
         lint_callback!(cx, check_crate_post,);
     })
 }
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index f58782c0f22..ba521b969ce 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -1,3 +1,8 @@
+use crate::errors::{CheckNameUnknownTool, RequestedLevel, UnsupportedGroup};
+use crate::lints::{
+    DeprecatedLintNameFromCommandLine, RemovedLintFromCommandLine, RenamedLintFromCommandLine,
+    UnknownLintFromCommandLine,
+};
 use crate::{
     builtin::MISSING_DOCS,
     context::{CheckLintNameResult, LintStore},
@@ -333,6 +338,11 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
         intravisit::walk_expr(self, e);
     }
 
+    fn visit_expr_field(&mut self, f: &'tcx hir::ExprField<'tcx>) {
+        self.add_id(f.hir_id);
+        intravisit::walk_expr_field(self, f);
+    }
+
     fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
         self.add_id(s.hir_id);
         intravisit::walk_field_def(self, s);
@@ -552,12 +562,55 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
 
     fn add_command_line(&mut self) {
         for &(ref lint_name, level) in &self.sess.opts.lint_opts {
-            self.store.check_lint_name_cmdline(self.sess, &lint_name, level, self.registered_tools);
+            // Checks the validity of lint names derived from the command line.
+            let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
+            if lint_name_only == crate::WARNINGS.name_lower()
+                && matches!(level, Level::ForceWarn(_))
+            {
+                self.sess.emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
+            }
+            match self.store.check_lint_name(lint_name_only, tool_name, self.registered_tools) {
+                CheckLintNameResult::Renamed(ref replace) => {
+                    let name = lint_name.as_str();
+                    let suggestion = RenamedLintSuggestion::WithoutSpan { replace };
+                    let requested_level = RequestedLevel { level, lint_name };
+                    let lint = RenamedLintFromCommandLine { name, suggestion, requested_level };
+                    self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
+                }
+                CheckLintNameResult::Removed(ref reason) => {
+                    let name = lint_name.as_str();
+                    let requested_level = RequestedLevel { level, lint_name };
+                    let lint = RemovedLintFromCommandLine { name, reason, requested_level };
+                    self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
+                }
+                CheckLintNameResult::NoLint(suggestion) => {
+                    let name = lint_name.clone();
+                    let suggestion =
+                        suggestion.map(|replace| UnknownLintSuggestion::WithoutSpan { replace });
+                    let requested_level = RequestedLevel { level, lint_name };
+                    let lint = UnknownLintFromCommandLine { name, suggestion, requested_level };
+                    self.emit_lint(UNKNOWN_LINTS, lint);
+                }
+                CheckLintNameResult::Tool(Err((Some(_), ref replace))) => {
+                    let name = lint_name.clone();
+                    let requested_level = RequestedLevel { level, lint_name };
+                    let lint = DeprecatedLintNameFromCommandLine { name, replace, requested_level };
+                    self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
+                }
+                CheckLintNameResult::NoTool => {
+                    self.sess.emit_err(CheckNameUnknownTool {
+                        tool_name: tool_name.unwrap(),
+                        sub: RequestedLevel { level, lint_name },
+                    });
+                }
+                _ => {}
+            };
+
             let orig_level = level;
             let lint_flag_val = Symbol::intern(lint_name);
 
             let Ok(ids) = self.store.find_lints(&lint_name) else {
-                // errors handled in check_lint_name_cmdline above
+                // errors already handled above
                 continue;
             };
             for id in ids {
@@ -915,24 +968,18 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
 
                     _ if !self.warn_about_weird_lints => {}
 
-                    CheckLintNameResult::Renamed(new_name) => {
+                    CheckLintNameResult::Renamed(ref replace) => {
                         let suggestion =
-                            RenamedLintSuggestion { suggestion: sp, replace: new_name.as_str() };
+                            RenamedLintSuggestion::WithSpan { suggestion: sp, replace };
                         let name = tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
-                        self.emit_spanned_lint(
-                            RENAMED_AND_REMOVED_LINTS,
-                            sp.into(),
-                            RenamedLint { name: name.as_str(), suggestion },
-                        );
+                        let lint = RenamedLint { name: name.as_str(), suggestion };
+                        self.emit_spanned_lint(RENAMED_AND_REMOVED_LINTS, sp.into(), lint);
                     }
 
-                    CheckLintNameResult::Removed(reason) => {
+                    CheckLintNameResult::Removed(ref reason) => {
                         let name = tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
-                        self.emit_spanned_lint(
-                            RENAMED_AND_REMOVED_LINTS,
-                            sp.into(),
-                            RemovedLint { name: name.as_str(), reason: reason.as_str() },
-                        );
+                        let lint = RemovedLint { name: name.as_str(), reason };
+                        self.emit_spanned_lint(RENAMED_AND_REMOVED_LINTS, sp.into(), lint);
                     }
 
                     CheckLintNameResult::NoLint(suggestion) => {
@@ -941,13 +988,11 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                         } else {
                             name.to_string()
                         };
-                        let suggestion = suggestion
-                            .map(|replace| UnknownLintSuggestion { suggestion: sp, replace });
-                        self.emit_spanned_lint(
-                            UNKNOWN_LINTS,
-                            sp.into(),
-                            UnknownLint { name, suggestion },
-                        );
+                        let suggestion = suggestion.map(|replace| {
+                            UnknownLintSuggestion::WithSpan { suggestion: sp, replace }
+                        });
+                        let lint = UnknownLint { name, suggestion };
+                        self.emit_spanned_lint(UNKNOWN_LINTS, sp.into(), lint);
                     }
                 }
                 // If this lint was renamed, apply the new lint instead of ignoring the attribute.
@@ -1092,3 +1137,14 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
 pub(crate) fn provide(providers: &mut Providers) {
     *providers = Providers { shallow_lint_levels_on, lint_expectations, ..*providers };
 }
+
+pub fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {
+    match lint_name.split_once("::") {
+        Some((tool_name, lint_name)) => {
+            let tool_name = Symbol::intern(tool_name);
+
+            (Some(tool_name), lint_name)
+        }
+        None => (None, lint_name),
+    }
+}
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 0e942d774a7..c091c260a47 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -2,6 +2,7 @@
 #![allow(rustc::diagnostic_outside_of_impl)]
 use std::num::NonZeroU32;
 
+use crate::errors::RequestedLevel;
 use crate::fluent_generated as fluent;
 use rustc_errors::{
     AddToDiagnostic, Applicability, DecorateLint, DiagnosticMessage, DiagnosticStyledString,
@@ -634,6 +635,8 @@ pub enum PtrNullChecksDiag<'a> {
         #[label]
         label: Span,
     },
+    #[diag(lint_ptr_null_checks_fn_ret)]
+    FnRet { fn_name: Ident },
 }
 
 // for_loops_over_fallibles.rs
@@ -1013,6 +1016,16 @@ pub struct DeprecatedLintName<'a> {
 }
 
 #[derive(LintDiagnostic)]
+#[diag(lint_deprecated_lint_name)]
+#[help]
+pub struct DeprecatedLintNameFromCommandLine<'a> {
+    pub name: String,
+    pub replace: &'a str,
+    #[subdiagnostic]
+    pub requested_level: RequestedLevel<'a>,
+}
+
+#[derive(LintDiagnostic)]
 #[diag(lint_renamed_lint)]
 pub struct RenamedLint<'a> {
     pub name: &'a str,
@@ -1021,11 +1034,25 @@ pub struct RenamedLint<'a> {
 }
 
 #[derive(Subdiagnostic)]
-#[suggestion(lint_suggestion, code = "{replace}", applicability = "machine-applicable")]
-pub struct RenamedLintSuggestion<'a> {
-    #[primary_span]
-    pub suggestion: Span,
-    pub replace: &'a str,
+pub enum RenamedLintSuggestion<'a> {
+    #[suggestion(lint_suggestion, code = "{replace}", applicability = "machine-applicable")]
+    WithSpan {
+        #[primary_span]
+        suggestion: Span,
+        replace: &'a str,
+    },
+    #[help(lint_help)]
+    WithoutSpan { replace: &'a str },
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_renamed_lint)]
+pub struct RenamedLintFromCommandLine<'a> {
+    pub name: &'a str,
+    #[subdiagnostic]
+    pub suggestion: RenamedLintSuggestion<'a>,
+    #[subdiagnostic]
+    pub requested_level: RequestedLevel<'a>,
 }
 
 #[derive(LintDiagnostic)]
@@ -1036,6 +1063,15 @@ pub struct RemovedLint<'a> {
 }
 
 #[derive(LintDiagnostic)]
+#[diag(lint_removed_lint)]
+pub struct RemovedLintFromCommandLine<'a> {
+    pub name: &'a str,
+    pub reason: &'a str,
+    #[subdiagnostic]
+    pub requested_level: RequestedLevel<'a>,
+}
+
+#[derive(LintDiagnostic)]
 #[diag(lint_unknown_lint)]
 pub struct UnknownLint {
     pub name: String,
@@ -1044,11 +1080,25 @@ pub struct UnknownLint {
 }
 
 #[derive(Subdiagnostic)]
-#[suggestion(lint_suggestion, code = "{replace}", applicability = "maybe-incorrect")]
-pub struct UnknownLintSuggestion {
-    #[primary_span]
-    pub suggestion: Span,
-    pub replace: Symbol,
+pub enum UnknownLintSuggestion {
+    #[suggestion(lint_suggestion, code = "{replace}", applicability = "maybe-incorrect")]
+    WithSpan {
+        #[primary_span]
+        suggestion: Span,
+        replace: Symbol,
+    },
+    #[help(lint_help)]
+    WithoutSpan { replace: Symbol },
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_unknown_lint, code = "E0602")]
+pub struct UnknownLintFromCommandLine<'a> {
+    pub name: String,
+    #[subdiagnostic]
+    pub suggestion: Option<UnknownLintSuggestion>,
+    #[subdiagnostic]
+    pub requested_level: RequestedLevel<'a>,
 }
 
 #[derive(LintDiagnostic)]
diff --git a/compiler/rustc_lint/src/ptr_nulls.rs b/compiler/rustc_lint/src/ptr_nulls.rs
index 02aff91032f..0de72d8d3db 100644
--- a/compiler/rustc_lint/src/ptr_nulls.rs
+++ b/compiler/rustc_lint/src/ptr_nulls.rs
@@ -31,12 +31,30 @@ declare_lint! {
 
 declare_lint_pass!(PtrNullChecks => [USELESS_PTR_NULL_CHECKS]);
 
-/// This function detects and returns the original expression from a series of consecutive casts,
-/// ie. `(my_fn as *const _ as *mut _).cast_mut()` would return the expression for `my_fn`.
-fn ptr_cast_chain<'a>(cx: &'a LateContext<'_>, mut e: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
+/// This function checks if the expression is from a series of consecutive casts,
+/// ie. `(my_fn as *const _ as *mut _).cast_mut()` and whether the original expression is either
+/// a fn ptr, a reference, or a function call whose definition is
+/// annotated with `#![rustc_never_returns_null_ptr]`.
+/// If this situation is present, the function returns the appropriate diagnostic.
+fn incorrect_check<'a, 'tcx: 'a>(
+    cx: &'a LateContext<'tcx>,
+    mut e: &'a Expr<'a>,
+) -> Option<PtrNullChecksDiag<'tcx>> {
     let mut had_at_least_one_cast = false;
     loop {
         e = e.peel_blocks();
+        if let ExprKind::MethodCall(_, _expr, [], _) = e.kind
+            && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
+            && cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr)
+            && let Some(fn_name) = cx.tcx.opt_item_ident(def_id) {
+            return Some(PtrNullChecksDiag::FnRet { fn_name });
+        } else if let ExprKind::Call(path, _args) = e.kind
+            && let ExprKind::Path(ref qpath) = path.kind
+            && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
+            && cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr)
+            && let Some(fn_name) = cx.tcx.opt_item_ident(def_id) {
+            return Some(PtrNullChecksDiag::FnRet { fn_name });
+        }
         e = if let ExprKind::Cast(expr, t) = e.kind
             && let TyKind::Ptr(_) = t.kind {
             had_at_least_one_cast = true;
@@ -46,33 +64,21 @@ fn ptr_cast_chain<'a>(cx: &'a LateContext<'_>, mut e: &'a Expr<'a>) -> Option<&'
             && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_cast | sym::ptr_cast_mut)) {
             had_at_least_one_cast = true;
             expr
-        } else if let ExprKind::Call(path, [arg]) = e.kind
-            && let ExprKind::Path(ref qpath) = path.kind
-            && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
-            && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_from_ref | sym::ptr_from_mut)) {
-            had_at_least_one_cast = true;
-            arg
         } else if had_at_least_one_cast {
-            return Some(e);
+            let orig_ty = cx.typeck_results().expr_ty(e);
+            return if orig_ty.is_fn() {
+                Some(PtrNullChecksDiag::FnPtr { orig_ty, label: e.span })
+            } else if orig_ty.is_ref() {
+                Some(PtrNullChecksDiag::Ref { orig_ty, label: e.span })
+            } else {
+                None
+            };
         } else {
             return None;
         };
     }
 }
 
-fn incorrect_check<'a>(cx: &LateContext<'a>, expr: &Expr<'_>) -> Option<PtrNullChecksDiag<'a>> {
-    let expr = ptr_cast_chain(cx, expr)?;
-
-    let orig_ty = cx.typeck_results().expr_ty(expr);
-    if orig_ty.is_fn() {
-        Some(PtrNullChecksDiag::FnPtr { orig_ty, label: expr.span })
-    } else if orig_ty.is_ref() {
-        Some(PtrNullChecksDiag::Ref { orig_ty, label: expr.span })
-    } else {
-        None
-    }
-}
-
 impl<'tcx> LateLintPass<'tcx> for PtrNullChecks {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         match expr.kind {
diff --git a/compiler/rustc_lint/src/reference_casting.rs b/compiler/rustc_lint/src/reference_casting.rs
index 883f6242b56..d540f473ae8 100644
--- a/compiler/rustc_lint/src/reference_casting.rs
+++ b/compiler/rustc_lint/src/reference_casting.rs
@@ -128,7 +128,11 @@ fn is_operation_we_care_about<'tcx>(
 fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
     let e = e.peel_blocks();
 
-    fn from_casts<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+    fn from_casts<'tcx>(
+        cx: &LateContext<'tcx>,
+        e: &'tcx Expr<'tcx>,
+        need_check_freeze: &mut bool,
+    ) -> Option<&'tcx Expr<'tcx>> {
         // <expr> as *mut ...
         let mut e = if let ExprKind::Cast(e, t) = e.kind
             && let ty::RawPtr(TypeAndMut { mutbl: Mutability::Mut, .. }) = cx.typeck_results().node_type(t.hir_id).kind() {
@@ -138,6 +142,14 @@ fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>)
             && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
             && cx.tcx.is_diagnostic_item(sym::ptr_cast_mut, def_id) {
             expr
+        // UnsafeCell::raw_get(<expr>)
+        } else if let ExprKind::Call(path, [arg]) = e.kind
+            && let ExprKind::Path(ref qpath) = path.kind
+            && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
+            && cx.tcx.is_diagnostic_item(sym::unsafe_cell_raw_get, def_id)
+        {
+            *need_check_freeze = true;
+            arg
         } else {
             return None;
         };
@@ -160,11 +172,18 @@ fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>)
             {
                 had_at_least_one_cast = true;
                 expr
-            // ptr::from_ref(<expr>)
+            // ptr::from_ref(<expr>) or UnsafeCell::raw_get(<expr>)
             } else if let ExprKind::Call(path, [arg]) = e.kind
                 && let ExprKind::Path(ref qpath) = path.kind
                 && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
-                && cx.tcx.is_diagnostic_item(sym::ptr_from_ref, def_id) {
+                && matches!(
+                    cx.tcx.get_diagnostic_name(def_id),
+                    Some(sym::ptr_from_ref | sym::unsafe_cell_raw_get)
+                )
+            {
+                if cx.tcx.is_diagnostic_item(sym::unsafe_cell_raw_get, def_id) {
+                    *need_check_freeze = true;
+                }
                 return Some(arg);
             } else if had_at_least_one_cast {
                 return Some(e);
@@ -190,10 +209,25 @@ fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>)
         }
     }
 
-    let Some(e) = from_casts(cx, e).or_else(|| from_transmute(cx, e)) else {
+    let mut need_check_freeze = false;
+    let Some(e) = from_casts(cx, e, &mut need_check_freeze).or_else(|| from_transmute(cx, e))
+    else {
         return false;
     };
 
     let e = e.peel_blocks();
-    matches!(cx.typeck_results().node_type(e.hir_id).kind(), ty::Ref(_, _, Mutability::Not))
+    let node_type = cx.typeck_results().node_type(e.hir_id);
+    if let ty::Ref(_, inner_ty, Mutability::Not) = node_type.kind() {
+        // If an UnsafeCell method is involved we need to additionaly check the
+        // inner type for the presence of the Freeze trait (ie does NOT contain
+        // an UnsafeCell), since in that case we would incorrectly lint on valid casts.
+        //
+        // We also consider non concrete skeleton types (ie generics)
+        // to be an issue since there is no way to make it safe for abitrary types.
+        !need_check_freeze
+            || inner_ty.is_freeze(cx.tcx, cx.param_env)
+            || !inner_ty.has_concrete_skeleton()
+    } else {
+        false
+    }
 }
diff --git a/compiler/rustc_lint/src/tests.rs b/compiler/rustc_lint/src/tests.rs
index fc9d6f636b2..4fd054cb717 100644
--- a/compiler/rustc_lint/src/tests.rs
+++ b/compiler/rustc_lint/src/tests.rs
@@ -1,4 +1,4 @@
-use crate::context::parse_lint_and_tool_name;
+use crate::levels::parse_lint_and_tool_name;
 use rustc_span::{create_default_session_globals_then, Symbol};
 
 #[test]
diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs
index 56508a2a6cc..e812493b3dd 100644
--- a/compiler/rustc_lint/src/traits.rs
+++ b/compiler/rustc_lint/src/traits.rs
@@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
             };
             let def_id = trait_predicate.trait_ref.def_id;
             if cx.tcx.lang_items().drop_trait() == Some(def_id) {
-                // Explicitly allow `impl Drop`, a drop-guards-as-Voldemort-type pattern.
+                // Explicitly allow `impl Drop`, a drop-guards-as-unnameable-type pattern.
                 if trait_predicate.trait_ref.self_ty().is_impl_trait() {
                     continue;
                 }
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index fc4c29eb36d..57c2e289f20 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -917,13 +917,18 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
         // At this point, the field's type is known to be nonnull and the parent enum is Option-like.
         // If the computed size for the field and the enum are different, the nonnull optimization isn't
         // being applied (and we've got a problem somewhere).
-        let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, param_env).unwrap();
-        if !compute_size_skeleton(ty).same_size(compute_size_skeleton(field_ty)) {
+        let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, param_env).ok();
+        if !compute_size_skeleton(ty)?.same_size(compute_size_skeleton(field_ty)?) {
             bug!("improper_ctypes: Option nonnull optimization not applied?");
         }
 
         // Return the nullable type this Option-like enum can be safely represented with.
-        let field_ty_abi = &tcx.layout_of(param_env.and(field_ty)).unwrap().abi;
+        let field_ty_layout = tcx.layout_of(param_env.and(field_ty));
+        if field_ty_layout.is_err() && !field_ty.has_non_region_param() {
+            bug!("should be able to compute the layout of non-polymorphic type");
+        }
+
+        let field_ty_abi = &field_ty_layout.ok()?.abi;
         if let Abi::Scalar(field_ty_scalar) = field_ty_abi {
             match field_ty_scalar.valid_range(&tcx) {
                 WrappingRange { start: 0, end }
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 39e6fb805ae..d5beff4f101 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -816,8 +816,7 @@ trait UnusedDelimLint {
         let (value, ctx, followed_by_block, left_pos, right_pos, is_kw) = match e.kind {
             // Do not lint `unused_braces` in `if let` expressions.
             If(ref cond, ref block, _)
-                if !matches!(cond.kind, Let(_, _, _))
-                    || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
+                if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
             {
                 let left = e.span.lo() + rustc_span::BytePos(2);
                 let right = block.span.lo();
@@ -826,8 +825,7 @@ trait UnusedDelimLint {
 
             // Do not lint `unused_braces` in `while let` expressions.
             While(ref cond, ref block, ..)
-                if !matches!(cond.kind, Let(_, _, _))
-                    || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
+                if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
             {
                 let left = e.span.lo() + rustc_span::BytePos(5);
                 let right = block.span.lo();
@@ -1003,7 +1001,7 @@ impl UnusedDelimLint for UnusedParens {
                     self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
                 }
             }
-            ast::ExprKind::Let(_, ref expr, _) => {
+            ast::ExprKind::Let(_, ref expr, _, _) => {
                 self.check_unused_delims_expr(
                     cx,
                     expr,
@@ -1067,7 +1065,7 @@ impl EarlyLintPass for UnusedParens {
         }
 
         match e.kind {
-            ExprKind::Let(ref pat, _, _) | ExprKind::ForLoop(ref pat, ..) => {
+            ExprKind::Let(ref pat, _, _, _) | ExprKind::ForLoop(ref pat, ..) => {
                 self.check_unused_parens_pat(cx, pat, false, false, (true, true));
             }
             // We ignore parens in cases like `if (((let Some(0) = Some(1))))` because we already
@@ -1311,7 +1309,7 @@ impl UnusedDelimLint for UnusedBraces {
                     }
                 }
             }
-            ast::ExprKind::Let(_, ref expr, _) => {
+            ast::ExprKind::Let(_, ref expr, _, _) => {
                 self.check_unused_delims_expr(
                     cx,
                     expr,
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 7e745de4f1a..672a7a20148 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -2554,8 +2554,8 @@ declare_lint! {
     ///
     /// The fix to this is to wrap the unsafe code in an `unsafe` block.
     ///
-    /// This lint is "allow" by default since this will affect a large amount
-    /// of existing code, and the exact plan for increasing the severity is
+    /// This lint is "allow" by default on editions up to 2021, from 2024 it is
+    /// "warn" by default; the plan for increasing severity further is
     /// still being considered. See [RFC #2585] and [issue #71668] for more
     /// details.
     ///
@@ -2567,6 +2567,7 @@ declare_lint! {
     pub UNSAFE_OP_IN_UNSAFE_FN,
     Allow,
     "unsafe operations in unsafe functions without an explicit unsafe block are deprecated",
+    @edition Edition2024 => Warn;
 }
 
 declare_lint! {
@@ -3381,6 +3382,7 @@ declare_lint_pass! {
         PROC_MACRO_BACK_COMPAT,
         PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
         PUB_USE_OF_PRIVATE_EXTERN_CRATE,
+        REFINING_IMPL_TRAIT,
         RENAMED_AND_REMOVED_LINTS,
         REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
         RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES,
@@ -4363,7 +4365,7 @@ declare_lint! {
     ///     pub struct S;
     /// }
     ///
-    /// pub fn get_voldemort() -> m::S { m::S }
+    /// pub fn get_unnameable() -> m::S { m::S }
     /// # fn main() {}
     /// ```
     ///
@@ -4448,7 +4450,6 @@ declare_lint! {
     /// ### Example
     ///
     /// ```rust,compile_fail
-    ///
     /// #![deny(ambiguous_glob_imports)]
     /// pub fn foo() -> u32 {
     ///     use sub::*;
@@ -4485,6 +4486,50 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `refining_impl_trait` lint detects usages of return-position impl
+    /// traits in trait signatures which are refined by implementations.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![feature(return_position_impl_trait_in_trait)]
+    /// #![deny(refining_impl_trait)]
+    ///
+    /// use std::fmt::Display;
+    ///
+    /// pub trait AsDisplay {
+    ///     fn as_display(&self) -> impl Display;
+    /// }
+    ///
+    /// impl<'s> AsDisplay for &'s str {
+    ///     fn as_display(&self) -> Self {
+    ///         *self
+    ///     }
+    /// }
+    ///
+    /// fn main() {
+    ///     // users can observe that the return type of
+    ///     // `<&str as AsDisplay>::as_display()` is `&str`.
+    ///     let x: &str = "".as_display();
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Return-position impl trait in traits (RPITITs) desugar to associated types,
+    /// and callers of methods for types where the implementation is known are
+    /// able to observe the types written in the impl signature. This may be
+    /// intended behavior, but may also pose a semver hazard for authors of libraries
+    /// who do not wish to make stronger guarantees about the types than what is
+    /// written in the trait signature.
+    pub REFINING_IMPL_TRAIT,
+    Warn,
+    "impl trait in impl method signature does not match trait method signature",
+}
+
+declare_lint! {
     /// The `elided_lifetimes_in_associated_constant` lint detects elided lifetimes
     /// that were erroneously allowed in associated constants.
     ///
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 76e736859d3..ef2f7940477 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -719,36 +719,24 @@ macro_rules! declare_lint {
     ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr,
      $(@feature_gate = $gate:expr;)?
      $(@future_incompatible = FutureIncompatibleInfo { $($field:ident : $val:expr),* $(,)*  }; )?
+     $(@edition $lint_edition:ident => $edition_level:ident;)?
      $($v:ident),*) => (
         $(#[$attr])*
         $vis static $NAME: &$crate::Lint = &$crate::Lint {
             name: stringify!($NAME),
             default_level: $crate::$Level,
             desc: $desc,
-            edition_lint_opts: None,
             is_plugin: false,
             $($v: true,)*
-            $(feature_gate: Some($gate),)*
+            $(feature_gate: Some($gate),)?
             $(future_incompatible: Some($crate::FutureIncompatibleInfo {
                 $($field: $val,)*
                 ..$crate::FutureIncompatibleInfo::default_fields_for_macro()
-            }),)*
+            }),)?
+            $(edition_lint_opts: Some(($crate::Edition::$lint_edition, $crate::$edition_level)),)?
             ..$crate::Lint::default_fields_for_macro()
         };
     );
-    ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr,
-     $lint_edition: expr => $edition_level: ident
-    ) => (
-        $(#[$attr])*
-        $vis static $NAME: &$crate::Lint = &$crate::Lint {
-            name: stringify!($NAME),
-            default_level: $crate::$Level,
-            desc: $desc,
-            edition_lint_opts: Some(($lint_edition, $crate::Level::$edition_level)),
-            report_in_external_macro: false,
-            is_plugin: false,
-        };
-    );
 }
 
 #[macro_export]
diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
index 8f8f1d8c04f..461b5290e69 100644
--- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
@@ -1,5 +1,6 @@
 #include <stdio.h>
 
+#include <cstddef>
 #include <iomanip>
 #include <vector>
 #include <set>
@@ -9,6 +10,7 @@
 #include "llvm/Analysis/AliasAnalysis.h"
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/CodeGen/CommandFlags.h"
 #include "llvm/CodeGen/TargetSubtargetInfo.h"
 #include "llvm/IR/AutoUpgrade.h"
 #include "llvm/IR/AssemblyAnnotationWriter.h"
@@ -50,6 +52,8 @@
 
 using namespace llvm;
 
+static codegen::RegisterCodeGenFlags CGF;
+
 typedef struct LLVMOpaquePass *LLVMPassRef;
 typedef struct LLVMOpaqueTargetMachine *LLVMTargetMachineRef;
 
@@ -235,16 +239,22 @@ enum class LLVMRustCodeGenOptLevel {
   Aggressive,
 };
 
-static CodeGenOpt::Level fromRust(LLVMRustCodeGenOptLevel Level) {
+#if LLVM_VERSION_GE(18, 0)
+  using CodeGenOptLevelEnum = llvm::CodeGenOptLevel;
+#else
+  using CodeGenOptLevelEnum = llvm::CodeGenOpt::Level;
+#endif
+
+static CodeGenOptLevelEnum fromRust(LLVMRustCodeGenOptLevel Level) {
   switch (Level) {
   case LLVMRustCodeGenOptLevel::None:
-    return CodeGenOpt::None;
+    return CodeGenOptLevelEnum::None;
   case LLVMRustCodeGenOptLevel::Less:
-    return CodeGenOpt::Less;
+    return CodeGenOptLevelEnum::Less;
   case LLVMRustCodeGenOptLevel::Default:
-    return CodeGenOpt::Default;
+    return CodeGenOptLevelEnum::Default;
   case LLVMRustCodeGenOptLevel::Aggressive:
-    return CodeGenOpt::Aggressive;
+    return CodeGenOptLevelEnum::Aggressive;
   default:
     report_fatal_error("Bad CodeGenOptLevel.");
   }
@@ -406,7 +416,9 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
     bool RelaxELFRelocations,
     bool UseInitArray,
     const char *SplitDwarfFile,
-    bool ForceEmulatedTls) {
+    const char *DebugInfoCompression,
+    bool ForceEmulatedTls,
+    const char *ArgsCstrBuff, size_t ArgsCstrBuffLen) {
 
   auto OptLevel = fromRust(RustOptLevel);
   auto RM = fromRust(RustReloc);
@@ -421,7 +433,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
     return nullptr;
   }
 
-  TargetOptions Options;
+  TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags(Trip);
 
   Options.FloatABIType = FloatABI::Default;
   if (UseSoftFloat) {
@@ -436,6 +448,16 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
   if (SplitDwarfFile) {
       Options.MCOptions.SplitDwarfFile = SplitDwarfFile;
   }
+#if LLVM_VERSION_GE(16, 0)
+  if (!strcmp("zlib", DebugInfoCompression) && llvm::compression::zlib::isAvailable()) {
+    Options.CompressDebugSections = DebugCompressionType::Zlib;
+  } else if (!strcmp("zstd", DebugInfoCompression) && llvm::compression::zstd::isAvailable()) {
+    Options.CompressDebugSections = DebugCompressionType::Zstd;
+  } else if (!strcmp("none", DebugInfoCompression)) {
+    Options.CompressDebugSections = DebugCompressionType::None;
+  }
+#endif
+
   Options.RelaxELFRelocations = RelaxELFRelocations;
   Options.UseInitArray = UseInitArray;
 
@@ -462,12 +484,48 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
 
   Options.EmitStackSizeSection = EmitStackSizeSection;
 
+
+  if (ArgsCstrBuff != nullptr)
+  {
+    int buffer_offset = 0;
+    assert(ArgsCstrBuff[ArgsCstrBuffLen - 1] == '\0');
+
+    const size_t arg0_len = std::strlen(ArgsCstrBuff);
+    char* arg0 = new char[arg0_len + 1];
+    memcpy(arg0, ArgsCstrBuff, arg0_len);
+    arg0[arg0_len] = '\0';
+    buffer_offset += arg0_len + 1;
+
+    const int num_cmd_arg_strings =
+      std::count(&ArgsCstrBuff[buffer_offset], &ArgsCstrBuff[ArgsCstrBuffLen], '\0');
+
+    std::string* cmd_arg_strings = new std::string[num_cmd_arg_strings];
+    for (int i = 0; i < num_cmd_arg_strings; ++i)
+    {
+      assert(buffer_offset < ArgsCstrBuffLen);
+      const int len = std::strlen(ArgsCstrBuff + buffer_offset);
+      cmd_arg_strings[i] = std::string(&ArgsCstrBuff[buffer_offset], len);
+      buffer_offset += len + 1;
+    }
+
+    assert(buffer_offset == ArgsCstrBuffLen);
+
+    Options.MCOptions.Argv0 = arg0;
+    Options.MCOptions.CommandLineArgs =
+      llvm::ArrayRef<std::string>(cmd_arg_strings, num_cmd_arg_strings);
+  }
+
   TargetMachine *TM = TheTarget->createTargetMachine(
       Trip.getTriple(), CPU, Feature, Options, RM, CM, OptLevel);
   return wrap(TM);
 }
 
 extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) {
+
+  MCTargetOptions& MCOptions = unwrap(TM)->Options.MCOptions;
+  delete[] MCOptions.Argv0;
+  delete[] MCOptions.CommandLineArgs.data();
+
   delete unwrap(TM);
 }
 
@@ -502,9 +560,17 @@ enum class LLVMRustFileType {
 static CodeGenFileType fromRust(LLVMRustFileType Type) {
   switch (Type) {
   case LLVMRustFileType::AssemblyFile:
+#if LLVM_VERSION_GE(18, 0)
+    return CodeGenFileType::AssemblyFile;
+#else
     return CGFT_AssemblyFile;
+#endif
   case LLVMRustFileType::ObjectFile:
+#if LLVM_VERSION_GE(18, 0)
+    return CodeGenFileType::ObjectFile;
+#else
     return CGFT_ObjectFile;
+#endif
   default:
     report_fatal_error("Bad FileType.");
   }
@@ -1058,6 +1124,13 @@ extern "C" void LLVMRustPrintPasses() {
 extern "C" void LLVMRustRunRestrictionPass(LLVMModuleRef M, char **Symbols,
                                            size_t Len) {
   auto PreserveFunctions = [=](const GlobalValue &GV) {
+    // Preserve LLVM-injected, ASAN-related symbols.
+    // See also https://github.com/rust-lang/rust/issues/113404.
+    if (GV.getName() == "___asan_globals_registered") {
+      return true;
+    }
+
+    // Preserve symbols exported from Rust modules.
     for (size_t I = 0; I < Len; I++) {
       if (GV.getName() == Symbols[I]) {
         return true;
@@ -1511,6 +1584,38 @@ LLVMRustGetBitcodeSliceFromObjectData(const char *data,
   return BitcodeOrError->getBufferStart();
 }
 
+// Find a section of an object file by name. Fail if the section is missing or
+// empty.
+extern "C" const char *LLVMRustGetSliceFromObjectDataByName(const char *data,
+                                                            size_t len,
+                                                            const char *name,
+                                                            size_t *out_len) {
+  *out_len = 0;
+  StringRef Data(data, len);
+  MemoryBufferRef Buffer(Data, ""); // The id is unused.
+  file_magic Type = identify_magic(Buffer.getBuffer());
+  Expected<std::unique_ptr<object::ObjectFile>> ObjFileOrError =
+      object::ObjectFile::createObjectFile(Buffer, Type);
+  if (!ObjFileOrError) {
+    LLVMRustSetLastError(toString(ObjFileOrError.takeError()).c_str());
+    return nullptr;
+  }
+  for (const object::SectionRef &Sec : (*ObjFileOrError)->sections()) {
+    Expected<StringRef> Name = Sec.getName();
+    if (Name && *Name == name) {
+      Expected<StringRef> SectionOrError = Sec.getContents();
+      if (!SectionOrError) {
+        LLVMRustSetLastError(toString(SectionOrError.takeError()).c_str());
+        return nullptr;
+      }
+      *out_len = SectionOrError->size();
+      return SectionOrError->data();
+    }
+  }
+  LLVMRustSetLastError("could not find requested section");
+  return nullptr;
+}
+
 // Computes the LTO cache key for the provided 'ModId' in the given 'Data',
 // storing the result in 'KeyOut'.
 // Currently, this cache key is a SHA-1 hash of anything that could affect
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 70cdf3d6d23..4390486b0de 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -2044,3 +2044,19 @@ extern "C" bool LLVMRustIsNonGVFunctionPointerTy(LLVMValueRef V) {
   }
   return false;
 }
+
+extern "C" bool LLVMRustLLVMHasZlibCompressionForDebugSymbols() {
+#if LLVM_VERSION_GE(16, 0)
+  return llvm::compression::zlib::isAvailable();
+#else
+  return false;
+#endif
+}
+
+extern "C" bool LLVMRustLLVMHasZstdCompressionForDebugSymbols() {
+#if LLVM_VERSION_GE(16, 0)
+  return llvm::compression::zstd::isAvailable();
+#else
+  return false;
+#endif
+}
diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl
index cc58d51befd..633004fdddf 100644
--- a/compiler/rustc_metadata/messages.ftl
+++ b/compiler/rustc_metadata/messages.ftl
@@ -259,9 +259,6 @@ metadata_std_required =
 metadata_symbol_conflicts_current =
     the current crate is indistinguishable from one of its dependencies: it has the same crate-name `{$crate_name}` and was compiled with the same `-C metadata` arguments. This will result in symbol conflicts between the two.
 
-metadata_symbol_conflicts_others =
-    found two different crates with name `{$crate_name}` that are not distinguished by differing `-C metadata`. This will result in symbol conflicts between the two.
-
 metadata_target_no_std_support =
     the `{$locator_triple}` target may not support the standard library
 
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs
index fce80ab37dd..69221475356 100644
--- a/compiler/rustc_metadata/src/creader.rs
+++ b/compiler/rustc_metadata/src/creader.rs
@@ -8,7 +8,7 @@ use rustc_ast::expand::allocator::{alloc_error_handler_name, global_fn_name, All
 use rustc_ast::{self as ast, *};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::svh::Svh;
-use rustc_data_structures::sync::{MappedReadGuard, MappedWriteGuard, ReadGuard, WriteGuard};
+use rustc_data_structures::sync::{FreezeReadGuard, FreezeWriteGuard};
 use rustc_expand::base::SyntaxExtension;
 use rustc_hir::def_id::{CrateNum, LocalDefId, StableCrateId, StableCrateIdMap, LOCAL_CRATE};
 use rustc_hir::definitions::Definitions;
@@ -134,14 +134,14 @@ impl<'a> std::fmt::Debug for CrateDump<'a> {
 }
 
 impl CStore {
-    pub fn from_tcx(tcx: TyCtxt<'_>) -> MappedReadGuard<'_, CStore> {
-        ReadGuard::map(tcx.untracked().cstore.read(), |cstore| {
+    pub fn from_tcx(tcx: TyCtxt<'_>) -> FreezeReadGuard<'_, CStore> {
+        FreezeReadGuard::map(tcx.untracked().cstore.read(), |cstore| {
             cstore.as_any().downcast_ref::<CStore>().expect("`tcx.cstore` is not a `CStore`")
         })
     }
 
-    pub fn from_tcx_mut(tcx: TyCtxt<'_>) -> MappedWriteGuard<'_, CStore> {
-        WriteGuard::map(tcx.untracked().cstore.write(), |cstore| {
+    pub fn from_tcx_mut(tcx: TyCtxt<'_>) -> FreezeWriteGuard<'_, CStore> {
+        FreezeWriteGuard::map(tcx.untracked().cstore.write(), |cstore| {
             cstore.untracked_as_any().downcast_mut().expect("`tcx.cstore` is not a `CStore`")
         })
     }
diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs
index bf6004ba864..3062939d8da 100644
--- a/compiler/rustc_metadata/src/locator.rs
+++ b/compiler/rustc_metadata/src/locator.rs
@@ -903,10 +903,11 @@ pub fn list_file_metadata(
     path: &Path,
     metadata_loader: &dyn MetadataLoader,
     out: &mut dyn Write,
+    ls_kinds: &[String],
 ) -> IoResult<()> {
     let flavor = get_flavor_from_path(path);
     match get_metadata_section(target, flavor, path, metadata_loader) {
-        Ok(metadata) => metadata.list_crate_metadata(out),
+        Ok(metadata) => metadata.list_crate_metadata(out, ls_kinds),
         Err(msg) => write!(out, "{msg}\n"),
     }
 }
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 7fd3c0f494a..a57bff3730d 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -725,25 +725,196 @@ impl MetadataBlob {
         LazyValue::<CrateRoot>::from_position(NonZeroUsize::new(pos).unwrap()).decode(self)
     }
 
-    pub(crate) fn list_crate_metadata(&self, out: &mut dyn io::Write) -> io::Result<()> {
+    pub(crate) fn list_crate_metadata(
+        &self,
+        out: &mut dyn io::Write,
+        ls_kinds: &[String],
+    ) -> io::Result<()> {
         let root = self.get_root();
-        writeln!(out, "Crate info:")?;
-        writeln!(out, "name {}{}", root.name(), root.extra_filename)?;
-        writeln!(out, "hash {} stable_crate_id {:?}", root.hash(), root.stable_crate_id)?;
-        writeln!(out, "proc_macro {:?}", root.proc_macro_data.is_some())?;
-        writeln!(out, "=External Dependencies=")?;
-
-        for (i, dep) in root.crate_deps.decode(self).enumerate() {
-            let CrateDep { name, extra_filename, hash, host_hash, kind, is_private } = dep;
-            let number = i + 1;
-
-            writeln!(
-                out,
-                "{number} {name}{extra_filename} hash {hash} host_hash {host_hash:?} kind {kind:?} {privacy}",
-                privacy = if is_private { "private" } else { "public" }
-            )?;
+
+        let all_ls_kinds = vec![
+            "root".to_owned(),
+            "lang_items".to_owned(),
+            "features".to_owned(),
+            "items".to_owned(),
+        ];
+        let ls_kinds = if ls_kinds.contains(&"all".to_owned()) { &all_ls_kinds } else { ls_kinds };
+
+        for kind in ls_kinds {
+            match &**kind {
+                "root" => {
+                    writeln!(out, "Crate info:")?;
+                    writeln!(out, "name {}{}", root.name(), root.extra_filename)?;
+                    writeln!(
+                        out,
+                        "hash {} stable_crate_id {:?}",
+                        root.hash(),
+                        root.stable_crate_id
+                    )?;
+                    writeln!(out, "proc_macro {:?}", root.proc_macro_data.is_some())?;
+                    writeln!(out, "triple {}", root.header.triple.triple())?;
+                    writeln!(out, "edition {}", root.edition)?;
+                    writeln!(out, "symbol_mangling_version {:?}", root.symbol_mangling_version)?;
+                    writeln!(
+                        out,
+                        "required_panic_strategy {:?} panic_in_drop_strategy {:?}",
+                        root.required_panic_strategy, root.panic_in_drop_strategy
+                    )?;
+                    writeln!(
+                        out,
+                        "has_global_allocator {} has_alloc_error_handler {} has_panic_handler {} has_default_lib_allocator {}",
+                        root.has_global_allocator,
+                        root.has_alloc_error_handler,
+                        root.has_panic_handler,
+                        root.has_default_lib_allocator
+                    )?;
+                    writeln!(
+                        out,
+                        "compiler_builtins {} needs_allocator {} needs_panic_runtime {} no_builtins {} panic_runtime {} profiler_runtime {}",
+                        root.compiler_builtins,
+                        root.needs_allocator,
+                        root.needs_panic_runtime,
+                        root.no_builtins,
+                        root.panic_runtime,
+                        root.profiler_runtime
+                    )?;
+
+                    writeln!(out, "=External Dependencies=")?;
+                    let dylib_dependency_formats =
+                        root.dylib_dependency_formats.decode(self).collect::<Vec<_>>();
+                    for (i, dep) in root.crate_deps.decode(self).enumerate() {
+                        let CrateDep { name, extra_filename, hash, host_hash, kind, is_private } =
+                            dep;
+                        let number = i + 1;
+
+                        writeln!(
+                            out,
+                            "{number} {name}{extra_filename} hash {hash} host_hash {host_hash:?} kind {kind:?} {privacy}{linkage}",
+                            privacy = if is_private { "private" } else { "public" },
+                            linkage = if dylib_dependency_formats.is_empty() {
+                                String::new()
+                            } else {
+                                format!(" linkage {:?}", dylib_dependency_formats[i])
+                            }
+                        )?;
+                    }
+                    write!(out, "\n")?;
+                }
+
+                "lang_items" => {
+                    writeln!(out, "=Lang items=")?;
+                    for (id, lang_item) in root.lang_items.decode(self) {
+                        writeln!(
+                            out,
+                            "{} = crate{}",
+                            lang_item.name(),
+                            DefPath::make(LOCAL_CRATE, id, |parent| root
+                                .tables
+                                .def_keys
+                                .get(self, parent)
+                                .unwrap()
+                                .decode(self))
+                            .to_string_no_crate_verbose()
+                        )?;
+                    }
+                    for lang_item in root.lang_items_missing.decode(self) {
+                        writeln!(out, "{} = <missing>", lang_item.name())?;
+                    }
+                    write!(out, "\n")?;
+                }
+
+                "features" => {
+                    writeln!(out, "=Lib features=")?;
+                    for (feature, since) in root.lib_features.decode(self) {
+                        writeln!(
+                            out,
+                            "{}{}",
+                            feature,
+                            if let Some(since) = since {
+                                format!(" since {since}")
+                            } else {
+                                String::new()
+                            }
+                        )?;
+                    }
+                    write!(out, "\n")?;
+                }
+
+                "items" => {
+                    writeln!(out, "=Items=")?;
+
+                    fn print_item(
+                        blob: &MetadataBlob,
+                        out: &mut dyn io::Write,
+                        item: DefIndex,
+                        indent: usize,
+                    ) -> io::Result<()> {
+                        let root = blob.get_root();
+
+                        let def_kind = root.tables.opt_def_kind.get(blob, item).unwrap();
+                        let def_key = root.tables.def_keys.get(blob, item).unwrap().decode(blob);
+                        let def_name = if item == CRATE_DEF_INDEX {
+                            rustc_span::symbol::kw::Crate
+                        } else {
+                            def_key
+                                .disambiguated_data
+                                .data
+                                .get_opt_name()
+                                .unwrap_or_else(|| Symbol::intern("???"))
+                        };
+                        let visibility =
+                            root.tables.visibility.get(blob, item).unwrap().decode(blob).map_id(
+                                |index| {
+                                    format!(
+                                        "crate{}",
+                                        DefPath::make(LOCAL_CRATE, index, |parent| root
+                                            .tables
+                                            .def_keys
+                                            .get(blob, parent)
+                                            .unwrap()
+                                            .decode(blob))
+                                        .to_string_no_crate_verbose()
+                                    )
+                                },
+                            );
+                        write!(
+                            out,
+                            "{nil: <indent$}{:?} {:?} {} {{",
+                            visibility,
+                            def_kind,
+                            def_name,
+                            nil = "",
+                        )?;
+
+                        if let Some(children) =
+                            root.tables.module_children_non_reexports.get(blob, item)
+                        {
+                            write!(out, "\n")?;
+                            for child in children.decode(blob) {
+                                print_item(blob, out, child, indent + 4)?;
+                            }
+                            writeln!(out, "{nil: <indent$}}}", nil = "")?;
+                        } else {
+                            writeln!(out, "}}")?;
+                        }
+
+                        Ok(())
+                    }
+
+                    print_item(self, out, CRATE_DEF_INDEX, 0)?;
+
+                    write!(out, "\n")?;
+                }
+
+                _ => {
+                    writeln!(
+                        out,
+                        "unknown -Zls kind. allowed values are: all, root, lang_items, features, items"
+                    )?;
+                }
+            }
         }
-        write!(out, "\n")?;
+
         Ok(())
     }
 }
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index aeda8af6d2c..f964a8c76c0 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -131,7 +131,7 @@ macro_rules! provide_one {
                 $tcx.ensure().crate_hash($def_id.krate);
             }
 
-            let cdata = rustc_data_structures::sync::MappedReadGuard::map(CStore::from_tcx($tcx), |c| {
+            let cdata = rustc_data_structures::sync::FreezeReadGuard::map(CStore::from_tcx($tcx), |c| {
                 c.get_crate_data($def_id.krate).cdata
             });
             let $cdata = crate::creader::CrateMetadataRef {
@@ -510,7 +510,7 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) {
         crates: |tcx, ()| {
             // The list of loaded crates is now frozen in query cache,
             // so make sure cstore is not mutably accessed from here on.
-            tcx.untracked().cstore.leak();
+            tcx.untracked().cstore.freeze();
             tcx.arena.alloc_from_iter(CStore::from_tcx(tcx).iter_crate_data().map(|(cnum, _)| cnum))
         },
         ..*providers
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 4f4351633a2..cc7ae932e11 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -14,7 +14,8 @@ use rustc_data_structures::temp_dir::MaybeTempDir;
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{
-    CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE,
+    CrateNum, DefId, DefIndex, LocalDefId, LocalDefIdSet, CRATE_DEF_ID, CRATE_DEF_INDEX,
+    LOCAL_CRATE,
 };
 use rustc_hir::definitions::DefPathData;
 use rustc_hir::lang_items::LangItem;
@@ -50,7 +51,6 @@ pub(super) struct EncodeContext<'a, 'tcx> {
     opaque: opaque::FileEncoder,
     tcx: TyCtxt<'tcx>,
     feat: &'tcx rustc_feature::Features,
-
     tables: TableBuilders,
 
     lazy_state: LazyState,
@@ -280,8 +280,8 @@ impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for SpanData {
             // All of this logic ensures that the final result of deserialization is a 'normal'
             // Span that can be used without any additional trouble.
             let metadata_index = {
-                // Introduce a new scope so that we drop the 'lock()' temporary
-                match &*source_file.external_src.lock() {
+                // Introduce a new scope so that we drop the 'read()' temporary
+                match &*source_file.external_src.read() {
                     ExternalSource::Foreign { metadata_index, .. } => *metadata_index,
                     src => panic!("Unexpected external source {src:?}"),
                 }
@@ -1002,15 +1002,31 @@ fn should_encode_stability(def_kind: DefKind) -> bool {
     }
 }
 
-/// Whether we should encode MIR.
+/// Whether we should encode MIR. Return a pair, resp. for CTFE and for LLVM.
 ///
 /// Computing, optimizing and encoding the MIR is a relatively expensive operation.
 /// We want to avoid this work when not required. Therefore:
 /// - we only compute `mir_for_ctfe` on items with const-eval semantics;
 /// - we skip `optimized_mir` for check runs.
+/// - we only encode `optimized_mir` that could be generated in other crates, that is, a code that
+///   is either generic or has inline hint, and is reachable from the other crates (contained
+///   in reachable set).
+///
+/// Note: Reachable set describes definitions that might be generated or referenced from other
+/// crates and it can be used to limit optimized MIR that needs to be encoded. On the other hand,
+/// the reachable set doesn't have much to say about which definitions might be evaluated at compile
+/// time in other crates, so it cannot be used to omit CTFE MIR. For example, `f` below is
+/// unreachable and yet it can be evaluated in other crates:
 ///
-/// Return a pair, resp. for CTFE and for LLVM.
-fn should_encode_mir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> (bool, bool) {
+/// ```
+/// const fn f() -> usize { 0 }
+/// pub struct S { pub a: [usize; f()] }
+/// ```
+fn should_encode_mir(
+    tcx: TyCtxt<'_>,
+    reachable_set: &LocalDefIdSet,
+    def_id: LocalDefId,
+) -> (bool, bool) {
     match tcx.def_kind(def_id) {
         // Constructors
         DefKind::Ctor(_, _) => {
@@ -1027,14 +1043,15 @@ fn should_encode_mir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> (bool, bool) {
         // Full-fledged functions + closures
         DefKind::AssocFn | DefKind::Fn | DefKind::Closure => {
             let generics = tcx.generics_of(def_id);
-            let needs_inline = (generics.requires_monomorphization(tcx)
-                || tcx.codegen_fn_attrs(def_id).requests_inline())
-                && tcx.sess.opts.output_types.should_codegen();
+            let opt = tcx.sess.opts.unstable_opts.always_encode_mir
+                || (tcx.sess.opts.output_types.should_codegen()
+                    && reachable_set.contains(&def_id)
+                    && (generics.requires_monomorphization(tcx)
+                        || tcx.codegen_fn_attrs(def_id).requests_inline()));
             // The function has a `const` modifier or is in a `#[const_trait]`.
             let is_const_fn = tcx.is_const_fn_raw(def_id.to_def_id())
                 || tcx.is_const_default_method(def_id.to_def_id());
-            let always_encode_mir = tcx.sess.opts.unstable_opts.always_encode_mir;
-            (is_const_fn, needs_inline || always_encode_mir)
+            (is_const_fn, opt)
         }
         // Generators require optimized MIR to compute layout.
         DefKind::Generator => (false, true),
@@ -1580,9 +1597,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         }
 
         let tcx = self.tcx;
+        let reachable_set = tcx.reachable_set(());
 
         let keys_and_jobs = tcx.mir_keys(()).iter().filter_map(|&def_id| {
-            let (encode_const, encode_opt) = should_encode_mir(tcx, def_id);
+            let (encode_const, encode_opt) = should_encode_mir(tcx, reachable_set, def_id);
             if encode_const || encode_opt { Some((def_id, encode_const, encode_opt)) } else { None }
         });
         for (def_id, encode_const, encode_opt) in keys_and_jobs {
@@ -2067,8 +2085,9 @@ fn prefetch_mir(tcx: TyCtxt<'_>) {
         return;
     }
 
+    let reachable_set = tcx.reachable_set(());
     par_for_each_in(tcx.mir_keys(()), |&def_id| {
-        let (encode_const, encode_opt) = should_encode_mir(tcx, def_id);
+        let (encode_const, encode_opt) = should_encode_mir(tcx, reachable_set, def_id);
 
         if encode_const {
             tcx.ensure_with_value().mir_for_ctfe(def_id);
diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs
index afcf08395bb..78a0f82db13 100644
--- a/compiler/rustc_middle/src/dep_graph/dep_node.rs
+++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs
@@ -97,7 +97,7 @@ macro_rules! define_dep_nodes {
             // discriminants of the variants have been assigned consecutively from 0
             // so that just the one comparison suffices to check that the u16 can be
             // transmuted to a DepKind.
-            const VARIANTS: u16 = {
+            pub const VARIANTS: u16 = {
                 let deps: &[DepKind] = &[$(DepKind::$variant,)*];
                 let mut i = 0;
                 while i < deps.len() {
diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs
index f79ce08b8ae..87436f9eeed 100644
--- a/compiler/rustc_middle/src/dep_graph/mod.rs
+++ b/compiler/rustc_middle/src/dep_graph/mod.rs
@@ -26,6 +26,7 @@ pub type DepKindStruct<'tcx> = rustc_query_system::dep_graph::DepKindStruct<TyCt
 impl rustc_query_system::dep_graph::DepKind for DepKind {
     const NULL: Self = DepKind::Null;
     const RED: Self = DepKind::Red;
+    const MAX: u16 = DepKind::VARIANTS - 1;
 
     fn debug_node(node: &DepNode, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         write!(f, "{:?}(", node.kind)?;
@@ -68,6 +69,21 @@ impl rustc_query_system::dep_graph::DepKind for DepKind {
             op(icx.task_deps)
         })
     }
+
+    #[track_caller]
+    #[inline]
+    fn from_u16(u: u16) -> Self {
+        if u > Self::MAX {
+            panic!("Invalid DepKind {u}");
+        }
+        // SAFETY: See comment on DepKind::VARIANTS
+        unsafe { std::mem::transmute(u) }
+    }
+
+    #[inline]
+    fn to_u16(self) -> u16 {
+        self as u16
+    }
 }
 
 impl<'tcx> DepContext for TyCtxt<'tcx> {
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index e20a202561f..f67f8015c04 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -1206,7 +1206,7 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh {
         upstream_crates.hash_stable(&mut hcx, &mut stable_hasher);
         source_file_names.hash_stable(&mut hcx, &mut stable_hasher);
         debugger_visualizers.hash_stable(&mut hcx, &mut stable_hasher);
-        if tcx.sess.opts.incremental_relative_spans() {
+        if tcx.sess.opts.incremental.is_some() {
             let definitions = tcx.untracked().definitions.freeze();
             let mut owner_spans: Vec<_> = krate
                 .owners
diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs
index 81823118ab8..b17f1512886 100644
--- a/compiler/rustc_middle/src/infer/canonical.rs
+++ b/compiler/rustc_middle/src/infer/canonical.rs
@@ -27,6 +27,7 @@ use crate::ty::GenericArg;
 use crate::ty::{self, BoundVar, List, Region, Ty, TyCtxt};
 use rustc_macros::HashStable;
 use smallvec::SmallVec;
+use std::fmt::Display;
 use std::ops::Index;
 
 /// A "canonicalized" type `V` is one where all free inference
@@ -40,6 +41,16 @@ pub struct Canonical<'tcx, V> {
     pub variables: CanonicalVarInfos<'tcx>,
 }
 
+impl<'tcx, V: Display> std::fmt::Display for Canonical<'tcx, V> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(
+            f,
+            "Canonical {{ value: {}, max_universe: {:?}, variables: {:?} }}",
+            self.value, self.max_universe, self.variables
+        )
+    }
+}
+
 pub type CanonicalVarInfos<'tcx> = &'tcx List<CanonicalVarInfo<'tcx>>;
 
 impl<'tcx> ty::TypeFoldable<TyCtxt<'tcx>> for CanonicalVarInfos<'tcx> {
@@ -173,6 +184,7 @@ impl<'tcx> CanonicalVarInfo<'tcx> {
             CanonicalVarKind::PlaceholderRegion(..) => false,
             CanonicalVarKind::Const(..) => true,
             CanonicalVarKind::PlaceholderConst(_, _) => false,
+            CanonicalVarKind::Effect => true,
         }
     }
 
@@ -182,7 +194,8 @@ impl<'tcx> CanonicalVarInfo<'tcx> {
             CanonicalVarKind::Ty(_)
             | CanonicalVarKind::PlaceholderTy(_)
             | CanonicalVarKind::Const(_, _)
-            | CanonicalVarKind::PlaceholderConst(_, _) => false,
+            | CanonicalVarKind::PlaceholderConst(_, _)
+            | CanonicalVarKind::Effect => false,
         }
     }
 
@@ -190,7 +203,8 @@ impl<'tcx> CanonicalVarInfo<'tcx> {
         match self.kind {
             CanonicalVarKind::Ty(_)
             | CanonicalVarKind::Region(_)
-            | CanonicalVarKind::Const(_, _) => bug!("expected placeholder: {self:?}"),
+            | CanonicalVarKind::Const(_, _)
+            | CanonicalVarKind::Effect => bug!("expected placeholder: {self:?}"),
 
             CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.bound.var.as_usize(),
             CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.bound.var.as_usize(),
@@ -222,6 +236,9 @@ pub enum CanonicalVarKind<'tcx> {
     /// Some kind of const inference variable.
     Const(ty::UniverseIndex, Ty<'tcx>),
 
+    /// Effect variable `'?E`.
+    Effect,
+
     /// A "placeholder" that represents "any const".
     PlaceholderConst(ty::PlaceholderConst<'tcx>, Ty<'tcx>),
 }
@@ -229,11 +246,11 @@ pub enum CanonicalVarKind<'tcx> {
 impl<'tcx> CanonicalVarKind<'tcx> {
     pub fn universe(self) -> ty::UniverseIndex {
         match self {
-            CanonicalVarKind::Ty(kind) => match kind {
-                CanonicalTyVarKind::General(ui) => ui,
-                CanonicalTyVarKind::Float | CanonicalTyVarKind::Int => ty::UniverseIndex::ROOT,
-            },
-
+            CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)) => ui,
+            CanonicalVarKind::Ty(CanonicalTyVarKind::Float | CanonicalTyVarKind::Int) => {
+                ty::UniverseIndex::ROOT
+            }
+            CanonicalVarKind::Effect => ty::UniverseIndex::ROOT,
             CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.universe,
             CanonicalVarKind::Region(ui) => ui,
             CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.universe,
@@ -248,15 +265,14 @@ impl<'tcx> CanonicalVarKind<'tcx> {
     /// the updated universe is not the root.
     pub fn with_updated_universe(self, ui: ty::UniverseIndex) -> CanonicalVarKind<'tcx> {
         match self {
-            CanonicalVarKind::Ty(kind) => match kind {
-                CanonicalTyVarKind::General(_) => {
-                    CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui))
-                }
-                CanonicalTyVarKind::Int | CanonicalTyVarKind::Float => {
-                    assert_eq!(ui, ty::UniverseIndex::ROOT);
-                    CanonicalVarKind::Ty(kind)
-                }
-            },
+            CanonicalVarKind::Ty(CanonicalTyVarKind::General(_)) => {
+                CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui))
+            }
+            CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float)
+            | CanonicalVarKind::Effect => {
+                assert_eq!(ui, ty::UniverseIndex::ROOT);
+                self
+            }
             CanonicalVarKind::PlaceholderTy(placeholder) => {
                 CanonicalVarKind::PlaceholderTy(ty::Placeholder { universe: ui, ..placeholder })
             }
@@ -443,6 +459,13 @@ impl<'tcx> CanonicalVarValues<'tcx> {
                             };
                             ty::Region::new_late_bound(tcx, ty::INNERMOST, br).into()
                         }
+                        CanonicalVarKind::Effect => ty::Const::new_bound(
+                            tcx,
+                            ty::INNERMOST,
+                            ty::BoundVar::from_usize(i),
+                            tcx.types.bool,
+                        )
+                        .into(),
                         CanonicalVarKind::Const(_, ty)
                         | CanonicalVarKind::PlaceholderConst(_, ty) => ty::Const::new_bound(
                             tcx,
diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs
index 85fb9214d9d..7ca9647590a 100644
--- a/compiler/rustc_middle/src/infer/unify_key.rs
+++ b/compiler/rustc_middle/src/infer/unify_key.rs
@@ -188,3 +188,53 @@ impl<'tcx> UnifyValue for ConstVarValue<'tcx> {
         })
     }
 }
+
+/// values for the effect inference variable
+#[derive(Clone, Copy, Debug)]
+pub enum EffectVarValue<'tcx> {
+    /// The host effect is on, enabling access to syscalls, filesystem access, etc.
+    Host,
+    /// The host effect is off. Execution is restricted to const operations only.
+    NoHost,
+    Const(ty::Const<'tcx>),
+}
+
+impl<'tcx> EffectVarValue<'tcx> {
+    pub fn as_const(self, tcx: TyCtxt<'tcx>) -> ty::Const<'tcx> {
+        match self {
+            EffectVarValue::Host => tcx.consts.true_,
+            EffectVarValue::NoHost => tcx.consts.false_,
+            EffectVarValue::Const(c) => c,
+        }
+    }
+}
+
+impl<'tcx> UnifyValue for EffectVarValue<'tcx> {
+    type Error = (EffectVarValue<'tcx>, EffectVarValue<'tcx>);
+    fn unify_values(value1: &Self, value2: &Self) -> Result<Self, Self::Error> {
+        match (value1, value2) {
+            (EffectVarValue::Host, EffectVarValue::Host) => Ok(EffectVarValue::Host),
+            (EffectVarValue::NoHost, EffectVarValue::NoHost) => Ok(EffectVarValue::NoHost),
+            (EffectVarValue::NoHost | EffectVarValue::Host, _)
+            | (_, EffectVarValue::NoHost | EffectVarValue::Host) => Err((*value1, *value2)),
+            (EffectVarValue::Const(_), EffectVarValue::Const(_)) => {
+                bug!("equating two const variables, both of which have known values")
+            }
+        }
+    }
+}
+
+impl<'tcx> UnifyKey for ty::EffectVid<'tcx> {
+    type Value = Option<EffectVarValue<'tcx>>;
+    #[inline]
+    fn index(&self) -> u32 {
+        self.index
+    }
+    #[inline]
+    fn from_index(i: u32) -> Self {
+        ty::EffectVid { index: i, phantom: PhantomData }
+    }
+    fn tag() -> &'static str {
+        "EffectVid"
+    }
+}
diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
index 02fd6ed7ba6..4e5725876c4 100644
--- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
+++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
@@ -87,7 +87,7 @@ bitflags! {
         /// #[cmse_nonsecure_entry]: with a TrustZone-M extension, declare a
         /// function as an entry function from Non-Secure code.
         const CMSE_NONSECURE_ENTRY      = 1 << 14;
-        /// `#[no_coverage]`: indicates that the function should be ignored by
+        /// `#[coverage(off)]`: indicates that the function should be ignored by
         /// the MIR `InstrumentCoverage` pass and not added to the coverage map
         /// during codegen.
         const NO_COVERAGE               = 1 << 15;
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index 577cd20b74c..7e3be0b5d5f 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -71,6 +71,8 @@ TrivialTypeTraversalAndLiftImpls! { ErrorHandled }
 
 pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
 pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
+/// `Ok(None)` indicates the constant was fine, but the valtree couldn't be constructed.
+/// This is needed in `thir::pattern::lower_inline_const`.
 pub type EvalToValTreeResult<'tcx> = Result<Option<ValTree<'tcx>>, ErrorHandled>;
 
 pub fn struct_error<'tcx>(
@@ -255,9 +257,16 @@ impl_into_diagnostic_arg_through_debug! {
 
 /// Error information for when the program caused Undefined Behavior.
 #[derive(Debug)]
-pub enum UndefinedBehaviorInfo<'a> {
+pub enum UndefinedBehaviorInfo<'tcx> {
     /// Free-form case. Only for errors that are never caught! Used by miri
     Ub(String),
+    // FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically
+    // dispatched
+    /// A custom (free-form) fluent-translated error, created by `err_ub_custom!`.
+    Custom(crate::error::CustomSubdiagnostic<'tcx>),
+    /// Validation error.
+    ValidationError(ValidationErrorInfo<'tcx>),
+
     /// Unreachable code was executed.
     Unreachable,
     /// A slice/array index projection went out-of-bounds.
@@ -319,12 +328,10 @@ pub enum UndefinedBehaviorInfo<'a> {
     UninhabitedEnumVariantWritten(VariantIdx),
     /// An uninhabited enum variant is projected.
     UninhabitedEnumVariantRead(VariantIdx),
-    /// Validation error.
-    ValidationError(ValidationErrorInfo<'a>),
-    // FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically
-    // dispatched
-    /// A custom (free-form) error, created by `err_ub_custom!`.
-    Custom(crate::error::CustomSubdiagnostic<'a>),
+    /// ABI-incompatible argument types.
+    AbiMismatchArgument { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> },
+    /// ABI-incompatible return types.
+    AbiMismatchReturn { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> },
 }
 
 #[derive(Debug, Clone, Copy)]
diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs
index 3543158bf82..44d1dcbbe17 100644
--- a/compiler/rustc_middle/src/mir/interpret/mod.rs
+++ b/compiler/rustc_middle/src/mir/interpret/mod.rs
@@ -149,7 +149,7 @@ pub use self::error::{
     UnsupportedOpInfo, ValidationErrorInfo, ValidationErrorKind,
 };
 
-pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar};
+pub use self::value::{ConstAlloc, ConstValue, Scalar};
 
 pub use self::allocation::{
     alloc_range, AllocBytes, AllocError, AllocRange, AllocResult, Allocation, ConstAllocation,
@@ -389,7 +389,7 @@ impl<'s> AllocDecodingSession<'s> {
                     trace!("creating fn alloc ID");
                     let instance = ty::Instance::decode(decoder);
                     trace!("decoded fn alloc instance: {:?}", instance);
-                    let alloc_id = decoder.interner().create_fn_alloc(instance);
+                    let alloc_id = decoder.interner().reserve_and_set_fn_alloc(instance);
                     alloc_id
                 }
                 AllocDiscriminant::VTable => {
@@ -399,7 +399,8 @@ impl<'s> AllocDecodingSession<'s> {
                     let poly_trait_ref =
                         <Option<ty::PolyExistentialTraitRef<'_>> as Decodable<D>>::decode(decoder);
                     trace!("decoded vtable alloc instance: {ty:?}, {poly_trait_ref:?}");
-                    let alloc_id = decoder.interner().create_vtable_alloc(ty, poly_trait_ref);
+                    let alloc_id =
+                        decoder.interner().reserve_and_set_vtable_alloc(ty, poly_trait_ref);
                     alloc_id
                 }
                 AllocDiscriminant::Static => {
@@ -407,7 +408,7 @@ impl<'s> AllocDecodingSession<'s> {
                     trace!("creating extern static alloc ID");
                     let did = <DefId as Decodable<D>>::decode(decoder);
                     trace!("decoded static def-ID: {:?}", did);
-                    let alloc_id = decoder.interner().create_static_alloc(did);
+                    let alloc_id = decoder.interner().reserve_and_set_static_alloc(did);
                     alloc_id
                 }
             }
@@ -544,13 +545,13 @@ impl<'tcx> TyCtxt<'tcx> {
 
     /// Generates an `AllocId` for a static or return a cached one in case this function has been
     /// called on the same static before.
-    pub fn create_static_alloc(self, static_id: DefId) -> AllocId {
+    pub fn reserve_and_set_static_alloc(self, static_id: DefId) -> AllocId {
         self.reserve_and_set_dedup(GlobalAlloc::Static(static_id))
     }
 
     /// Generates an `AllocId` for a function. Depending on the function type,
     /// this might get deduplicated or assigned a new ID each time.
-    pub fn create_fn_alloc(self, instance: Instance<'tcx>) -> AllocId {
+    pub fn reserve_and_set_fn_alloc(self, instance: Instance<'tcx>) -> AllocId {
         // Functions cannot be identified by pointers, as asm-equal functions can get deduplicated
         // by the linker (we set the "unnamed_addr" attribute for LLVM) and functions can be
         // duplicated across crates.
@@ -575,7 +576,7 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     /// Generates an `AllocId` for a (symbolic, not-reified) vtable. Will get deduplicated.
-    pub fn create_vtable_alloc(
+    pub fn reserve_and_set_vtable_alloc(
         self,
         ty: Ty<'tcx>,
         poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
@@ -588,7 +589,7 @@ impl<'tcx> TyCtxt<'tcx> {
     /// Statics with identical content will still point to the same `Allocation`, i.e.,
     /// their data will be deduplicated through `Allocation` interning -- but they
     /// are different places in memory and as such need different IDs.
-    pub fn create_memory_alloc(self, mem: ConstAllocation<'tcx>) -> AllocId {
+    pub fn reserve_and_set_memory_alloc(self, mem: ConstAllocation<'tcx>) -> AllocId {
         let id = self.reserve_alloc_id();
         self.set_alloc_id_memory(id, mem);
         id
diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs
index 5345a658803..c169733ad74 100644
--- a/compiler/rustc_middle/src/mir/interpret/value.rs
+++ b/compiler/rustc_middle/src/mir/interpret/value.rs
@@ -9,10 +9,13 @@ use rustc_apfloat::{
 use rustc_macros::HashStable;
 use rustc_target::abi::{HasDataLayout, Size};
 
-use crate::ty::{ParamEnv, ScalarInt, Ty, TyCtxt};
+use crate::{
+    mir::interpret::alloc_range,
+    ty::{ParamEnv, ScalarInt, Ty, TyCtxt},
+};
 
 use super::{
-    AllocId, AllocRange, ConstAllocation, InterpResult, Pointer, PointerArithmetic, Provenance,
+    AllocId, ConstAllocation, InterpResult, Pointer, PointerArithmetic, Provenance,
     ScalarSizeMismatch,
 };
 
@@ -30,22 +33,32 @@ pub struct ConstAlloc<'tcx> {
 #[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
 #[derive(HashStable, Lift)]
 pub enum ConstValue<'tcx> {
-    /// Used only for types with `layout::abi::Scalar` ABI.
+    /// Used for types with `layout::abi::Scalar` ABI.
     ///
     /// Not using the enum `Value` to encode that this must not be `Uninit`.
     Scalar(Scalar),
 
-    /// Only used for ZSTs.
+    /// Only for ZSTs.
     ZeroSized,
 
-    /// Used only for `&[u8]` and `&str`
+    /// Used for `&[u8]` and `&str`.
+    ///
+    /// This is worth an optimized representation since Rust has literals of these types.
+    /// Not having to indirect those through an `AllocId` (or two, if we used `Indirect`) has shown
+    /// measurable performance improvements on stress tests.
     Slice { data: ConstAllocation<'tcx>, start: usize, end: usize },
 
-    /// A value not represented/representable by `Scalar` or `Slice`
-    ByRef {
-        /// The backing memory of the value, may contain more memory than needed for just the value
-        /// in order to share `ConstAllocation`s between values
-        alloc: ConstAllocation<'tcx>,
+    /// A value not representable by the other variants; needs to be stored in-memory.
+    ///
+    /// Must *not* be used for scalars or ZST, but having `&str` or other slices in this variant is fine.
+    Indirect {
+        /// The backing memory of the value. May contain more memory than needed for just the value
+        /// if this points into some other larger ConstValue.
+        ///
+        /// We use an `AllocId` here instead of a `ConstAllocation<'tcx>` to make sure that when a
+        /// raw constant (which is basically just an `AllocId`) is turned into a `ConstValue` and
+        /// back, we can preserve the original `AllocId`.
+        alloc_id: AllocId,
         /// Offset into `alloc`
         offset: Size,
     },
@@ -58,7 +71,7 @@ impl<'tcx> ConstValue<'tcx> {
     #[inline]
     pub fn try_to_scalar(&self) -> Option<Scalar<AllocId>> {
         match *self {
-            ConstValue::ByRef { .. } | ConstValue::Slice { .. } | ConstValue::ZeroSized => None,
+            ConstValue::Indirect { .. } | ConstValue::Slice { .. } | ConstValue::ZeroSized => None,
             ConstValue::Scalar(val) => Some(val),
         }
     }
@@ -104,6 +117,54 @@ impl<'tcx> ConstValue<'tcx> {
     pub fn from_target_usize(i: u64, cx: &impl HasDataLayout) -> Self {
         ConstValue::Scalar(Scalar::from_target_usize(i, cx))
     }
+
+    /// Must only be called on constants of type `&str` or `&[u8]`!
+    pub fn try_get_slice_bytes_for_diagnostics(&self, tcx: TyCtxt<'tcx>) -> Option<&'tcx [u8]> {
+        let (data, start, end) = match self {
+            ConstValue::Scalar(_) | ConstValue::ZeroSized => {
+                bug!("`try_get_slice_bytes` on non-slice constant")
+            }
+            &ConstValue::Slice { data, start, end } => (data, start, end),
+            &ConstValue::Indirect { alloc_id, offset } => {
+                // The reference itself is stored behind an indirection.
+                // Load the reference, and then load the actual slice contents.
+                let a = tcx.global_alloc(alloc_id).unwrap_memory().inner();
+                let ptr_size = tcx.data_layout.pointer_size;
+                if a.size() < offset + 2 * ptr_size {
+                    // (partially) dangling reference
+                    return None;
+                }
+                // Read the wide pointer components.
+                let ptr = a
+                    .read_scalar(
+                        &tcx,
+                        alloc_range(offset, ptr_size),
+                        /* read_provenance */ true,
+                    )
+                    .ok()?;
+                let ptr = ptr.to_pointer(&tcx).ok()?;
+                let len = a
+                    .read_scalar(
+                        &tcx,
+                        alloc_range(offset + ptr_size, ptr_size),
+                        /* read_provenance */ false,
+                    )
+                    .ok()?;
+                let len = len.to_target_usize(&tcx).ok()?;
+                let len: usize = len.try_into().ok()?;
+                if len == 0 {
+                    return Some(&[]);
+                }
+                // Non-empty slice, must have memory. We know this is a relative pointer.
+                let (inner_alloc_id, offset) = ptr.into_parts();
+                let data = tcx.global_alloc(inner_alloc_id?).unwrap_memory();
+                (data, offset.bytes_usize(), offset.bytes_usize() + len)
+            }
+        };
+
+        // This is for diagnostics only, so we are okay to use `inspect_with_uninit_and_ptr_outside_interpreter`.
+        Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end))
+    }
 }
 
 /// A `Scalar` represents an immediate, primitive value existing outside of a
@@ -505,18 +566,3 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
         Ok(Double::from_bits(self.to_u64()?.into()))
     }
 }
-
-/// Gets the bytes of a constant slice value.
-pub fn get_slice_bytes<'tcx>(cx: &impl HasDataLayout, val: ConstValue<'tcx>) -> &'tcx [u8] {
-    if let ConstValue::Slice { data, start, end } = val {
-        let len = end - start;
-        data.inner()
-            .get_bytes_strip_provenance(
-                cx,
-                AllocRange { start: Size::from_bytes(start), size: Size::from_bytes(len) },
-            )
-            .unwrap_or_else(|err| bug!("const slice is invalid: {:?}", err))
-    } else {
-        bug!("expected const slice, but found another const value");
-    }
-}
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 6484c30167c..8df3a79b4d4 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -8,6 +8,7 @@ use crate::mir::interpret::{
 use crate::mir::visit::MirVisitable;
 use crate::ty::codec::{TyDecoder, TyEncoder};
 use crate::ty::fold::{FallibleTypeFolder, TypeFoldable};
+use crate::ty::print::with_no_trimmed_paths;
 use crate::ty::print::{FmtPrinter, Printer};
 use crate::ty::visit::TypeVisitableExt;
 use crate::ty::{self, List, Ty, TyCtxt};
@@ -25,6 +26,7 @@ use rustc_target::abi::{FieldIdx, Size, VariantIdx};
 
 use polonius_engine::Atom;
 pub use rustc_ast::Mutability;
+use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::graph::dominators::Dominators;
 use rustc_index::{Idx, IndexSlice, IndexVec};
@@ -35,6 +37,8 @@ use rustc_span::{Span, DUMMY_SP};
 use either::Either;
 
 use std::borrow::Cow;
+use std::cell::RefCell;
+use std::collections::hash_map::Entry;
 use std::fmt::{self, Debug, Display, Formatter, Write};
 use std::ops::{Index, IndexMut};
 use std::{iter, mem};
@@ -97,6 +101,36 @@ impl<'tcx> HasLocalDecls<'tcx> for Body<'tcx> {
     }
 }
 
+thread_local! {
+    static PASS_NAMES: RefCell<FxHashMap<&'static str, &'static str>> = {
+        RefCell::new(FxHashMap::default())
+    };
+}
+
+/// Converts a MIR pass name into a snake case form to match the profiling naming style.
+fn to_profiler_name(type_name: &'static str) -> &'static str {
+    PASS_NAMES.with(|names| match names.borrow_mut().entry(type_name) {
+        Entry::Occupied(e) => *e.get(),
+        Entry::Vacant(e) => {
+            let snake_case: String = type_name
+                .chars()
+                .flat_map(|c| {
+                    if c.is_ascii_uppercase() {
+                        vec!['_', c.to_ascii_lowercase()]
+                    } else if c == '-' {
+                        vec!['_']
+                    } else {
+                        vec![c]
+                    }
+                })
+                .collect();
+            let result = &*String::leak(format!("mir_pass{}", snake_case));
+            e.insert(result);
+            result
+        }
+    })
+}
+
 /// A streamlined trait that you can implement to create a pass; the
 /// pass will be named after the type, and it will consist of a main
 /// loop that goes over each available MIR and applies `run_pass`.
@@ -106,6 +140,10 @@ pub trait MirPass<'tcx> {
         if let Some((_, tail)) = name.rsplit_once(':') { tail } else { name }
     }
 
+    fn profiler_name(&self) -> &'static str {
+        to_profiler_name(self.name())
+    }
+
     /// Returns `true` if this pass is enabled with the current combination of compiler flags.
     fn is_enabled(&self, _sess: &Session) -> bool {
         true
@@ -1028,19 +1066,6 @@ pub enum VarDebugInfoContents<'tcx> {
     /// This `Place` only contains projection which satisfy `can_use_in_debuginfo`.
     Place(Place<'tcx>),
     Const(Constant<'tcx>),
-    /// The user variable's data is split across several fragments,
-    /// each described by a `VarDebugInfoFragment`.
-    /// See DWARF 5's "2.6.1.2 Composite Location Descriptions"
-    /// and LLVM's `DW_OP_LLVM_fragment` for more details on
-    /// the underlying debuginfo feature this relies on.
-    Composite {
-        /// Type of the original user variable.
-        /// This cannot contain a union or an enum.
-        ty: Ty<'tcx>,
-        /// All the parts of the original user variable, which ended
-        /// up in disjoint places, due to optimizations.
-        fragments: Vec<VarDebugInfoFragment<'tcx>>,
-    },
 }
 
 impl<'tcx> Debug for VarDebugInfoContents<'tcx> {
@@ -1048,19 +1073,16 @@ impl<'tcx> Debug for VarDebugInfoContents<'tcx> {
         match self {
             VarDebugInfoContents::Const(c) => write!(fmt, "{c}"),
             VarDebugInfoContents::Place(p) => write!(fmt, "{p:?}"),
-            VarDebugInfoContents::Composite { ty, fragments } => {
-                write!(fmt, "{ty:?}{{ ")?;
-                for f in fragments.iter() {
-                    write!(fmt, "{f:?}, ")?;
-                }
-                write!(fmt, "}}")
-            }
         }
     }
 }
 
-#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
+#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
 pub struct VarDebugInfoFragment<'tcx> {
+    /// Type of the original user variable.
+    /// This cannot contain a union or an enum.
+    pub ty: Ty<'tcx>,
+
     /// Where in the composite user variable this fragment is,
     /// represented as a "projection" into the composite variable.
     /// At lower levels, this corresponds to a byte/bit range.
@@ -1071,29 +1093,10 @@ pub struct VarDebugInfoFragment<'tcx> {
     // to match on the discriminant, or by using custom type debuginfo
     // with non-overlapping variants for the composite variable.
     pub projection: Vec<PlaceElem<'tcx>>,
-
-    /// Where the data for this fragment can be found.
-    /// This `Place` only contains projection which satisfy `can_use_in_debuginfo`.
-    pub contents: Place<'tcx>,
-}
-
-impl Debug for VarDebugInfoFragment<'_> {
-    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
-        for elem in self.projection.iter() {
-            match elem {
-                ProjectionElem::Field(field, _) => {
-                    write!(fmt, ".{:?}", field.index())?;
-                }
-                _ => bug!("unsupported fragment projection `{:?}`", elem),
-            }
-        }
-
-        write!(fmt, " => {:?}", self.contents)
-    }
 }
 
 /// Debug information pertaining to a user variable.
-#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
+#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
 pub struct VarDebugInfo<'tcx> {
     pub name: Symbol,
 
@@ -1102,6 +1105,13 @@ pub struct VarDebugInfo<'tcx> {
     /// (see `LocalDecl`'s `source_info` field for more details).
     pub source_info: SourceInfo,
 
+    /// The user variable's data is split across several fragments,
+    /// each described by a `VarDebugInfoFragment`.
+    /// See DWARF 5's "2.6.1.2 Composite Location Descriptions"
+    /// and LLVM's `DW_OP_LLVM_fragment` for more details on
+    /// the underlying debuginfo feature this relies on.
+    pub composite: Option<Box<VarDebugInfoFragment<'tcx>>>,
+
     /// Where the data for this user variable is to be found.
     pub value: VarDebugInfoContents<'tcx>,
 
@@ -1111,6 +1121,20 @@ pub struct VarDebugInfo<'tcx> {
     pub argument_index: Option<u16>,
 }
 
+impl Debug for VarDebugInfo<'_> {
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+        if let Some(box VarDebugInfoFragment { ty, ref projection }) = self.composite {
+            pre_fmt_projection(&projection[..], fmt)?;
+            write!(fmt, "({}: {})", self.name, ty)?;
+            post_fmt_projection(&projection[..], fmt)?;
+        } else {
+            write!(fmt, "{}", self.name)?;
+        }
+
+        write!(fmt, " => {:?}", self.value)
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // BasicBlock
 
@@ -1575,7 +1599,7 @@ impl<V, T> ProjectionElem<V, T> {
 /// need neither the `V` parameter for `Index` nor the `T` for `Field`.
 pub type ProjectionKind = ProjectionElem<(), ()>;
 
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
 pub struct PlaceRef<'tcx> {
     pub local: Local,
     pub projection: &'tcx [PlaceElem<'tcx>],
@@ -1751,69 +1775,90 @@ impl<'tcx> PlaceRef<'tcx> {
     }
 }
 
+impl From<Local> for PlaceRef<'_> {
+    #[inline]
+    fn from(local: Local) -> Self {
+        PlaceRef { local, projection: &[] }
+    }
+}
+
 impl Debug for Place<'_> {
     fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
-        for elem in self.projection.iter().rev() {
-            match elem {
-                ProjectionElem::OpaqueCast(_)
-                | ProjectionElem::Downcast(_, _)
-                | ProjectionElem::Field(_, _) => {
-                    write!(fmt, "(").unwrap();
-                }
-                ProjectionElem::Deref => {
-                    write!(fmt, "(*").unwrap();
-                }
-                ProjectionElem::Index(_)
-                | ProjectionElem::ConstantIndex { .. }
-                | ProjectionElem::Subslice { .. } => {}
-            }
-        }
+        self.as_ref().fmt(fmt)
+    }
+}
 
+impl Debug for PlaceRef<'_> {
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+        pre_fmt_projection(self.projection, fmt)?;
         write!(fmt, "{:?}", self.local)?;
+        post_fmt_projection(self.projection, fmt)
+    }
+}
 
-        for elem in self.projection.iter() {
-            match elem {
-                ProjectionElem::OpaqueCast(ty) => {
-                    write!(fmt, " as {ty})")?;
-                }
-                ProjectionElem::Downcast(Some(name), _index) => {
-                    write!(fmt, " as {name})")?;
-                }
-                ProjectionElem::Downcast(None, index) => {
-                    write!(fmt, " as variant#{index:?})")?;
-                }
-                ProjectionElem::Deref => {
-                    write!(fmt, ")")?;
-                }
-                ProjectionElem::Field(field, ty) => {
-                    write!(fmt, ".{:?}: {:?})", field.index(), ty)?;
-                }
-                ProjectionElem::Index(ref index) => {
-                    write!(fmt, "[{index:?}]")?;
-                }
-                ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => {
-                    write!(fmt, "[{offset:?} of {min_length:?}]")?;
-                }
-                ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => {
-                    write!(fmt, "[-{offset:?} of {min_length:?}]")?;
-                }
-                ProjectionElem::Subslice { from, to, from_end: true } if to == 0 => {
-                    write!(fmt, "[{from:?}:]")?;
-                }
-                ProjectionElem::Subslice { from, to, from_end: true } if from == 0 => {
-                    write!(fmt, "[:-{to:?}]")?;
-                }
-                ProjectionElem::Subslice { from, to, from_end: true } => {
-                    write!(fmt, "[{from:?}:-{to:?}]")?;
-                }
-                ProjectionElem::Subslice { from, to, from_end: false } => {
-                    write!(fmt, "[{from:?}..{to:?}]")?;
-                }
+fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
+    for &elem in projection.iter().rev() {
+        match elem {
+            ProjectionElem::OpaqueCast(_)
+            | ProjectionElem::Downcast(_, _)
+            | ProjectionElem::Field(_, _) => {
+                write!(fmt, "(").unwrap();
+            }
+            ProjectionElem::Deref => {
+                write!(fmt, "(*").unwrap();
             }
+            ProjectionElem::Index(_)
+            | ProjectionElem::ConstantIndex { .. }
+            | ProjectionElem::Subslice { .. } => {}
         }
+    }
 
-        Ok(())
+    Ok(())
+}
+
+fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
+    for &elem in projection.iter() {
+        match elem {
+            ProjectionElem::OpaqueCast(ty) => {
+                write!(fmt, " as {ty})")?;
+            }
+            ProjectionElem::Downcast(Some(name), _index) => {
+                write!(fmt, " as {name})")?;
+            }
+            ProjectionElem::Downcast(None, index) => {
+                write!(fmt, " as variant#{index:?})")?;
+            }
+            ProjectionElem::Deref => {
+                write!(fmt, ")")?;
+            }
+            ProjectionElem::Field(field, ty) => {
+                with_no_trimmed_paths!(write!(fmt, ".{:?}: {})", field.index(), ty)?);
+            }
+            ProjectionElem::Index(ref index) => {
+                write!(fmt, "[{index:?}]")?;
+            }
+            ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => {
+                write!(fmt, "[{offset:?} of {min_length:?}]")?;
+            }
+            ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => {
+                write!(fmt, "[-{offset:?} of {min_length:?}]")?;
+            }
+            ProjectionElem::Subslice { from, to, from_end: true } if to == 0 => {
+                write!(fmt, "[{from:?}:]")?;
+            }
+            ProjectionElem::Subslice { from, to, from_end: true } if from == 0 => {
+                write!(fmt, "[:-{to:?}]")?;
+            }
+            ProjectionElem::Subslice { from, to, from_end: true } => {
+                write!(fmt, "[{from:?}:-{to:?}]")?;
+            }
+            ProjectionElem::Subslice { from, to, from_end: false } => {
+                write!(fmt, "[{from:?}..{to:?}]")?;
+            }
+        }
     }
+
+    Ok(())
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -2070,7 +2115,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
             }
             Len(ref a) => write!(fmt, "Len({a:?})"),
             Cast(ref kind, ref place, ref ty) => {
-                write!(fmt, "{place:?} as {ty:?} ({kind:?})")
+                with_no_trimmed_paths!(write!(fmt, "{place:?} as {ty} ({kind:?})"))
             }
             BinaryOp(ref op, box (ref a, ref b)) => write!(fmt, "{op:?}({a:?}, {b:?})"),
             CheckedBinaryOp(ref op, box (ref a, ref b)) => {
@@ -2078,11 +2123,14 @@ impl<'tcx> Debug for Rvalue<'tcx> {
             }
             UnaryOp(ref op, ref a) => write!(fmt, "{op:?}({a:?})"),
             Discriminant(ref place) => write!(fmt, "discriminant({place:?})"),
-            NullaryOp(ref op, ref t) => match op {
-                NullOp::SizeOf => write!(fmt, "SizeOf({t:?})"),
-                NullOp::AlignOf => write!(fmt, "AlignOf({t:?})"),
-                NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t:?}, {fields:?})"),
-            },
+            NullaryOp(ref op, ref t) => {
+                let t = with_no_trimmed_paths!(format!("{}", t));
+                match op {
+                    NullOp::SizeOf => write!(fmt, "SizeOf({t})"),
+                    NullOp::AlignOf => write!(fmt, "AlignOf({t})"),
+                    NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"),
+                }
+            }
             ThreadLocalRef(did) => ty::tls::with(|tcx| {
                 let muta = tcx.static_mutability(did).unwrap().prefix_str();
                 write!(fmt, "&/*tls*/ {}{}", muta, tcx.def_path_str(did))
@@ -2218,7 +2266,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
             }
 
             ShallowInitBox(ref place, ref ty) => {
-                write!(fmt, "ShallowInitBox({place:?}, {ty:?})")
+                with_no_trimmed_paths!(write!(fmt, "ShallowInitBox({place:?}, {ty})"))
             }
         }
     }
@@ -2249,7 +2297,11 @@ pub struct Constant<'tcx> {
 #[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable, Debug)]
 #[derive(Lift, TypeFoldable, TypeVisitable)]
 pub enum ConstantKind<'tcx> {
-    /// This constant came from the type system
+    /// This constant came from the type system.
+    ///
+    /// Any way of turning `ty::Const` into `ConstValue` should go through `valtree_to_const_val`;
+    /// this ensures that we consistently produce "clean" values without data in the padding or
+    /// anything like that.
     Ty(ty::Const<'tcx>),
 
     /// An unevaluated mir constant which is not part of the type system.
@@ -2289,18 +2341,6 @@ impl<'tcx> ConstantKind<'tcx> {
     }
 
     #[inline]
-    pub fn try_to_value(self, tcx: TyCtxt<'tcx>) -> Option<interpret::ConstValue<'tcx>> {
-        match self {
-            ConstantKind::Ty(c) => match c.kind() {
-                ty::ConstKind::Value(valtree) => Some(tcx.valtree_to_const_val((c.ty(), valtree))),
-                _ => None,
-            },
-            ConstantKind::Val(val, _) => Some(val),
-            ConstantKind::Unevaluated(..) => None,
-        }
-    }
-
-    #[inline]
     pub fn try_to_scalar(self) -> Option<Scalar> {
         match self {
             ConstantKind::Ty(c) => match c.kind() {
@@ -2317,7 +2357,7 @@ impl<'tcx> ConstantKind<'tcx> {
 
     #[inline]
     pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
-        Some(self.try_to_scalar()?.assert_int())
+        self.try_to_scalar()?.try_to_int().ok()
     }
 
     #[inline]
@@ -2331,37 +2371,55 @@ impl<'tcx> ConstantKind<'tcx> {
     }
 
     #[inline]
-    pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
+    pub fn eval(
+        self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        span: Option<Span>,
+    ) -> Result<interpret::ConstValue<'tcx>, ErrorHandled> {
         match self {
-            Self::Ty(c) => {
-                if let Some(val) = c.try_eval_for_mir(tcx, param_env) {
-                    match val {
-                        Ok(val) => Self::Val(val, c.ty()),
-                        Err(guar) => Self::Ty(ty::Const::new_error(tcx, guar, self.ty())),
-                    }
-                } else {
-                    self
-                }
+            ConstantKind::Ty(c) => {
+                // We want to consistently have a "clean" value for type system constants (i.e., no
+                // data hidden in the padding), so we always go through a valtree here.
+                let val = c.eval(tcx, param_env, span)?;
+                Ok(tcx.valtree_to_const_val((self.ty(), val)))
             }
-            Self::Val(_, _) => self,
-            Self::Unevaluated(uneval, ty) => {
+            ConstantKind::Unevaluated(uneval, _) => {
                 // FIXME: We might want to have a `try_eval`-like function on `Unevaluated`
-                match tcx.const_eval_resolve(param_env, uneval, None) {
-                    Ok(val) => Self::Val(val, ty),
-                    Err(ErrorHandled::TooGeneric) => self,
-                    Err(ErrorHandled::Reported(guar)) => {
-                        Self::Ty(ty::Const::new_error(tcx, guar.into(), ty))
-                    }
-                }
+                tcx.const_eval_resolve(param_env, uneval, span)
             }
+            ConstantKind::Val(val, _) => Ok(val),
         }
     }
 
-    /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.
+    /// Normalizes the constant to a value or an error if possible.
     #[inline]
-    pub fn eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 {
-        self.try_eval_bits(tcx, param_env, ty)
-            .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self))
+    pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
+        match self.eval(tcx, param_env, None) {
+            Ok(val) => Self::Val(val, self.ty()),
+            Err(ErrorHandled::Reported(guar)) => {
+                Self::Ty(ty::Const::new_error(tcx, guar.into(), self.ty()))
+            }
+            Err(ErrorHandled::TooGeneric) => self,
+        }
+    }
+
+    #[inline]
+    pub fn try_eval_scalar(
+        self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Option<Scalar> {
+        self.eval(tcx, param_env, None).ok()?.try_to_scalar()
+    }
+
+    #[inline]
+    pub fn try_eval_scalar_int(
+        self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Option<ScalarInt> {
+        self.try_eval_scalar(tcx, param_env)?.try_to_int().ok()
     }
 
     #[inline]
@@ -2371,59 +2429,38 @@ impl<'tcx> ConstantKind<'tcx> {
         param_env: ty::ParamEnv<'tcx>,
         ty: Ty<'tcx>,
     ) -> Option<u128> {
-        match self {
-            Self::Ty(ct) => ct.try_eval_bits(tcx, param_env, ty),
-            Self::Val(val, t) => {
-                assert_eq!(*t, ty);
-                let size =
-                    tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
-                val.try_to_bits(size)
-            }
-            Self::Unevaluated(uneval, ty) => {
-                match tcx.const_eval_resolve(param_env, *uneval, None) {
-                    Ok(val) => {
-                        let size = tcx
-                            .layout_of(param_env.with_reveal_all_normalized(tcx).and(*ty))
-                            .ok()?
-                            .size;
-                        val.try_to_bits(size)
-                    }
-                    Err(_) => None,
-                }
-            }
-        }
+        let int = self.try_eval_scalar_int(tcx, param_env)?;
+        assert_eq!(self.ty(), ty);
+        let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
+        int.to_bits(size).ok()
     }
 
+    /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.
     #[inline]
-    pub fn try_eval_bool(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Option<bool> {
-        match self {
-            Self::Ty(ct) => ct.try_eval_bool(tcx, param_env),
-            Self::Val(val, _) => val.try_to_bool(),
-            Self::Unevaluated(uneval, _) => {
-                match tcx.const_eval_resolve(param_env, *uneval, None) {
-                    Ok(val) => val.try_to_bool(),
-                    Err(_) => None,
-                }
-            }
-        }
+    pub fn eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 {
+        self.try_eval_bits(tcx, param_env, ty)
+            .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self))
     }
 
     #[inline]
     pub fn try_eval_target_usize(
-        &self,
+        self,
         tcx: TyCtxt<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
     ) -> Option<u64> {
-        match self {
-            Self::Ty(ct) => ct.try_eval_target_usize(tcx, param_env),
-            Self::Val(val, _) => val.try_to_target_usize(tcx),
-            Self::Unevaluated(uneval, _) => {
-                match tcx.const_eval_resolve(param_env, *uneval, None) {
-                    Ok(val) => val.try_to_target_usize(tcx),
-                    Err(_) => None,
-                }
-            }
-        }
+        self.try_eval_scalar_int(tcx, param_env)?.try_to_target_usize(tcx).ok()
+    }
+
+    #[inline]
+    /// Panics if the value cannot be evaluated or doesn't contain a valid `usize`.
+    pub fn eval_target_usize(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> u64 {
+        self.try_eval_target_usize(tcx, param_env)
+            .unwrap_or_else(|| bug!("expected usize, got {:#?}", self))
+    }
+
+    #[inline]
+    pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Option<bool> {
+        self.try_eval_scalar_int(tcx, param_env)?.try_into().ok()
     }
 
     #[inline]
@@ -2565,13 +2602,13 @@ impl<'tcx> ConstantKind<'tcx> {
         }
     }
 
-    pub fn from_const(c: ty::Const<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
+    pub fn from_ty_const(c: ty::Const<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
         match c.kind() {
             ty::ConstKind::Value(valtree) => {
+                // Make sure that if `c` is normalized, then the return value is normalized.
                 let const_val = tcx.valtree_to_const_val((c.ty(), valtree));
                 Self::Val(const_val, c.ty())
             }
-            ty::ConstKind::Unevaluated(uv) => Self::Unevaluated(uv.expand(), c.ty()),
             _ => Self::Ty(c),
         }
     }
@@ -2599,6 +2636,11 @@ impl<'tcx> UnevaluatedConst<'tcx> {
     pub fn new(def: DefId, args: GenericArgsRef<'tcx>) -> UnevaluatedConst<'tcx> {
         UnevaluatedConst { def, args, promoted: Default::default() }
     }
+
+    #[inline]
+    pub fn from_instance(instance: ty::Instance<'tcx>) -> Self {
+        UnevaluatedConst::new(instance.def_id(), instance.args)
+    }
 }
 
 /// A collection of projections into user types.
@@ -2845,35 +2887,21 @@ fn pretty_print_const_value<'tcx>(
         let u8_type = tcx.types.u8;
         match (ct, ty.kind()) {
             // Byte/string slices, printed as (byte) string literals.
-            (ConstValue::Slice { data, start, end }, ty::Ref(_, inner, _)) => {
-                match inner.kind() {
-                    ty::Slice(t) => {
-                        if *t == u8_type {
-                            // The `inspect` here is okay since we checked the bounds, and `u8` carries
-                            // no provenance (we have an active slice reference here). We don't use
-                            // this result to affect interpreter execution.
-                            let byte_str = data
-                                .inner()
-                                .inspect_with_uninit_and_ptr_outside_interpreter(start..end);
-                            pretty_print_byte_str(fmt, byte_str)?;
-                            return Ok(());
-                        }
-                    }
-                    ty::Str => {
-                        // The `inspect` here is okay since we checked the bounds, and `str` carries
-                        // no provenance (we have an active `str` reference here). We don't use this
-                        // result to affect interpreter execution.
-                        let slice = data
-                            .inner()
-                            .inspect_with_uninit_and_ptr_outside_interpreter(start..end);
-                        fmt.write_str(&format!("{:?}", String::from_utf8_lossy(slice)))?;
-                        return Ok(());
-                    }
-                    _ => {}
+            (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => {
+                if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
+                    fmt.write_str(&format!("{:?}", String::from_utf8_lossy(data)))?;
+                    return Ok(());
+                }
+            }
+            (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(t) if *t == u8_type) => {
+                if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
+                    pretty_print_byte_str(fmt, data)?;
+                    return Ok(());
                 }
             }
-            (ConstValue::ByRef { alloc, offset }, ty::Array(t, n)) if *t == u8_type => {
-                let n = n.try_to_bits(tcx.data_layout.pointer_size).unwrap();
+            (ConstValue::Indirect { alloc_id, offset }, ty::Array(t, n)) if *t == u8_type => {
+                let n = n.try_to_target_usize(tcx).unwrap();
+                let alloc = tcx.global_alloc(alloc_id).unwrap_memory();
                 // cast is ok because we already checked for pointer size (32 or 64 bit) above
                 let range = AllocRange { start: offset, size: Size::from_bytes(n) };
                 let byte_str = alloc.inner().get_bytes_strip_provenance(&tcx, range).unwrap();
@@ -3056,6 +3084,6 @@ mod size_asserts {
     static_assert_size!(StatementKind<'_>, 16);
     static_assert_size!(Terminator<'_>, 104);
     static_assert_size!(TerminatorKind<'_>, 88);
-    static_assert_size!(VarDebugInfo<'_>, 80);
+    static_assert_size!(VarDebugInfo<'_>, 88);
     // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs
index 8fd980d5a9e..403e80bd34c 100644
--- a/compiler/rustc_middle/src/mir/mono.rs
+++ b/compiler/rustc_middle/src/mir/mono.rs
@@ -78,9 +78,11 @@ impl<'tcx> MonoItem<'tcx> {
         }
     }
 
-    pub fn is_generic_fn(&self) -> bool {
-        match *self {
-            MonoItem::Fn(ref instance) => instance.args.non_erasable_generics().next().is_some(),
+    pub fn is_generic_fn(&self, tcx: TyCtxt<'tcx>) -> bool {
+        match self {
+            MonoItem::Fn(instance) => {
+                instance.args.non_erasable_generics(tcx, instance.def_id()).next().is_some()
+            }
             MonoItem::Static(..) | MonoItem::GlobalAsm(..) => false,
         }
     }
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 773056e8a17..c4fae27a7a5 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -460,7 +460,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
                 ConstValue::ZeroSized => "<ZST>".to_string(),
                 ConstValue::Scalar(s) => format!("Scalar({s:?})"),
                 ConstValue::Slice { .. } => "Slice(..)".to_string(),
-                ConstValue::ByRef { .. } => "ByRef(..)".to_string(),
+                ConstValue::Indirect { .. } => "ByRef(..)".to_string(),
             };
 
             let fmt_valtree = |valtree: &ty::ValTree<'tcx>| match valtree {
@@ -554,10 +554,7 @@ fn write_scope_tree(
             continue;
         }
 
-        let indented_debug_info = format!(
-            "{0:1$}debug {2} => {3:?};",
-            INDENT, indent, var_debug_info.name, var_debug_info.value,
-        );
+        let indented_debug_info = format!("{0:1$}debug {2:?};", INDENT, indent, var_debug_info);
 
         if tcx.sess.opts.unstable_opts.mir_include_spans {
             writeln!(
@@ -586,8 +583,10 @@ fn write_scope_tree(
 
         let mut_str = local_decl.mutability.prefix_str();
 
-        let mut indented_decl =
-            format!("{0:1$}let {2}{3:?}: {4:?}", INDENT, indent, mut_str, local, local_decl.ty);
+        let mut indented_decl = ty::print::with_no_trimmed_paths!(format!(
+            "{0:1$}let {2}{3:?}: {4}",
+            INDENT, indent, mut_str, local, local_decl.ty
+        ));
         if let Some(user_ty) = &local_decl.user_ty {
             for user_ty in user_ty.projections() {
                 write!(indented_decl, " as {user_ty:?}").unwrap();
@@ -702,14 +701,18 @@ pub fn write_allocations<'tcx>(
     fn alloc_ids_from_const_val(val: ConstValue<'_>) -> impl Iterator<Item = AllocId> + '_ {
         match val {
             ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _)) => {
-                Either::Left(Either::Left(std::iter::once(ptr.provenance)))
+                Either::Left(std::iter::once(ptr.provenance))
             }
-            ConstValue::Scalar(interpret::Scalar::Int { .. }) => {
-                Either::Left(Either::Right(std::iter::empty()))
+            ConstValue::Scalar(interpret::Scalar::Int { .. }) => Either::Right(std::iter::empty()),
+            ConstValue::ZeroSized => Either::Right(std::iter::empty()),
+            ConstValue::Slice { .. } => {
+                // `u8`/`str` slices, shouldn't contain pointers that we want to print.
+                Either::Right(std::iter::empty())
             }
-            ConstValue::ZeroSized => Either::Left(Either::Right(std::iter::empty())),
-            ConstValue::ByRef { alloc, .. } | ConstValue::Slice { data: alloc, .. } => {
-                Either::Right(alloc_ids_from_alloc(alloc))
+            ConstValue::Indirect { alloc_id, .. } => {
+                // FIXME: we don't actually want to print all of these, since some are printed nicely directly as values inline in MIR.
+                // Really we'd want `pretty_print_const_value` to decide which allocations to print, instead of having a separate visitor.
+                Either::Left(std::iter::once(alloc_id))
             }
         }
     }
@@ -1061,11 +1064,11 @@ fn write_user_type_annotations(
     for (index, annotation) in body.user_type_annotations.iter_enumerated() {
         writeln!(
             w,
-            "| {:?}: user_ty: {:?}, span: {}, inferred_ty: {:?}",
+            "| {:?}: user_ty: {}, span: {}, inferred_ty: {}",
             index.index(),
             annotation.user_ty,
             tcx.sess.source_map().span_to_embeddable_string(annotation.span),
-            annotation.inferred_ty,
+            with_no_trimmed_paths!(format!("{}", annotation.inferred_ty)),
         )?;
     }
     if !body.user_type_annotations.is_empty() {
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 87b04aabe6a..61244b94289 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -838,12 +838,20 @@ macro_rules! make_mir_visitor {
                 let VarDebugInfo {
                     name: _,
                     source_info,
+                    composite,
                     value,
                     argument_index: _,
                 } = var_debug_info;
 
                 self.visit_source_info(source_info);
                 let location = Location::START;
+                if let Some(box VarDebugInfoFragment { ref $($mutability)? ty, ref $($mutability)? projection }) = composite {
+                    self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
+                    for elem in projection {
+                        let ProjectionElem::Field(_, ty) = elem else { bug!() };
+                        self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
+                    }
+                }
                 match value {
                     VarDebugInfoContents::Const(c) => self.visit_constant(c, location),
                     VarDebugInfoContents::Place(place) =>
@@ -852,17 +860,6 @@ macro_rules! make_mir_visitor {
                             PlaceContext::NonUse(NonUseContext::VarDebugInfo),
                             location
                         ),
-                    VarDebugInfoContents::Composite { ty, fragments } => {
-                        // FIXME(eddyb) use a better `TyContext` here.
-                        self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
-                        for VarDebugInfoFragment { projection: _, contents } in fragments {
-                            self.visit_place(
-                                contents,
-                                PlaceContext::NonUse(NonUseContext::VarDebugInfo),
-                                location,
-                            );
-                        }
-                    }
                 }
             }
 
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index bf340846f10..9e358ea4eba 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1140,6 +1140,7 @@ rustc_queries! {
     query reachable_set(_: ()) -> &'tcx LocalDefIdSet {
         arena_cache
         desc { "reachability" }
+        cache_on_disk_if { true }
     }
 
     /// Per-body `region::ScopeTree`. The `DefId` should be the owner `DefId` for the body;
diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs
index 64853bd9612..280f5d0a84c 100644
--- a/compiler/rustc_middle/src/query/on_disk_cache.rs
+++ b/compiler/rustc_middle/src/query/on_disk_cache.rs
@@ -692,7 +692,7 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for Span {
         let len = BytePos::decode(decoder);
 
         let file_lo = decoder.file_index_to_file(file_lo_index);
-        let lo = file_lo.lines(|lines| lines[line_lo - 1] + col_lo);
+        let lo = file_lo.lines()[line_lo - 1] + col_lo;
         let lo = file_lo.absolute_position(lo);
         let hi = lo + len;
 
@@ -896,7 +896,7 @@ impl<'a, 'tcx> Encodable<CacheEncoder<'a, 'tcx>> for Span {
         }
 
         if let Some(parent) = span_data.parent {
-            let enclosing = s.tcx.source_span(parent).data_untracked();
+            let enclosing = s.tcx.source_span_untracked(parent).data_untracked();
             if enclosing.contains(span_data) {
                 TAG_RELATIVE_SPAN.encode(s);
                 (span_data.lo - enclosing.lo).to_u32().encode(s);
diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs
index 9d63d291854..27a1e64a78b 100644
--- a/compiler/rustc_middle/src/traits/solve.rs
+++ b/compiler/rustc_middle/src/traits/solve.rs
@@ -9,6 +9,9 @@ use crate::ty::{
     self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable,
     TypeVisitor,
 };
+use rustc_span::def_id::DefId;
+
+use super::BuiltinImplSource;
 
 mod cache;
 pub mod inspect;
@@ -235,3 +238,63 @@ pub enum IsNormalizesToHack {
     Yes,
     No,
 }
+
+/// Possible ways the given goal can be proven.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum CandidateSource {
+    /// A user written impl.
+    ///
+    /// ## Examples
+    ///
+    /// ```rust
+    /// fn main() {
+    ///     let x: Vec<u32> = Vec::new();
+    ///     // This uses the impl from the standard library to prove `Vec<T>: Clone`.
+    ///     let y = x.clone();
+    /// }
+    /// ```
+    Impl(DefId),
+    /// A builtin impl generated by the compiler. When adding a new special
+    /// trait, try to use actual impls whenever possible. Builtin impls should
+    /// only be used in cases where the impl cannot be manually be written.
+    ///
+    /// Notable examples are auto traits, `Sized`, and `DiscriminantKind`.
+    /// For a list of all traits with builtin impls, check out the
+    /// `EvalCtxt::assemble_builtin_impl_candidates` method.
+    BuiltinImpl(BuiltinImplSource),
+    /// An assumption from the environment.
+    ///
+    /// More precisely we've used the `n-th` assumption in the `param_env`.
+    ///
+    /// ## Examples
+    ///
+    /// ```rust
+    /// fn is_clone<T: Clone>(x: T) -> (T, T) {
+    ///     // This uses the assumption `T: Clone` from the `where`-bounds
+    ///     // to prove `T: Clone`.
+    ///     (x.clone(), x)
+    /// }
+    /// ```
+    ParamEnv(usize),
+    /// If the self type is an alias type, e.g. an opaque type or a projection,
+    /// we know the bounds on that alias to hold even without knowing its concrete
+    /// underlying type.
+    ///
+    /// More precisely this candidate is using the `n-th` bound in the `item_bounds` of
+    /// the self type.
+    ///
+    /// ## Examples
+    ///
+    /// ```rust
+    /// trait Trait {
+    ///     type Assoc: Clone;
+    /// }
+    ///
+    /// fn foo<T: Trait>(x: <T as Trait>::Assoc) {
+    ///     // We prove `<T as Trait>::Assoc` by looking at the bounds on `Assoc` in
+    ///     // in the trait definition.
+    ///     let _y = x.clone();
+    /// }
+    /// ```
+    AliasBound,
+}
diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs
index 4e2af3816ac..d8b3a061b77 100644
--- a/compiler/rustc_middle/src/traits/solve/inspect.rs
+++ b/compiler/rustc_middle/src/traits/solve/inspect.rs
@@ -1,5 +1,6 @@
 use super::{
-    CanonicalInput, Certainty, Goal, IsNormalizesToHack, NoSolution, QueryInput, QueryResult,
+    CandidateSource, CanonicalInput, Certainty, Goal, IsNormalizesToHack, NoSolution, QueryInput,
+    QueryResult,
 };
 use crate::ty;
 use format::ProofTreeFormatter;
@@ -7,26 +8,30 @@ use std::fmt::{Debug, Write};
 
 mod format;
 
-#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+#[derive(Debug, Eq, PartialEq)]
 pub enum CacheHit {
     Provisional,
     Global,
 }
 
-#[derive(Eq, PartialEq, Hash, HashStable)]
+#[derive(Eq, PartialEq)]
 pub struct GoalEvaluation<'tcx> {
     pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>,
-    pub canonicalized_goal: CanonicalInput<'tcx>,
-
-    pub kind: GoalEvaluationKind<'tcx>,
     pub is_normalizes_to_hack: IsNormalizesToHack,
+    pub evaluation: CanonicalGoalEvaluation<'tcx>,
     pub returned_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
+}
 
+#[derive(Eq, PartialEq)]
+pub struct CanonicalGoalEvaluation<'tcx> {
+    pub goal: CanonicalInput<'tcx>,
+    pub kind: GoalEvaluationKind<'tcx>,
     pub result: QueryResult<'tcx>,
 }
 
-#[derive(Eq, PartialEq, Hash, HashStable)]
+#[derive(Eq, PartialEq)]
 pub enum GoalEvaluationKind<'tcx> {
+    Overflow,
     CacheHit(CacheHit),
     Uncached { revisions: Vec<GoalEvaluationStep<'tcx>> },
 }
@@ -36,55 +41,50 @@ impl Debug for GoalEvaluation<'_> {
     }
 }
 
-#[derive(Eq, PartialEq, Hash, HashStable)]
+#[derive(Eq, PartialEq)]
 pub struct AddedGoalsEvaluation<'tcx> {
     pub evaluations: Vec<Vec<GoalEvaluation<'tcx>>>,
     pub result: Result<Certainty, NoSolution>,
 }
-impl Debug for AddedGoalsEvaluation<'_> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        ProofTreeFormatter::new(f).format_nested_goal_evaluation(self)
-    }
-}
 
-#[derive(Eq, PartialEq, Hash, HashStable)]
+#[derive(Eq, PartialEq)]
 pub struct GoalEvaluationStep<'tcx> {
     pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
 
-    pub nested_goal_evaluations: Vec<AddedGoalsEvaluation<'tcx>>,
-    pub candidates: Vec<GoalCandidate<'tcx>>,
-
-    pub result: QueryResult<'tcx>,
-}
-impl Debug for GoalEvaluationStep<'_> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        ProofTreeFormatter::new(f).format_evaluation_step(self)
-    }
+    /// The actual evaluation of the goal, always `ProbeKind::Root`.
+    pub evaluation: GoalCandidate<'tcx>,
 }
 
-#[derive(Eq, PartialEq, Hash, HashStable)]
+#[derive(Eq, PartialEq)]
 pub struct GoalCandidate<'tcx> {
-    pub nested_goal_evaluations: Vec<AddedGoalsEvaluation<'tcx>>,
+    pub added_goals_evaluations: Vec<AddedGoalsEvaluation<'tcx>>,
     pub candidates: Vec<GoalCandidate<'tcx>>,
-    pub kind: CandidateKind<'tcx>,
+    pub kind: ProbeKind<'tcx>,
+}
+
+impl Debug for GoalCandidate<'_> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        ProofTreeFormatter::new(f).format_candidate(self)
+    }
 }
 
-#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
-pub enum CandidateKind<'tcx> {
+#[derive(Debug, PartialEq, Eq)]
+pub enum ProbeKind<'tcx> {
+    /// The root inference context while proving a goal.
+    Root { result: QueryResult<'tcx> },
     /// Probe entered when normalizing the self ty during candidate assembly
     NormalizedSelfTyAssembly,
-    /// A normal candidate for proving a goal
-    Candidate { name: String, result: QueryResult<'tcx> },
+    /// Some candidate to prove the current goal.
+    ///
+    /// FIXME: Remove this in favor of always using more strongly typed variants.
+    MiscCandidate { name: &'static str, result: QueryResult<'tcx> },
+    /// A candidate for proving a trait or alias-relate goal.
+    TraitCandidate { source: CandidateSource, result: QueryResult<'tcx> },
     /// Used in the probe that wraps normalizing the non-self type for the unsize
     /// trait, which is also structurally matched on.
     UnsizeAssembly,
     /// During upcasting from some source object to target object type, used to
     /// do a probe to find out what projection type(s) may be used to prove that
     /// the source type upholds all of the target type's object bounds.
-    UpcastProbe,
-}
-impl Debug for GoalCandidate<'_> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        ProofTreeFormatter::new(f).format_candidate(self)
-    }
+    UpcastProjectionCompatibility,
 }
diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs
index 8759fecb05a..d916e80a625 100644
--- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs
+++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs
@@ -39,44 +39,52 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
         func(&mut ProofTreeFormatter { f: &mut Indentor { f: self.f, on_newline: true } })
     }
 
-    pub(super) fn format_goal_evaluation(&mut self, goal: &GoalEvaluation<'_>) -> std::fmt::Result {
-        let goal_text = match goal.is_normalizes_to_hack {
+    pub(super) fn format_goal_evaluation(&mut self, eval: &GoalEvaluation<'_>) -> std::fmt::Result {
+        let goal_text = match eval.is_normalizes_to_hack {
             IsNormalizesToHack::Yes => "NORMALIZES-TO HACK GOAL",
             IsNormalizesToHack::No => "GOAL",
         };
+        writeln!(self.f, "{}: {:?}", goal_text, eval.uncanonicalized_goal)?;
+        self.nested(|this| this.format_canonical_goal_evaluation(&eval.evaluation))?;
+        if eval.returned_goals.len() > 0 {
+            writeln!(self.f, "NESTED GOALS ADDED TO CALLER: [")?;
+            self.nested(|this| {
+                for goal in eval.returned_goals.iter() {
+                    writeln!(this.f, "ADDED GOAL: {goal:?},")?;
+                }
+                Ok(())
+            })?;
+
+            writeln!(self.f, "]")
+        } else {
+            Ok(())
+        }
+    }
 
-        writeln!(self.f, "{}: {:?}", goal_text, goal.uncanonicalized_goal)?;
-        writeln!(self.f, "CANONICALIZED: {:?}", goal.canonicalized_goal)?;
+    pub(super) fn format_canonical_goal_evaluation(
+        &mut self,
+        eval: &CanonicalGoalEvaluation<'_>,
+    ) -> std::fmt::Result {
+        writeln!(self.f, "GOAL: {:?}", eval.goal)?;
 
-        match &goal.kind {
+        match &eval.kind {
+            GoalEvaluationKind::Overflow => {
+                writeln!(self.f, "OVERFLOW: {:?}", eval.result)
+            }
             GoalEvaluationKind::CacheHit(CacheHit::Global) => {
-                writeln!(self.f, "GLOBAL CACHE HIT: {:?}", goal.result)
+                writeln!(self.f, "GLOBAL CACHE HIT: {:?}", eval.result)
             }
             GoalEvaluationKind::CacheHit(CacheHit::Provisional) => {
-                writeln!(self.f, "PROVISIONAL CACHE HIT: {:?}", goal.result)
+                writeln!(self.f, "PROVISIONAL CACHE HIT: {:?}", eval.result)
             }
             GoalEvaluationKind::Uncached { revisions } => {
                 for (n, step) in revisions.iter().enumerate() {
-                    writeln!(self.f, "REVISION {n}: {:?}", step.result)?;
+                    writeln!(self.f, "REVISION {n}")?;
                     self.nested(|this| this.format_evaluation_step(step))?;
                 }
-                writeln!(self.f, "RESULT: {:?}", goal.result)
+                writeln!(self.f, "RESULT: {:?}", eval.result)
             }
-        }?;
-
-        if goal.returned_goals.len() > 0 {
-            writeln!(self.f, "NESTED GOALS ADDED TO CALLER: [")?;
-            self.nested(|this| {
-                for goal in goal.returned_goals.iter() {
-                    writeln!(this.f, "ADDED GOAL: {goal:?},")?;
-                }
-                Ok(())
-            })?;
-
-            writeln!(self.f, "]")?;
         }
-
-        Ok(())
     }
 
     pub(super) fn format_evaluation_step(
@@ -84,54 +92,52 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
         evaluation_step: &GoalEvaluationStep<'_>,
     ) -> std::fmt::Result {
         writeln!(self.f, "INSTANTIATED: {:?}", evaluation_step.instantiated_goal)?;
-
-        for candidate in &evaluation_step.candidates {
-            self.nested(|this| this.format_candidate(candidate))?;
-        }
-        for nested in &evaluation_step.nested_goal_evaluations {
-            self.nested(|this| this.format_nested_goal_evaluation(nested))?;
-        }
-
-        Ok(())
+        self.format_candidate(&evaluation_step.evaluation)
     }
 
     pub(super) fn format_candidate(&mut self, candidate: &GoalCandidate<'_>) -> std::fmt::Result {
         match &candidate.kind {
-            CandidateKind::NormalizedSelfTyAssembly => {
+            ProbeKind::Root { result } => {
+                writeln!(self.f, "ROOT RESULT: {result:?}")
+            }
+            ProbeKind::NormalizedSelfTyAssembly => {
                 writeln!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:")
             }
-            CandidateKind::UnsizeAssembly => {
+            ProbeKind::UnsizeAssembly => {
                 writeln!(self.f, "ASSEMBLING CANDIDATES FOR UNSIZING:")
             }
-            CandidateKind::UpcastProbe => {
+            ProbeKind::UpcastProjectionCompatibility => {
                 writeln!(self.f, "PROBING FOR PROJECTION COMPATIBILITY FOR UPCASTING:")
             }
-            CandidateKind::Candidate { name, result } => {
+            ProbeKind::MiscCandidate { name, result } => {
                 writeln!(self.f, "CANDIDATE {name}: {result:?}")
             }
+            ProbeKind::TraitCandidate { source, result } => {
+                writeln!(self.f, "CANDIDATE {source:?}: {result:?}")
+            }
         }?;
 
         self.nested(|this| {
             for candidate in &candidate.candidates {
                 this.format_candidate(candidate)?;
             }
-            for nested in &candidate.nested_goal_evaluations {
-                this.format_nested_goal_evaluation(nested)?;
+            for nested in &candidate.added_goals_evaluations {
+                this.format_added_goals_evaluation(nested)?;
             }
             Ok(())
         })
     }
 
-    pub(super) fn format_nested_goal_evaluation(
+    pub(super) fn format_added_goals_evaluation(
         &mut self,
-        nested_goal_evaluation: &AddedGoalsEvaluation<'_>,
+        added_goals_evaluation: &AddedGoalsEvaluation<'_>,
     ) -> std::fmt::Result {
-        writeln!(self.f, "TRY_EVALUATE_ADDED_GOALS: {:?}", nested_goal_evaluation.result)?;
+        writeln!(self.f, "TRY_EVALUATE_ADDED_GOALS: {:?}", added_goals_evaluation.result)?;
 
-        for (n, revision) in nested_goal_evaluation.evaluations.iter().enumerate() {
-            writeln!(self.f, "REVISION {n}")?;
+        for (n, iterations) in added_goals_evaluation.evaluations.iter().enumerate() {
+            writeln!(self.f, "ITERATION {n}")?;
             self.nested(|this| {
-                for goal_evaluation in revision {
+                for goal_evaluation in iterations {
                     this.format_goal_evaluation(goal_evaluation)?;
                 }
                 Ok(())
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index cce10417e1b..629f9f8cd7d 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -1,5 +1,5 @@
 use crate::middle::resolve_bound_vars as rbv;
-use crate::mir::interpret::{AllocId, ConstValue, LitToConstInput, Scalar};
+use crate::mir::interpret::{AllocId, ErrorHandled, LitToConstInput, Scalar};
 use crate::ty::{self, GenericArgs, ParamEnv, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
 use rustc_data_structures::intern::Interned;
 use rustc_error_messages::MultiSpan;
@@ -14,9 +14,8 @@ mod valtree;
 
 pub use int::*;
 pub use kind::*;
-use rustc_span::ErrorGuaranteed;
+use rustc_span::Span;
 use rustc_span::DUMMY_SP;
-use rustc_target::abi::Size;
 pub use valtree::*;
 
 use super::sty::ConstKind;
@@ -36,16 +35,6 @@ pub struct ConstData<'tcx> {
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 static_assert_size!(ConstData<'_>, 40);
 
-enum EvalMode {
-    Typeck,
-    Mir,
-}
-
-enum EvalResult<'tcx> {
-    ValTree(ty::ValTree<'tcx>),
-    ConstVal(ConstValue<'tcx>),
-}
-
 impl<'tcx> Const<'tcx> {
     #[inline]
     pub fn ty(self) -> Ty<'tcx> {
@@ -165,7 +154,7 @@ impl<'tcx> Const<'tcx> {
 
         let ty = tcx.type_of(def).no_bound_vars().expect("const parameter types cannot be generic");
 
-        match Self::try_eval_lit_or_param(tcx, ty, expr) {
+        match Self::try_from_lit_or_param(tcx, ty, expr) {
             Some(v) => v,
             None => ty::Const::new_unevaluated(
                 tcx,
@@ -179,7 +168,7 @@ impl<'tcx> Const<'tcx> {
     }
 
     #[instrument(skip(tcx), level = "debug")]
-    fn try_eval_lit_or_param(
+    fn try_from_lit_or_param(
         tcx: TyCtxt<'tcx>,
         ty: Ty<'tcx>,
         expr: &'tcx hir::Expr<'tcx>,
@@ -254,14 +243,6 @@ impl<'tcx> Const<'tcx> {
         }
     }
 
-    /// Panics if self.kind != ty::ConstKind::Value
-    pub fn to_valtree(self) -> ty::ValTree<'tcx> {
-        match self.kind() {
-            ty::ConstKind::Value(valtree) => valtree,
-            _ => bug!("expected ConstKind::Value, got {:?}", self.kind()),
-        }
-    }
-
     #[inline]
     /// Creates a constant with the given integer value and interns it.
     pub fn from_bits(tcx: TyCtxt<'tcx>, bits: u128, ty: ParamEnvAnd<'tcx, Ty<'tcx>>) -> Self {
@@ -294,14 +275,66 @@ impl<'tcx> Const<'tcx> {
         Self::from_bits(tcx, n as u128, ParamEnv::empty().and(tcx.types.usize))
     }
 
-    /// Attempts to convert to a `ValTree`
-    pub fn try_to_valtree(self) -> Option<ty::ValTree<'tcx>> {
+    /// Returns the evaluated constant
+    #[inline]
+    pub fn eval(
+        self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ParamEnv<'tcx>,
+        span: Option<Span>,
+    ) -> Result<ValTree<'tcx>, ErrorHandled> {
+        assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}");
         match self.kind() {
-            ty::ConstKind::Value(valtree) => Some(valtree),
-            _ => None,
+            ConstKind::Unevaluated(unevaluated) => {
+                // FIXME(eddyb) maybe the `const_eval_*` methods should take
+                // `ty::ParamEnvAnd` instead of having them separate.
+                let (param_env, unevaluated) = unevaluated.prepare_for_eval(tcx, param_env);
+                // try to resolve e.g. associated constants to their definition on an impl, and then
+                // evaluate the const.
+                let c = tcx.const_eval_resolve_for_typeck(param_env, unevaluated, span)?;
+                Ok(c.expect("`ty::Const::eval` called on a non-valtree-compatible type"))
+            }
+            ConstKind::Value(val) => Ok(val),
+            ConstKind::Error(g) => Err(g.into()),
+            ConstKind::Param(_)
+            | ConstKind::Infer(_)
+            | ConstKind::Bound(_, _)
+            | ConstKind::Placeholder(_)
+            | ConstKind::Expr(_) => Err(ErrorHandled::TooGeneric),
         }
     }
 
+    /// Normalizes the constant to a value or an error if possible.
+    #[inline]
+    pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self {
+        match self.eval(tcx, param_env, None) {
+            Ok(val) => Self::new_value(tcx, val, self.ty()),
+            Err(ErrorHandled::Reported(r)) => Self::new_error(tcx, r.into(), self.ty()),
+            Err(ErrorHandled::TooGeneric) => self,
+        }
+    }
+
+    #[inline]
+    pub fn try_eval_scalar(
+        self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Option<Scalar> {
+        self.eval(tcx, param_env, None).ok()?.try_to_scalar()
+    }
+
+    #[inline]
+    /// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of
+    /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it
+    /// contains const generic parameters or pointers).
+    pub fn try_eval_scalar_int(
+        self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ParamEnv<'tcx>,
+    ) -> Option<ScalarInt> {
+        self.try_eval_scalar(tcx, param_env)?.try_to_int().ok()
+    }
+
     #[inline]
     /// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of
     /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it
@@ -312,15 +345,18 @@ impl<'tcx> Const<'tcx> {
         param_env: ParamEnv<'tcx>,
         ty: Ty<'tcx>,
     ) -> Option<u128> {
+        let int = self.try_eval_scalar_int(tcx, param_env)?;
         assert_eq!(self.ty(), ty);
         let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
         // if `ty` does not depend on generic parameters, use an empty param_env
-        self.eval(tcx, param_env).try_to_bits(size)
+        int.to_bits(size).ok()
     }
 
     #[inline]
-    pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<bool> {
-        self.eval(tcx, param_env).try_to_bool()
+    /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.
+    pub fn eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 {
+        self.try_eval_bits(tcx, param_env, ty)
+            .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self))
     }
 
     #[inline]
@@ -329,29 +365,12 @@ impl<'tcx> Const<'tcx> {
         tcx: TyCtxt<'tcx>,
         param_env: ParamEnv<'tcx>,
     ) -> Option<u64> {
-        self.eval(tcx, param_env).try_to_target_usize(tcx)
-    }
-
-    #[inline]
-    /// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the
-    /// unevaluated constant.
-    pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Const<'tcx> {
-        if let Some(val) = self.try_eval_for_typeck(tcx, param_env) {
-            match val {
-                Ok(val) => ty::Const::new_value(tcx, val, self.ty()),
-                Err(guar) => ty::Const::new_error(tcx, guar, self.ty()),
-            }
-        } else {
-            // Either the constant isn't evaluatable or ValTree creation failed.
-            self
-        }
+        self.try_eval_scalar_int(tcx, param_env)?.try_to_target_usize(tcx).ok()
     }
 
     #[inline]
-    /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.
-    pub fn eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 {
-        self.try_eval_bits(tcx, param_env, ty)
-            .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self))
+    pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<bool> {
+        self.try_eval_scalar_int(tcx, param_env)?.try_into().ok()
     }
 
     #[inline]
@@ -361,136 +380,30 @@ impl<'tcx> Const<'tcx> {
             .unwrap_or_else(|| bug!("expected usize, got {:#?}", self))
     }
 
-    #[inline]
-    /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary
-    /// return `None`.
-    // FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged.
-    pub fn try_eval_for_mir(
-        self,
-        tcx: TyCtxt<'tcx>,
-        param_env: ParamEnv<'tcx>,
-    ) -> Option<Result<ConstValue<'tcx>, ErrorGuaranteed>> {
-        match self.try_eval_inner(tcx, param_env, EvalMode::Mir) {
-            Some(Ok(EvalResult::ValTree(_))) => unreachable!(),
-            Some(Ok(EvalResult::ConstVal(v))) => Some(Ok(v)),
-            Some(Err(e)) => Some(Err(e)),
-            None => None,
-        }
-    }
-
-    #[inline]
-    /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary
-    /// return `None`.
-    // FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged.
-    pub fn try_eval_for_typeck(
-        self,
-        tcx: TyCtxt<'tcx>,
-        param_env: ParamEnv<'tcx>,
-    ) -> Option<Result<ty::ValTree<'tcx>, ErrorGuaranteed>> {
-        match self.try_eval_inner(tcx, param_env, EvalMode::Typeck) {
-            Some(Ok(EvalResult::ValTree(v))) => Some(Ok(v)),
-            Some(Ok(EvalResult::ConstVal(_))) => unreachable!(),
-            Some(Err(e)) => Some(Err(e)),
-            None => None,
+    /// Panics if self.kind != ty::ConstKind::Value
+    pub fn to_valtree(self) -> ty::ValTree<'tcx> {
+        match self.kind() {
+            ty::ConstKind::Value(valtree) => valtree,
+            _ => bug!("expected ConstKind::Value, got {:?}", self.kind()),
         }
     }
 
-    #[inline]
-    fn try_eval_inner(
-        self,
-        tcx: TyCtxt<'tcx>,
-        param_env: ParamEnv<'tcx>,
-        eval_mode: EvalMode,
-    ) -> Option<Result<EvalResult<'tcx>, ErrorGuaranteed>> {
-        assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}");
-        if let ConstKind::Unevaluated(unevaluated) = self.kind() {
-            use crate::mir::interpret::ErrorHandled;
-
-            // HACK(eddyb) this erases lifetimes even though `const_eval_resolve`
-            // also does later, but we want to do it before checking for
-            // inference variables.
-            // Note that we erase regions *before* calling `with_reveal_all_normalized`,
-            // so that we don't try to invoke this query with
-            // any region variables.
-
-            // HACK(eddyb) when the query key would contain inference variables,
-            // attempt using identity args and `ParamEnv` instead, that will succeed
-            // when the expression doesn't depend on any parameters.
-            // FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that
-            // we can call `infcx.const_eval_resolve` which handles inference variables.
-            let param_env_and = if (param_env, unevaluated).has_non_region_infer() {
-                tcx.param_env(unevaluated.def).and(ty::UnevaluatedConst {
-                    def: unevaluated.def,
-                    args: GenericArgs::identity_for_item(tcx, unevaluated.def),
-                })
-            } else {
-                tcx.erase_regions(param_env)
-                    .with_reveal_all_normalized(tcx)
-                    .and(tcx.erase_regions(unevaluated))
-            };
-
-            // FIXME(eddyb) maybe the `const_eval_*` methods should take
-            // `ty::ParamEnvAnd` instead of having them separate.
-            let (param_env, unevaluated) = param_env_and.into_parts();
-            // try to resolve e.g. associated constants to their definition on an impl, and then
-            // evaluate the const.
-            match eval_mode {
-                EvalMode::Typeck => {
-                    match tcx.const_eval_resolve_for_typeck(param_env, unevaluated, None) {
-                        // NOTE(eddyb) `val` contains no lifetimes/types/consts,
-                        // and we use the original type, so nothing from `args`
-                        // (which may be identity args, see above),
-                        // can leak through `val` into the const we return.
-                        Ok(val) => Some(Ok(EvalResult::ValTree(val?))),
-                        Err(ErrorHandled::TooGeneric) => None,
-                        Err(ErrorHandled::Reported(e)) => Some(Err(e.into())),
-                    }
-                }
-                EvalMode::Mir => {
-                    match tcx.const_eval_resolve(param_env, unevaluated.expand(), None) {
-                        // NOTE(eddyb) `val` contains no lifetimes/types/consts,
-                        // and we use the original type, so nothing from `args`
-                        // (which may be identity args, see above),
-                        // can leak through `val` into the const we return.
-                        Ok(val) => Some(Ok(EvalResult::ConstVal(val))),
-                        Err(ErrorHandled::TooGeneric) => None,
-                        Err(ErrorHandled::Reported(e)) => Some(Err(e.into())),
-                    }
-                }
-            }
-        } else {
-            None
+    /// Attempts to convert to a `ValTree`
+    pub fn try_to_valtree(self) -> Option<ty::ValTree<'tcx>> {
+        match self.kind() {
+            ty::ConstKind::Value(valtree) => Some(valtree),
+            _ => None,
         }
     }
 
     #[inline]
-    pub fn try_to_value(self) -> Option<ty::ValTree<'tcx>> {
-        if let ConstKind::Value(val) = self.kind() { Some(val) } else { None }
-    }
-
-    #[inline]
     pub fn try_to_scalar(self) -> Option<Scalar<AllocId>> {
-        self.try_to_value()?.try_to_scalar()
-    }
-
-    #[inline]
-    pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
-        self.try_to_value()?.try_to_scalar_int()
-    }
-
-    #[inline]
-    pub fn try_to_bits(self, size: Size) -> Option<u128> {
-        self.try_to_scalar_int()?.to_bits(size).ok()
-    }
-
-    #[inline]
-    pub fn try_to_bool(self) -> Option<bool> {
-        self.try_to_scalar_int()?.try_into().ok()
+        self.try_to_valtree()?.try_to_scalar()
     }
 
     #[inline]
     pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
-        self.try_to_value()?.try_to_target_usize(tcx)
+        self.try_to_valtree()?.try_to_target_usize(tcx)
     }
 
     pub fn is_ct_infer(self) -> bool {
diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs
index b16163edf14..9d99344d5bd 100644
--- a/compiler/rustc_middle/src/ty/consts/int.rs
+++ b/compiler/rustc_middle/src/ty/consts/int.rs
@@ -227,6 +227,11 @@ impl ScalarInt {
     }
 
     #[inline]
+    pub fn try_from_target_usize(i: impl Into<u128>, tcx: TyCtxt<'_>) -> Option<Self> {
+        Self::try_from_uint(i, tcx.data_layout.pointer_size)
+    }
+
+    #[inline]
     pub fn assert_bits(self, target_size: Size) -> u128 {
         self.to_bits(target_size).unwrap_or_else(|size| {
             bug!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes())
diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs
index db4a15fbee5..e25402fe0c2 100644
--- a/compiler/rustc_middle/src/ty/consts/kind.rs
+++ b/compiler/rustc_middle/src/ty/consts/kind.rs
@@ -2,7 +2,7 @@ use super::Const;
 use crate::mir;
 use crate::ty::abstract_const::CastKind;
 use crate::ty::GenericArgsRef;
-use crate::ty::{self, List, Ty};
+use crate::ty::{self, visit::TypeVisitableExt as _, List, Ty, TyCtxt};
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_hir::def_id::DefId;
 use rustc_macros::HashStable;
@@ -22,9 +22,37 @@ impl rustc_errors::IntoDiagnosticArg for UnevaluatedConst<'_> {
 }
 
 impl<'tcx> UnevaluatedConst<'tcx> {
+    /// FIXME(RalfJung): I cannot explain what this does or why it makes sense, but not doing this
+    /// hurts performance.
     #[inline]
-    pub fn expand(self) -> mir::UnevaluatedConst<'tcx> {
-        mir::UnevaluatedConst { def: self.def, args: self.args, promoted: None }
+    pub(crate) fn prepare_for_eval(
+        self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> (ty::ParamEnv<'tcx>, Self) {
+        // HACK(eddyb) this erases lifetimes even though `const_eval_resolve`
+        // also does later, but we want to do it before checking for
+        // inference variables.
+        // Note that we erase regions *before* calling `with_reveal_all_normalized`,
+        // so that we don't try to invoke this query with
+        // any region variables.
+
+        // HACK(eddyb) when the query key would contain inference variables,
+        // attempt using identity args and `ParamEnv` instead, that will succeed
+        // when the expression doesn't depend on any parameters.
+        // FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that
+        // we can call `infcx.const_eval_resolve` which handles inference variables.
+        if (param_env, self).has_non_region_infer() {
+            (
+                tcx.param_env(self.def),
+                ty::UnevaluatedConst {
+                    def: self.def,
+                    args: ty::GenericArgs::identity_for_item(tcx, self.def),
+                },
+            )
+        } else {
+            (tcx.erase_regions(param_env).with_reveal_all_normalized(tcx), tcx.erase_regions(self))
+        }
     }
 }
 
@@ -55,6 +83,11 @@ static_assert_size!(super::ConstKind<'_>, 32);
 pub enum InferConst<'tcx> {
     /// Infer the value of the const.
     Var(ty::ConstVid<'tcx>),
+    /// Infer the value of the effect.
+    ///
+    /// For why this is separate from the `Var` variant above, see the
+    /// documentation on `EffectVid`.
+    EffectVar(ty::EffectVid<'tcx>),
     /// A fresh const variable. See `infer::freshen` for more details.
     Fresh(u32),
 }
@@ -62,7 +95,9 @@ pub enum InferConst<'tcx> {
 impl<CTX> HashStable<CTX> for InferConst<'_> {
     fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
         match self {
-            InferConst::Var(_) => panic!("const variables should not be hashed: {self:?}"),
+            InferConst::Var(_) | InferConst::EffectVar(_) => {
+                panic!("const variables should not be hashed: {self:?}")
+            }
             InferConst::Fresh(i) => i.hash_stable(hcx, hasher),
         }
     }
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 90d847804f0..f7484048757 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -39,9 +39,7 @@ use rustc_data_structures::profiling::SelfProfilerRef;
 use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap};
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::steal::Steal;
-use rustc_data_structures::sync::{
-    self, FreezeReadGuard, Lock, Lrc, MappedReadGuard, ReadGuard, WorkerLocal,
-};
+use rustc_data_structures::sync::{self, FreezeReadGuard, Lock, Lrc, WorkerLocal};
 use rustc_data_structures::unord::UnordSet;
 use rustc_errors::{
     DecorateLint, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed, MultiSpan,
@@ -649,7 +647,7 @@ impl<'tcx> TyCtxt<'tcx> {
         // Create an allocation that just contains these bytes.
         let alloc = interpret::Allocation::from_bytes_byte_aligned_immutable(bytes);
         let alloc = self.mk_const_alloc(alloc);
-        self.create_memory_alloc(alloc)
+        self.reserve_and_set_memory_alloc(alloc)
     }
 
     /// Returns a range of the start/end indices specified with the
@@ -995,8 +993,8 @@ impl<'tcx> TyCtxt<'tcx> {
     /// Note that this is *untracked* and should only be used within the query
     /// system if the result is otherwise tracked through queries
     #[inline]
-    pub fn cstore_untracked(self) -> MappedReadGuard<'tcx, CrateStoreDyn> {
-        ReadGuard::map(self.untracked.cstore.read(), |c| &**c)
+    pub fn cstore_untracked(self) -> FreezeReadGuard<'tcx, CrateStoreDyn> {
+        FreezeReadGuard::map(self.untracked.cstore.read(), |c| &**c)
     }
 
     /// Give out access to the untracked data without any sanity checks.
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index 5db9b775a0f..e7ebb985ca4 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -70,10 +70,10 @@ impl<'tcx> Ty<'tcx> {
     /// description in error messages. This is used in the primary span label. Beyond what
     /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to
     /// ADTs with no type arguments.
-    pub fn is_simple_text(self) -> bool {
+    pub fn is_simple_text(self, tcx: TyCtxt<'tcx>) -> bool {
         match self.kind() {
-            Adt(_, args) => args.non_erasable_generics().next().is_none(),
-            Ref(_, ty, _) => ty.is_simple_text(),
+            Adt(def, args) => args.non_erasable_generics(tcx, def.did()).next().is_none(),
+            Ref(_, ty, _) => ty.is_simple_text(tcx),
             _ => self.is_simple_ty(),
         }
     }
diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs
index bbd4a623330..231635c086e 100644
--- a/compiler/rustc_middle/src/ty/flags.rs
+++ b/compiler/rustc_middle/src/ty/flags.rs
@@ -34,6 +34,26 @@ impl FlagComputation {
         result.flags
     }
 
+    pub fn bound_var_flags(vars: &ty::List<ty::BoundVariableKind>) -> FlagComputation {
+        let mut computation = FlagComputation::new();
+
+        for bv in vars {
+            match bv {
+                ty::BoundVariableKind::Ty(_) => {
+                    computation.flags |= TypeFlags::HAS_TY_LATE_BOUND;
+                }
+                ty::BoundVariableKind::Region(_) => {
+                    computation.flags |= TypeFlags::HAS_RE_LATE_BOUND;
+                }
+                ty::BoundVariableKind::Const => {
+                    computation.flags |= TypeFlags::HAS_CT_LATE_BOUND;
+                }
+            }
+        }
+
+        computation
+    }
+
     fn add_flags(&mut self, flags: TypeFlags) {
         self.flags = self.flags | flags;
     }
@@ -57,21 +77,7 @@ impl FlagComputation {
     where
         F: FnOnce(&mut Self, T),
     {
-        let mut computation = FlagComputation::new();
-
-        for bv in value.bound_vars() {
-            match bv {
-                ty::BoundVariableKind::Ty(_) => {
-                    computation.flags |= TypeFlags::HAS_TY_LATE_BOUND;
-                }
-                ty::BoundVariableKind::Region(_) => {
-                    computation.flags |= TypeFlags::HAS_RE_LATE_BOUND;
-                }
-                ty::BoundVariableKind::Const => {
-                    computation.flags |= TypeFlags::HAS_CT_LATE_BOUND;
-                }
-            }
-        }
+        let mut computation = FlagComputation::bound_var_flags(value.bound_vars());
 
         f(&mut computation, value.skip_binder());
 
@@ -324,7 +330,9 @@ impl FlagComputation {
                 self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
                 match infer {
                     InferConst::Fresh(_) => self.add_flags(TypeFlags::HAS_CT_FRESH),
-                    InferConst::Var(_) => self.add_flags(TypeFlags::HAS_CT_INFER),
+                    InferConst::Var(_) | InferConst::EffectVar(_) => {
+                        self.add_flags(TypeFlags::HAS_CT_INFER)
+                    }
                 }
             }
             ty::ConstKind::Bound(debruijn, _) => {
diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs
index 97dab5cb47e..e598ead791e 100644
--- a/compiler/rustc_middle/src/ty/generic_args.rs
+++ b/compiler/rustc_middle/src/ty/generic_args.rs
@@ -379,12 +379,17 @@ impl<'tcx> GenericArgs<'tcx> {
         self.iter().filter_map(|k| k.as_const())
     }
 
+    /// Returns generic arguments that are not lifetimes or host effect params.
     #[inline]
     pub fn non_erasable_generics(
         &'tcx self,
+        tcx: TyCtxt<'tcx>,
+        def_id: DefId,
     ) -> impl DoubleEndedIterator<Item = GenericArgKind<'tcx>> + 'tcx {
-        self.iter().filter_map(|k| match k.unpack() {
-            GenericArgKind::Lifetime(_) => None,
+        let generics = tcx.generics_of(def_id);
+        self.iter().enumerate().filter_map(|(i, k)| match k.unpack() {
+            _ if Some(i) == generics.host_effect_index => None,
+            ty::GenericArgKind::Lifetime(_) => None,
             generic => Some(generic),
         })
     }
@@ -440,7 +445,7 @@ impl<'tcx> GenericArgs<'tcx> {
         target_args: GenericArgsRef<'tcx>,
     ) -> GenericArgsRef<'tcx> {
         let defs = tcx.generics_of(source_ancestor);
-        tcx.mk_args_from_iter(target_args.iter().chain(self.iter().skip(defs.params.len())))
+        tcx.mk_args_from_iter(target_args.iter().chain(self.iter().skip(defs.count())))
     }
 
     pub fn truncate_to(&self, tcx: TyCtxt<'tcx>, generics: &ty::Generics) -> GenericArgsRef<'tcx> {
@@ -450,6 +455,11 @@ impl<'tcx> GenericArgs<'tcx> {
     pub fn host_effect_param(&'tcx self) -> Option<ty::Const<'tcx>> {
         self.consts().rfind(|x| matches!(x.kind(), ty::ConstKind::Param(p) if p.name == sym::host))
     }
+
+    pub fn print_as_list(&self) -> String {
+        let v = self.iter().map(|arg| arg.to_string()).collect::<Vec<_>>();
+        format!("[{}]", v.join(", "))
+    }
 }
 
 impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for GenericArgsRef<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs
index 70a35f137d8..8e6c1cd4bbb 100644
--- a/compiler/rustc_middle/src/ty/generics.rs
+++ b/compiler/rustc_middle/src/ty/generics.rs
@@ -12,7 +12,7 @@ use super::{Clause, EarlyBoundRegion, InstantiatedPredicates, ParamConst, ParamT
 pub enum GenericParamDefKind {
     Lifetime,
     Type { has_default: bool, synthetic: bool },
-    Const { has_default: bool },
+    Const { has_default: bool, is_host_effect: bool },
 }
 
 impl GenericParamDefKind {
@@ -87,7 +87,7 @@ impl GenericParamDef {
             GenericParamDefKind::Type { has_default, .. } if has_default => {
                 Some(tcx.type_of(self.def_id).map_bound(|t| t.into()))
             }
-            GenericParamDefKind::Const { has_default } if has_default => {
+            GenericParamDefKind::Const { has_default, .. } if has_default => {
                 Some(tcx.const_param_default(self.def_id).map_bound(|c| c.into()))
             }
             _ => None,
@@ -187,7 +187,7 @@ impl<'tcx> Generics {
                 GenericParamDefKind::Type { has_default, .. } => {
                     own_defaults.types += has_default as usize;
                 }
-                GenericParamDefKind::Const { has_default } => {
+                GenericParamDefKind::Const { has_default, .. } => {
                     own_defaults.consts += has_default as usize;
                 }
             }
@@ -212,10 +212,12 @@ impl<'tcx> Generics {
     pub fn own_requires_monomorphization(&self) -> bool {
         for param in &self.params {
             match param.kind {
-                GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
+                GenericParamDefKind::Type { .. }
+                | GenericParamDefKind::Const { is_host_effect: false, .. } => {
                     return true;
                 }
-                GenericParamDefKind::Lifetime => {}
+                GenericParamDefKind::Lifetime
+                | GenericParamDefKind::Const { is_host_effect: true, .. } => {}
             }
         }
         false
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index 8913bf76d34..e5b9203d12a 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -139,7 +139,7 @@ impl<'tcx> Instance<'tcx> {
         }
 
         // If this a non-generic instance, it cannot be a shared monomorphization.
-        self.args.non_erasable_generics().next()?;
+        self.args.non_erasable_generics(tcx, self.def_id()).next()?;
 
         match self.def {
             InstanceDef::Item(def) => tcx
@@ -344,6 +344,7 @@ impl<'tcx> Instance<'tcx> {
     pub fn mono(tcx: TyCtxt<'tcx>, def_id: DefId) -> Instance<'tcx> {
         let args = GenericArgs::for_item(tcx, def_id, |param, _| match param.kind {
             ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
+            ty::GenericParamDefKind::Const { is_host_effect: true, .. } => tcx.consts.true_.into(),
             ty::GenericParamDefKind::Type { .. } => {
                 bug!("Instance::mono: {:?} has type parameters", def_id)
             }
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 1fdc4f7500f..99b697d0ea5 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -98,9 +98,9 @@ pub use self::sty::BoundRegionKind::*;
 pub use self::sty::{
     AliasTy, Article, Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVar,
     BoundVariableKind, CanonicalPolyFnSig, ClosureArgs, ClosureArgsParts, ConstKind, ConstVid,
-    EarlyBoundRegion, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig,
-    FreeRegion, GenSig, GeneratorArgs, GeneratorArgsParts, InlineConstArgs, InlineConstArgsParts,
-    ParamConst, ParamTy, PolyExistentialPredicate, PolyExistentialProjection,
+    EarlyBoundRegion, EffectVid, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef,
+    FnSig, FreeRegion, GenSig, GeneratorArgs, GeneratorArgsParts, InlineConstArgs,
+    InlineConstArgsParts, ParamConst, ParamTy, PolyExistentialPredicate, PolyExistentialProjection,
     PolyExistentialTraitRef, PolyFnSig, PolyGenSig, PolyTraitRef, Region, RegionKind, RegionVid,
     TraitRef, TyKind, TypeAndMut, UpvarArgs, VarianceDiagInfo,
 };
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index ac0c88468fa..f24ac79323f 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -760,9 +760,12 @@ pub trait PrettyPrinter<'tcx>:
                 // only affect certain debug messages (e.g. messages printed
                 // from `rustc_middle::ty` during the computation of `tcx.predicates_of`),
                 // and should have no effect on any compiler output.
+                // [Unless `-Zverbose` is used, e.g. in the output of
+                // `tests/ui/nll/ty-outlives/impl-trait-captures.rs`, for
+                // example.]
                 if self.should_print_verbose() {
                     // FIXME(eddyb) print this with `print_def_path`.
-                    p!(write("Opaque({:?}, {:?})", def_id, args));
+                    p!(write("Opaque({:?}, {})", def_id, args.print_as_list()));
                     return Ok(self);
                 }
 
@@ -894,7 +897,7 @@ pub trait PrettyPrinter<'tcx>:
                     p!(print_def_path(did, args));
                     if !args.as_closure().is_valid() {
                         p!(" closure_args=(unavailable)");
-                        p!(write(" args={:?}", args));
+                        p!(write(" args={}", args.print_as_list()));
                     } else {
                         p!(" closure_kind_ty=", print(args.as_closure().kind_ty()));
                         p!(
@@ -1123,6 +1126,17 @@ pub trait PrettyPrinter<'tcx>:
             }
         }
 
+        if self.tcx().features().return_type_notation
+            && let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. }) = self.tcx().opt_rpitit_info(def_id)
+            && let ty::Alias(_, alias_ty) = self.tcx().fn_sig(fn_def_id).skip_binder().output().skip_binder().kind()
+            && alias_ty.def_id == def_id
+        {
+            let num_args = self.tcx().generics_of(fn_def_id).count();
+            write!(self, " {{ ")?;
+            self = self.print_def_path(fn_def_id, &args[..num_args])?;
+            write!(self, "() }}")?;
+        }
+
         Ok(self)
     }
 
@@ -1239,21 +1253,18 @@ pub trait PrettyPrinter<'tcx>:
                         .generics_of(principal.def_id)
                         .own_args_no_defaults(cx.tcx(), principal.args);
 
-                    let mut projections = predicates.projection_bounds();
-
-                    let mut args = args.iter().cloned();
-                    let arg0 = args.next();
-                    let projection0 = projections.next();
-                    if arg0.is_some() || projection0.is_some() {
-                        let args = arg0.into_iter().chain(args);
-                        let projections = projection0.into_iter().chain(projections);
+                    let mut projections: Vec<_> = predicates.projection_bounds().collect();
+                    projections.sort_by_cached_key(|proj| {
+                        cx.tcx().item_name(proj.item_def_id()).to_string()
+                    });
 
+                    if !args.is_empty() || !projections.is_empty() {
                         p!(generic_delimiters(|mut cx| {
-                            cx = cx.comma_sep(args)?;
-                            if arg0.is_some() && projection0.is_some() {
+                            cx = cx.comma_sep(args.iter().copied())?;
+                            if !args.is_empty() && !projections.is_empty() {
                                 write!(cx, ", ")?;
                             }
-                            cx.comma_sep(projections)
+                            cx.comma_sep(projections.iter().copied())
                         }));
                     }
                 }
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index f979ddd00fa..7c25d0209c9 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -18,6 +18,7 @@ use std::ops::ControlFlow;
 use std::rc::Rc;
 use std::sync::Arc;
 
+use super::print::PrettyPrinter;
 use super::{GenericArg, GenericArgKind, Region};
 
 impl fmt::Debug for ty::TraitDef {
@@ -138,6 +139,12 @@ impl<'tcx> fmt::Debug for ty::ConstVid<'tcx> {
     }
 }
 
+impl fmt::Debug for ty::EffectVid<'_> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "?{}e", self.index)
+    }
+}
+
 impl<'tcx> fmt::Debug for ty::TraitRef<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         with_no_trimmed_paths!(fmt::Display::fmt(self, f))
@@ -154,7 +161,7 @@ impl<'tcx> ty::DebugWithInfcx<TyCtxt<'tcx>> for Ty<'tcx> {
 }
 impl<'tcx> fmt::Debug for Ty<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        with_no_trimmed_paths!(fmt::Display::fmt(self, f))
+        with_no_trimmed_paths!(fmt::Debug::fmt(self.kind(), f))
     }
 }
 
@@ -253,6 +260,7 @@ impl<'tcx> fmt::Debug for ty::InferConst<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             InferConst::Var(var) => write!(f, "{var:?}"),
+            InferConst::EffectVar(var) => write!(f, "{var:?}"),
             InferConst::Fresh(var) => write!(f, "Fresh({var:?})"),
         }
     }
@@ -267,6 +275,7 @@ impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for ty::InferConst<'tcx> {
             None => write!(f, "{:?}", this.data),
             Some(universe) => match *this.data {
                 Var(vid) => write!(f, "?{}_{}c", vid.index, universe.index()),
+                EffectVar(vid) => write!(f, "?{}_{}e", vid.index, universe.index()),
                 Fresh(_) => {
                     unreachable!()
                 }
@@ -335,14 +344,27 @@ impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for ty::Const<'tcx> {
         this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
         f: &mut core::fmt::Formatter<'_>,
     ) -> core::fmt::Result {
-        // This reflects what `Const` looked liked before `Interned` was
-        // introduced. We print it like this to avoid having to update expected
-        // output in a lot of tests.
+        // If this is a value, we spend some effort to make it look nice.
+        if let ConstKind::Value(_) = this.data.kind() {
+            return ty::tls::with(move |tcx| {
+                // Somehow trying to lift the valtree results in lifetime errors, so we lift the
+                // entire constant.
+                let lifted = tcx.lift(*this.data).unwrap();
+                let ConstKind::Value(valtree) = lifted.kind() else {
+                    bug!("we checked that this is a valtree")
+                };
+                let cx = FmtPrinter::new(tcx, Namespace::ValueNS);
+                let cx =
+                    cx.pretty_print_const_valtree(valtree, lifted.ty(), /*print_ty*/ true)?;
+                f.write_str(&cx.into_buffer())
+            });
+        }
+        // Fall back to something verbose.
         write!(
             f,
-            "Const {{ ty: {:?}, kind: {:?} }}",
-            &this.map(|data| data.ty()),
-            &this.map(|data| data.kind())
+            "{kind:?}: {ty:?}",
+            ty = &this.map(|data| data.ty()),
+            kind = &this.map(|data| data.kind())
         )
     }
 }
@@ -502,6 +524,7 @@ TrivialTypeTraversalAndLiftImpls! {
     ::rustc_span::symbol::Ident,
     ::rustc_errors::ErrorGuaranteed,
     interpret::Scalar,
+    interpret::AllocId,
     rustc_target::abi::Size,
     ty::BoundVar,
 }
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 0291cdd6c57..2502e303f7a 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -1577,6 +1577,20 @@ pub struct ConstVid<'tcx> {
     pub phantom: PhantomData<&'tcx ()>,
 }
 
+/// An **effect** **v**ariable **ID**.
+///
+/// Handling effect infer variables happens separately from const infer variables
+/// because we do not want to reuse any of the const infer machinery. If we try to
+/// relate an effect variable with a normal one, we would ICE, which can catch bugs
+/// where we are not correctly using the effect var for an effect param. Fallback
+/// is also implemented on top of having separate effect and normal const variables.
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(TyEncodable, TyDecodable)]
+pub struct EffectVid<'tcx> {
+    pub index: u32,
+    pub phantom: PhantomData<&'tcx ()>,
+}
+
 rustc_index::newtype_index! {
     /// A **region** (lifetime) **v**ariable **ID**.
     #[derive(HashStable)]
@@ -2735,6 +2749,8 @@ impl<'tcx> Ty<'tcx> {
             | ty::Error(_)
             // Extern types have metadata = ().
             | ty::Foreign(..)
+            // `dyn*` has no metadata
+            | ty::Dynamic(_, _, DynKind::DynStar)
             // If returned by `struct_tail_without_normalization` this is a unit struct
             // without any fields, or not a struct, and therefore is Sized.
             | ty::Adt(..)
@@ -2743,7 +2759,7 @@ impl<'tcx> Ty<'tcx> {
             | ty::Tuple(..) => (tcx.types.unit, false),
 
             ty::Str | ty::Slice(_) => (tcx.types.usize, false),
-            ty::Dynamic(..) => {
+            ty::Dynamic(_, _, DynKind::Dyn) => {
                 let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None);
                 (tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()]), false)
             },
@@ -2857,7 +2873,7 @@ impl<'tcx> Ty<'tcx> {
             | ty::Uint(..)
             | ty::Float(..) => true,
 
-            // The voldemort ZSTs are fine.
+            // ZST which can't be named are fine.
             ty::FnDef(..) => true,
 
             ty::Array(element_ty, _len) => element_ty.is_trivially_pure_clone_copy(),
diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs
index 6e55e7915c9..bf9b244936f 100644
--- a/compiler/rustc_middle/src/ty/trait_def.rs
+++ b/compiler/rustc_middle/src/ty/trait_def.rs
@@ -90,6 +90,10 @@ pub struct TraitImpls {
 }
 
 impl TraitImpls {
+    pub fn is_empty(&self) -> bool {
+        self.blanket_impls.is_empty() && self.non_blanket_impls.is_empty()
+    }
+
     pub fn blanket_impls(&self) -> &[DefId] {
         self.blanket_impls.as_slice()
     }
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index 327cd0a5d7b..159cbb72a3b 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -165,7 +165,7 @@ pub struct TypeckResults<'tcx> {
     /// reading places that are mentioned in a closure (because of _ patterns). However,
     /// to ensure the places are initialized, we introduce fake reads.
     /// Consider these two examples:
-    /// ``` (discriminant matching with only wildcard arm)
+    /// ```ignore (discriminant matching with only wildcard arm)
     /// let x: u8;
     /// let c = || match x { _ => () };
     /// ```
@@ -173,7 +173,7 @@ pub struct TypeckResults<'tcx> {
     /// want to capture it. However, we do still want an error here, because `x` should have
     /// to be initialized at the point where c is created. Therefore, we add a "fake read"
     /// instead.
-    /// ``` (destructured assignments)
+    /// ```ignore (destructured assignments)
     /// let c = || {
     ///     let (t1, t2) = t;
     /// }
@@ -722,3 +722,14 @@ pub enum UserType<'tcx> {
     /// given substitutions applied.
     TypeOf(DefId, UserArgs<'tcx>),
 }
+
+impl<'tcx> std::fmt::Display for UserType<'tcx> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Ty(arg0) => {
+                ty::print::with_no_trimmed_paths!(write!(f, "Ty({})", arg0))
+            }
+            Self::TypeOf(arg0, arg1) => write!(f, "TypeOf({:?}, {:?})", arg0, arg1),
+        }
+    }
+}
diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs
index 156eda477ad..5deb5bfb19e 100644
--- a/compiler/rustc_middle/src/ty/visit.rs
+++ b/compiler/rustc_middle/src/ty/visit.rs
@@ -33,14 +33,6 @@ pub trait TypeVisitableExt<'tcx>: TypeVisitable<TyCtxt<'tcx>> {
     }
 
     fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        // N.B. Even though this uses a visitor, the visitor does not actually
-        //      recurse through the whole `TypeVisitable` implementor type.
-        //
-        //      Instead it stops on the first "level", visiting types, regions,
-        //      consts and predicates just fetches their type flags.
-        //
-        //      Thus this is a lot faster than it might seem and should be
-        //      optimized to a simple field access.
         let res =
             self.visit_with(&mut HasTypeFlagsVisitor { flags }).break_value() == Some(FoundFlags);
         trace!(?self, ?flags, ?res, "has_type_flags");
@@ -485,11 +477,40 @@ impl std::fmt::Debug for HasTypeFlagsVisitor {
     }
 }
 
+// Note: this visitor traverses values down to the level of
+// `Ty`/`Const`/`Predicate`, but not within those types. This is because the
+// type flags at the outer layer are enough. So it's faster than it first
+// looks, particular for `Ty`/`Predicate` where it's just a field access.
+//
+// N.B. The only case where this isn't totally true is binders, which also
+// add `HAS_{RE,TY,CT}_LATE_BOUND` flag depending on the *bound variables* that
+// are present, regardless of whether those bound variables are used. This
+// is important for anonymization of binders in `TyCtxt::erase_regions`. We
+// specifically detect this case in `visit_binder`.
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasTypeFlagsVisitor {
     type BreakTy = FoundFlags;
 
+    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
+        &mut self,
+        t: &Binder<'tcx, T>,
+    ) -> ControlFlow<Self::BreakTy> {
+        // If we're looking for any of the HAS_*_LATE_BOUND flags, we need to
+        // additionally consider the bound vars on the binder itself, even if
+        // the contents of a the binder (e.g. a `TraitRef`) doesn't reference
+        // the bound vars.
+        if self.flags.intersects(TypeFlags::HAS_LATE_BOUND) {
+            let bound_var_flags = FlagComputation::bound_var_flags(t.bound_vars());
+            if bound_var_flags.flags.intersects(self.flags) {
+                return ControlFlow::Break(FoundFlags);
+            }
+        }
+
+        t.super_visit_with(self)
+    }
+
     #[inline]
     fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+        // Note: no `super_visit_with` call.
         let flags = t.flags();
         if flags.intersects(self.flags) {
             ControlFlow::Break(FoundFlags)
@@ -500,6 +521,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasTypeFlagsVisitor {
 
     #[inline]
     fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+        // Note: no `super_visit_with` call, as usual for `Region`.
         let flags = r.type_flags();
         if flags.intersects(self.flags) {
             ControlFlow::Break(FoundFlags)
@@ -510,6 +532,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasTypeFlagsVisitor {
 
     #[inline]
     fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+        // Note: no `super_visit_with` call.
         let flags = FlagComputation::for_const(c);
         trace!(r.flags=?flags);
         if flags.intersects(self.flags) {
@@ -521,6 +544,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasTypeFlagsVisitor {
 
     #[inline]
     fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
+        // Note: no `super_visit_with` call.
         if predicate.flags().intersects(self.flags) {
             ControlFlow::Break(FoundFlags)
         } else {
diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs
index 97402caa001..62f41921d88 100644
--- a/compiler/rustc_middle/src/ty/vtable.rs
+++ b/compiler/rustc_middle/src/ty/vtable.rs
@@ -84,7 +84,7 @@ pub(super) fn vtable_allocation_provider<'tcx>(
         let scalar = match entry {
             VtblEntry::MetadataDropInPlace => {
                 let instance = ty::Instance::resolve_drop_in_place(tcx, ty);
-                let fn_alloc_id = tcx.create_fn_alloc(instance);
+                let fn_alloc_id = tcx.reserve_and_set_fn_alloc(instance);
                 let fn_ptr = Pointer::from(fn_alloc_id);
                 Scalar::from_pointer(fn_ptr, &tcx)
             }
@@ -94,7 +94,7 @@ pub(super) fn vtable_allocation_provider<'tcx>(
             VtblEntry::Method(instance) => {
                 // Prepare the fn ptr we write into the vtable.
                 let instance = instance.polymorphize(tcx);
-                let fn_alloc_id = tcx.create_fn_alloc(instance);
+                let fn_alloc_id = tcx.reserve_and_set_fn_alloc(instance);
                 let fn_ptr = Pointer::from(fn_alloc_id);
                 Scalar::from_pointer(fn_ptr, &tcx)
             }
@@ -112,5 +112,5 @@ pub(super) fn vtable_allocation_provider<'tcx>(
     }
 
     vtable.mutability = Mutability::Not;
-    tcx.create_memory_alloc(tcx.mk_const_alloc(vtable))
+    tcx.reserve_and_set_memory_alloc(tcx.mk_const_alloc(vtable))
 }
diff --git a/compiler/rustc_mir_build/src/build/custom/parse.rs b/compiler/rustc_mir_build/src/build/custom/parse.rs
index f83c62fd580..e2ab2cb90c7 100644
--- a/compiler/rustc_mir_build/src/build/custom/parse.rs
+++ b/compiler/rustc_mir_build/src/build/custom/parse.rs
@@ -241,6 +241,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
             let dbginfo = VarDebugInfo {
                 name,
                 source_info: SourceInfo { span, scope: self.source_scope },
+                composite: None,
                 argument_index: None,
                 value,
             };
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 5ec216cea61..7e81c8b50c2 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -2287,6 +2287,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             name,
             source_info: debug_source_info,
             value: VarDebugInfoContents::Place(for_arm_body.into()),
+            composite: None,
             argument_index: None,
         });
         let locals = if has_guard.0 {
@@ -2306,6 +2307,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 name,
                 source_info: debug_source_info,
                 value: VarDebugInfoContents::Place(ref_for_guard.into()),
+                composite: None,
                 argument_index: None,
             });
             LocalsForNode::ForGuard { ref_for_guard, for_arm_body }
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index e614046e83e..4e10916ad61 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -823,6 +823,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     name,
                     source_info: SourceInfo::outermost(captured_place.var_ident.span),
                     value: VarDebugInfoContents::Place(use_place),
+                    composite: None,
                     argument_index: None,
                 });
 
@@ -852,6 +853,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     name,
                     source_info,
                     value: VarDebugInfoContents::Place(arg_local.into()),
+                    composite: None,
                     argument_index: Some(argument_index as u16 + 1),
                 });
             }
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 6c1f7d7a606..18b0e9a643e 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -950,7 +950,7 @@ impl<'tcx> Cx<'tcx> {
                 let kind = if self.tcx.is_thread_local_static(id) {
                     ExprKind::ThreadLocalRef(id)
                 } else {
-                    let alloc_id = self.tcx.create_static_alloc(id);
+                    let alloc_id = self.tcx.reserve_and_set_static_alloc(id);
                     ExprKind::StaticRef { alloc_id, ty, def_id: id }
                 };
                 ExprKind::Deref {
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 7736183027f..0ea61ec8d40 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -198,11 +198,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
             }
             (Some(PatKind::Constant { value: lo }), None) => {
                 let hi = ty.numeric_max_val(self.tcx)?;
-                Some((*lo, mir::ConstantKind::from_const(hi, self.tcx)))
+                Some((*lo, mir::ConstantKind::from_ty_const(hi, self.tcx)))
             }
             (None, Some(PatKind::Constant { value: hi })) => {
                 let lo = ty.numeric_min_val(self.tcx)?;
-                Some((mir::ConstantKind::from_const(lo, self.tcx), *hi))
+                Some((mir::ConstantKind::from_ty_const(lo, self.tcx), *hi))
             }
             _ => None,
         }
@@ -626,6 +626,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
 
         let ct = ty::UnevaluatedConst { def: def_id.to_def_id(), args: args };
         // First try using a valtree in order to destructure the constant into a pattern.
+        // FIXME: replace "try to do a thing, then fall back to another thing"
+        // but something more principled, like a trait query checking whether this can be turned into a valtree.
         if let Ok(Some(valtree)) =
             self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, Some(span))
         {
diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs
index 6872ef5e985..514146b5030 100644
--- a/compiler/rustc_mir_dataflow/src/value_analysis.rs
+++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs
@@ -532,7 +532,7 @@ impl<V: Clone + HasTop + HasBottom> State<V> {
     /// places that are non-overlapping or identical.
     ///
     /// The target place must have been flooded before calling this method.
-    fn insert_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map) {
+    pub fn insert_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map) {
         let StateData::Reachable(values) = &mut self.0 else { return };
 
         // If both places are tracked, we copy the value to the target.
@@ -581,6 +581,14 @@ impl<V: Clone + HasTop + HasBottom> State<V> {
         }
     }
 
+    /// Retrieve the value stored for a place, or ⊤ if it is not tracked.
+    pub fn get_len(&self, place: PlaceRef<'_>, map: &Map) -> V {
+        match map.find_len(place) {
+            Some(place) => self.get_idx(place, map),
+            None => V::TOP,
+        }
+    }
+
     /// Retrieve the value stored for a place index, or ⊤ if it is not tracked.
     pub fn get_idx(&self, place: PlaceIndex, map: &Map) -> V {
         match &self.0 {
@@ -626,45 +634,36 @@ pub struct Map {
 }
 
 impl Map {
-    fn new() -> Self {
-        Self {
+    /// Returns a map that only tracks places whose type has scalar layout.
+    ///
+    /// This is currently the only way to create a [`Map`]. The way in which the tracked places are
+    /// chosen is an implementation detail and may not be relied upon (other than that their type
+    /// are scalars).
+    pub fn new<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, value_limit: Option<usize>) -> Self {
+        let mut map = Self {
             locals: IndexVec::new(),
             projections: FxHashMap::default(),
             places: IndexVec::new(),
             value_count: 0,
             inner_values: IndexVec::new(),
             inner_values_buffer: Vec::new(),
-        }
-    }
-
-    /// Returns a map that only tracks places whose type passes the filter.
-    ///
-    /// This is currently the only way to create a [`Map`]. The way in which the tracked places are
-    /// chosen is an implementation detail and may not be relied upon (other than that their type
-    /// passes the filter).
-    pub fn from_filter<'tcx>(
-        tcx: TyCtxt<'tcx>,
-        body: &Body<'tcx>,
-        filter: impl Fn(Ty<'tcx>) -> bool,
-        value_limit: Option<usize>,
-    ) -> Self {
-        let mut map = Self::new();
+        };
         let exclude = excluded_locals(body);
-        map.register_with_filter(tcx, body, filter, exclude, value_limit);
+        map.register(tcx, body, exclude, value_limit);
         debug!("registered {} places ({} nodes in total)", map.value_count, map.places.len());
         map
     }
 
-    /// Register all non-excluded places that pass the filter.
-    fn register_with_filter<'tcx>(
+    /// Register all non-excluded places that have scalar layout.
+    fn register<'tcx>(
         &mut self,
         tcx: TyCtxt<'tcx>,
         body: &Body<'tcx>,
-        filter: impl Fn(Ty<'tcx>) -> bool,
         exclude: BitSet<Local>,
         value_limit: Option<usize>,
     ) {
         let mut worklist = VecDeque::with_capacity(value_limit.unwrap_or(body.local_decls.len()));
+        let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
 
         // Start by constructing the places for each bare local.
         self.locals = IndexVec::from_elem(None, &body.local_decls);
@@ -679,7 +678,7 @@ impl Map {
             self.locals[local] = Some(place);
 
             // And push the eventual children places to the worklist.
-            self.register_children(tcx, place, decl.ty, &filter, &mut worklist);
+            self.register_children(tcx, param_env, place, decl.ty, &mut worklist);
         }
 
         // `place.elem1.elem2` with type `ty`.
@@ -702,7 +701,7 @@ impl Map {
             }
 
             // And push the eventual children places to the worklist.
-            self.register_children(tcx, place, ty, &filter, &mut worklist);
+            self.register_children(tcx, param_env, place, ty, &mut worklist);
         }
 
         // Pre-compute the tree of ValueIndex nested in each PlaceIndex.
@@ -732,42 +731,52 @@ impl Map {
     fn register_children<'tcx>(
         &mut self,
         tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
         place: PlaceIndex,
         ty: Ty<'tcx>,
-        filter: &impl Fn(Ty<'tcx>) -> bool,
         worklist: &mut VecDeque<(PlaceIndex, Option<TrackElem>, TrackElem, Ty<'tcx>)>,
     ) {
         // Allocate a value slot if it doesn't have one, and the user requested one.
-        if self.places[place].value_index.is_none() && filter(ty) {
+        assert!(self.places[place].value_index.is_none());
+        if tcx.layout_of(param_env.and(ty)).map_or(false, |layout| layout.abi.is_scalar()) {
             self.places[place].value_index = Some(self.value_count.into());
             self.value_count += 1;
         }
 
         // For enums, directly create the `Discriminant`, as that's their main use.
         if ty.is_enum() {
-            let discr_ty = ty.discriminant_ty(tcx);
-            if filter(discr_ty) {
-                let discr = *self
-                    .projections
-                    .entry((place, TrackElem::Discriminant))
-                    .or_insert_with(|| {
-                        // Prepend new child to the linked list.
-                        let next = self.places.push(PlaceInfo::new(Some(TrackElem::Discriminant)));
-                        self.places[next].next_sibling = self.places[place].first_child;
-                        self.places[place].first_child = Some(next);
-                        next
-                    });
-
-                // Allocate a value slot if it doesn't have one.
-                if self.places[discr].value_index.is_none() {
-                    self.places[discr].value_index = Some(self.value_count.into());
-                    self.value_count += 1;
-                }
-            }
+            // Prepend new child to the linked list.
+            let discr = self.places.push(PlaceInfo::new(Some(TrackElem::Discriminant)));
+            self.places[discr].next_sibling = self.places[place].first_child;
+            self.places[place].first_child = Some(discr);
+            let old = self.projections.insert((place, TrackElem::Discriminant), discr);
+            assert!(old.is_none());
+
+            // Allocate a value slot since it doesn't have one.
+            assert!(self.places[discr].value_index.is_none());
+            self.places[discr].value_index = Some(self.value_count.into());
+            self.value_count += 1;
+        }
+
+        if let Some(ref_ty) = ty.builtin_deref(true) && let ty::Slice(..) = ref_ty.ty.kind() {
+            assert!(self.places[place].value_index.is_none(), "slices are not scalars");
+
+            // Prepend new child to the linked list.
+            let len = self.places.push(PlaceInfo::new(Some(TrackElem::DerefLen)));
+            self.places[len].next_sibling = self.places[place].first_child;
+            self.places[place].first_child = Some(len);
+
+            let old = self.projections.insert((place, TrackElem::DerefLen), len);
+            assert!(old.is_none());
+
+            // Allocate a value slot since it doesn't have one.
+            assert!( self.places[len].value_index.is_none() );
+            self.places[len].value_index = Some(self.value_count.into());
+            self.value_count += 1;
         }
 
         // Recurse with all fields of this place.
-        iter_fields(ty, tcx, ty::ParamEnv::reveal_all(), |variant, field, ty| {
+        iter_fields(ty, tcx, param_env, |variant, field, ty| {
             worklist.push_back((
                 place,
                 variant.map(TrackElem::Variant),
@@ -834,6 +843,11 @@ impl Map {
         self.find_extra(place, [TrackElem::Discriminant])
     }
 
+    /// Locates the given place and applies `DerefLen`, if it exists in the tree.
+    pub fn find_len(&self, place: PlaceRef<'_>) -> Option<PlaceIndex> {
+        self.find_extra(place, [TrackElem::DerefLen])
+    }
+
     /// Iterate over all direct children.
     pub fn children(&self, parent: PlaceIndex) -> impl Iterator<Item = PlaceIndex> + '_ {
         Children::new(self, parent)
@@ -914,6 +928,31 @@ impl Map {
             f(v)
         }
     }
+
+    /// Invoke a function on each value in the given place and all descendants.
+    pub fn for_each_projection_value<O>(
+        &self,
+        root: PlaceIndex,
+        value: O,
+        project: &mut impl FnMut(TrackElem, &O) -> Option<O>,
+        f: &mut impl FnMut(PlaceIndex, &O),
+    ) {
+        // Fast path is there is nothing to do.
+        if self.inner_values[root].is_empty() {
+            return;
+        }
+
+        if self.places[root].value_index.is_some() {
+            f(root, &value)
+        }
+
+        for child in self.children(root) {
+            let elem = self.places[child].proj_elem.unwrap();
+            if let Some(value) = project(elem, &value) {
+                self.for_each_projection_value(child, value, project, f);
+            }
+        }
+    }
 }
 
 /// This is the information tracked for every [`PlaceIndex`] and is stored by [`Map`].
@@ -985,6 +1024,8 @@ pub enum TrackElem {
     Field(FieldIdx),
     Variant(VariantIdx),
     Discriminant,
+    // Length of a slice.
+    DerefLen,
 }
 
 impl<V, T> TryFrom<ProjectionElem<V, T>> for TrackElem {
@@ -1124,6 +1165,9 @@ fn debug_with_context_rec<V: Debug + Eq>(
                     format!("{}.{}", place_str, field.index())
                 }
             }
+            TrackElem::DerefLen => {
+                format!("Len(*{})", place_str)
+            }
         };
         debug_with_context_rec(child, &child_place_str, new, old, map, f)?;
     }
diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl
index 2598eb2ed09..5a99afc45b0 100644
--- a/compiler/rustc_mir_transform/messages.ftl
+++ b/compiler/rustc_mir_transform/messages.ftl
@@ -42,8 +42,6 @@ mir_transform_requires_unsafe = {$details} is unsafe and requires unsafe {$op_in
     }
     .not_inherited = items do not inherit unsafety from separate enclosing items
 
-mir_transform_simd_shuffle_last_const = last argument of `simd_shuffle` is required to be a `const` item
-
 mir_transform_target_feature_call_label = call to function with `#[target_feature]`
 mir_transform_target_feature_call_note = can only be called if the required target features are available
 
diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs
index 0fce9cb19a8..d5af321d726 100644
--- a/compiler/rustc_mir_transform/src/check_unsafety.rs
+++ b/compiler/rustc_mir_transform/src/check_unsafety.rs
@@ -483,7 +483,7 @@ fn unsafety_check_result(tcx: TyCtxt<'_>, def: LocalDefId) -> &UnsafetyCheckResu
     // `mir_built` force this.
     let body = &tcx.mir_built(def).borrow();
 
-    if body.is_custom_mir() {
+    if body.is_custom_mir() || body.tainted_by_errors.is_some() {
         return tcx.arena.alloc(UnsafetyCheckResult {
             violations: Vec::new(),
             used_unsafe_blocks: Default::default(),
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index 5f135e96980..00e3e3a8f9f 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -15,10 +15,11 @@ use rustc_middle::mir::visit::{
 use rustc_middle::mir::*;
 use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
 use rustc_middle::ty::{self, GenericArgs, Instance, ParamEnv, Ty, TyCtxt, TypeVisitableExt};
-use rustc_span::{def_id::DefId, Span, DUMMY_SP};
+use rustc_span::{def_id::DefId, Span};
 use rustc_target::abi::{self, Align, HasDataLayout, Size, TargetDataLayout};
 use rustc_target::spec::abi::Abi as CallAbi;
 
+use crate::dataflow_const_prop::Patch;
 use crate::MirPass;
 use rustc_const_eval::interpret::{
     self, compile_time_machine, AllocId, ConstAllocation, ConstValue, FnArg, Frame, ImmTy,
@@ -32,32 +33,30 @@ const MAX_ALLOC_LIMIT: u64 = 1024;
 
 /// Macro for machine-specific `InterpError` without allocation.
 /// (These will never be shown to the user, but they help diagnose ICEs.)
-macro_rules! throw_machine_stop_str {
-    ($($tt:tt)*) => {{
-        // We make a new local type for it. The type itself does not carry any information,
-        // but its vtable (for the `MachineStopType` trait) does.
-        #[derive(Debug)]
-        struct Zst;
-        // Printing this type shows the desired string.
-        impl std::fmt::Display for Zst {
-            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-                write!(f, $($tt)*)
-            }
+pub(crate) macro throw_machine_stop_str($($tt:tt)*) {{
+    // We make a new local type for it. The type itself does not carry any information,
+    // but its vtable (for the `MachineStopType` trait) does.
+    #[derive(Debug)]
+    struct Zst;
+    // Printing this type shows the desired string.
+    impl std::fmt::Display for Zst {
+        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+            write!(f, $($tt)*)
         }
+    }
 
-        impl rustc_middle::mir::interpret::MachineStopType for Zst {
-            fn diagnostic_message(&self) -> rustc_errors::DiagnosticMessage {
-                self.to_string().into()
-            }
-
-            fn add_args(
-                self: Box<Self>,
-                _: &mut dyn FnMut(std::borrow::Cow<'static, str>, rustc_errors::DiagnosticArgValue<'static>),
-            ) {}
+    impl rustc_middle::mir::interpret::MachineStopType for Zst {
+        fn diagnostic_message(&self) -> rustc_errors::DiagnosticMessage {
+            self.to_string().into()
         }
-        throw_machine_stop!(Zst)
-    }};
-}
+
+        fn add_args(
+            self: Box<Self>,
+            _: &mut dyn FnMut(std::borrow::Cow<'static, str>, rustc_errors::DiagnosticArgValue<'static>),
+        ) {}
+    }
+    throw_machine_stop!(Zst)
+}}
 
 pub struct ConstProp;
 
@@ -85,9 +84,9 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
             return;
         }
 
-        let is_generator = tcx.type_of(def_id.to_def_id()).instantiate_identity().is_generator();
         // FIXME(welseywiser) const prop doesn't work on generators because of query cycles
         // computing their layout.
+        let is_generator = def_kind == DefKind::Generator;
         if is_generator {
             trace!("ConstProp skipped for generator {:?}", def_id);
             return;
@@ -95,33 +94,22 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
 
         trace!("ConstProp starting for {:?}", def_id);
 
-        let dummy_body = &Body::new(
-            body.source,
-            (*body.basic_blocks).to_owned(),
-            body.source_scopes.clone(),
-            body.local_decls.clone(),
-            Default::default(),
-            body.arg_count,
-            Default::default(),
-            body.span,
-            body.generator_kind(),
-            body.tainted_by_errors,
-        );
-
         // FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold
         // constants, instead of just checking for const-folding succeeding.
         // That would require a uniform one-def no-mutation analysis
         // and RPO (or recursing when needing the value of a local).
-        let mut optimization_finder = ConstPropagator::new(body, dummy_body, tcx);
+        let mut optimization_finder = ConstPropagator::new(body, tcx);
 
         // Traverse the body in reverse post-order, to ensure that `FullConstProp` locals are
         // assigned before being read.
-        let rpo = body.basic_blocks.reverse_postorder().to_vec();
-        for bb in rpo {
-            let data = &mut body.basic_blocks.as_mut_preserves_cfg()[bb];
+        for &bb in body.basic_blocks.reverse_postorder() {
+            let data = &body.basic_blocks[bb];
             optimization_finder.visit_basic_block_data(bb, data);
         }
 
+        let mut patch = optimization_finder.patch;
+        patch.visit_body_preserves_cfg(body);
+
         trace!("ConstProp done for {:?}", def_id);
     }
 }
@@ -145,14 +133,17 @@ impl ConstPropMachine<'_, '_> {
 
 impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> {
     compile_time_machine!(<'mir, 'tcx>);
+
     const PANIC_ON_ALLOC_FAIL: bool = true; // all allocations are small (see `MAX_ALLOC_LIMIT`)
 
+    const POST_MONO_CHECKS: bool = false; // this MIR is still generic!
+
     type MemoryKind = !;
 
     #[inline(always)]
     fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment {
         // We do not check for alignment to avoid having to carry an `Align`
-        // in `ConstValue::ByRef`.
+        // in `ConstValue::Indirect`.
         CheckAlignment::No
     }
 
@@ -301,6 +292,7 @@ struct ConstPropagator<'mir, 'tcx> {
     tcx: TyCtxt<'tcx>,
     param_env: ParamEnv<'tcx>,
     local_decls: &'mir IndexSlice<Local, LocalDecl<'tcx>>,
+    patch: Patch<'tcx>,
 }
 
 impl<'tcx> LayoutOfHelpers<'tcx> for ConstPropagator<'_, 'tcx> {
@@ -334,11 +326,7 @@ impl<'tcx> ty::layout::HasParamEnv<'tcx> for ConstPropagator<'_, 'tcx> {
 }
 
 impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
-    fn new(
-        body: &Body<'tcx>,
-        dummy_body: &'mir Body<'tcx>,
-        tcx: TyCtxt<'tcx>,
-    ) -> ConstPropagator<'mir, 'tcx> {
+    fn new(body: &'mir Body<'tcx>, tcx: TyCtxt<'tcx>) -> ConstPropagator<'mir, 'tcx> {
         let def_id = body.source.def_id();
         let args = &GenericArgs::identity_for_item(tcx, def_id);
         let param_env = tcx.param_env_reveal_all_normalized(def_id);
@@ -369,7 +357,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
 
         ecx.push_stack_frame(
             Instance::new(def_id, args),
-            dummy_body,
+            body,
             &ret,
             StackPopCleanup::Root { cleanup: false },
         )
@@ -384,7 +372,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             ecx.frame_mut().locals[local].make_live_uninit();
         }
 
-        ConstPropagator { ecx, tcx, param_env, local_decls: &dummy_body.local_decls }
+        let patch = Patch::new(tcx);
+        ConstPropagator { ecx, tcx, param_env, local_decls: &body.local_decls, patch }
     }
 
     fn get_const(&self, place: Place<'tcx>) -> Option<OpTy<'tcx>> {
@@ -421,12 +410,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         ecx.machine.written_only_inside_own_block_locals.remove(&local);
     }
 
-    fn propagate_operand(&mut self, operand: &mut Operand<'tcx>) {
-        if let Some(place) = operand.place() && let Some(op) = self.replace_with_const(place) {
-            *operand = op;
-        }
-    }
-
     fn check_rvalue(&mut self, rvalue: &Rvalue<'tcx>) -> Option<()> {
         // Perform any special handling for specific Rvalue types.
         // Generally, checks here fall into one of two categories:
@@ -542,16 +525,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         }
     }
 
-    /// Creates a new `Operand::Constant` from a `Scalar` value
-    fn operand_from_scalar(&self, scalar: Scalar, ty: Ty<'tcx>) -> Operand<'tcx> {
-        Operand::Constant(Box::new(Constant {
-            span: DUMMY_SP,
-            user_ty: None,
-            literal: ConstantKind::from_scalar(self.tcx, scalar, ty),
-        }))
-    }
-
-    fn replace_with_const(&mut self, place: Place<'tcx>) -> Option<Operand<'tcx>> {
+    fn replace_with_const(&mut self, place: Place<'tcx>) -> Option<ConstantKind<'tcx>> {
         // This will return None if the above `const_prop` invocation only "wrote" a
         // type whose creation requires no write. E.g. a generator whose initial state
         // consists solely of uninitialized memory (so it doesn't capture any locals).
@@ -561,31 +535,26 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         }
         trace!("replacing {:?} with {:?}", place, value);
 
-        // FIXME> figure out what to do when read_immediate_raw fails
+        // FIXME: figure out what to do when read_immediate_raw fails
         let imm = self.ecx.read_immediate_raw(&value).ok()?;
 
         let Right(imm) = imm else { return None };
         match *imm {
             Immediate::Scalar(scalar) if scalar.try_to_int().is_ok() => {
-                Some(self.operand_from_scalar(scalar, value.layout.ty))
+                Some(ConstantKind::from_scalar(self.tcx, scalar, value.layout.ty))
             }
             Immediate::ScalarPair(l, r) if l.try_to_int().is_ok() && r.try_to_int().is_ok() => {
-                let alloc = self
+                let alloc_id = self
                     .ecx
                     .intern_with_temp_alloc(value.layout, |ecx, dest| {
                         ecx.write_immediate(*imm, dest)
                     })
                     .ok()?;
 
-                let literal = ConstantKind::Val(
-                    ConstValue::ByRef { alloc, offset: Size::ZERO },
+                Some(ConstantKind::Val(
+                    ConstValue::Indirect { alloc_id, offset: Size::ZERO },
                     value.layout.ty,
-                );
-                Some(Operand::Constant(Box::new(Constant {
-                    span: DUMMY_SP,
-                    user_ty: None,
-                    literal,
-                })))
+                ))
             }
             // Scalars or scalar pairs that contain undef values are assumed to not have
             // successfully evaluated and are thus not propagated.
@@ -727,40 +696,29 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
     }
 }
 
-impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
-    fn tcx(&self) -> TyCtxt<'tcx> {
-        self.tcx
-    }
-
-    fn visit_operand(&mut self, operand: &mut Operand<'tcx>, location: Location) {
+impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
+    fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
         self.super_operand(operand, location);
-        self.propagate_operand(operand)
+        if let Some(place) = operand.place() && let Some(value) = self.replace_with_const(place) {
+            self.patch.before_effect.insert((location, place), value);
+        }
     }
 
-    fn process_projection_elem(
+    fn visit_projection_elem(
         &mut self,
+        _: PlaceRef<'tcx>,
         elem: PlaceElem<'tcx>,
-        _: Location,
-    ) -> Option<PlaceElem<'tcx>> {
+        _: PlaceContext,
+        location: Location,
+    ) {
         if let PlaceElem::Index(local) = elem
-            && let Some(value) = self.get_const(local.into())
-            && let Some(imm) = value.as_mplace_or_imm().right()
-            && let Immediate::Scalar(scalar) = *imm
-            && let Ok(offset) = scalar.to_target_usize(&self.tcx)
-            && let Some(min_length) = offset.checked_add(1)
+            && let Some(value) = self.replace_with_const(local.into())
         {
-            Some(PlaceElem::ConstantIndex { offset, min_length, from_end: false })
-        } else {
-            None
+            self.patch.before_effect.insert((location, local.into()), value);
         }
     }
 
-    fn visit_assign(
-        &mut self,
-        place: &mut Place<'tcx>,
-        rvalue: &mut Rvalue<'tcx>,
-        location: Location,
-    ) {
+    fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
         self.super_assign(place, rvalue, location);
 
         let Some(()) = self.check_rvalue(rvalue) else { return };
@@ -777,7 +735,7 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
                     {
                         trace!("skipping replace of Rvalue::Use({:?} because it is already a const", c);
                     } else if let Some(operand) = self.replace_with_const(*place) {
-                        *rvalue = Rvalue::Use(operand);
+                        self.patch.assignments.insert(location, operand);
                     }
                 } else {
                     // Const prop failed, so erase the destination, ensuring that whatever happens
@@ -801,7 +759,7 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
         }
     }
 
-    fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
+    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
         trace!("visit_statement: {:?}", statement);
 
         // We want to evaluate operands before any change to the assigned-to value,
@@ -845,7 +803,7 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
         }
     }
 
-    fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) {
+    fn visit_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'tcx>) {
         self.super_basic_block_data(block, data);
 
         // We remove all Locals which are restricted in propagation to their containing blocks and
diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs
index 4b51beed095..b52827a1e88 100644
--- a/compiler/rustc_mir_transform/src/const_prop_lint.rs
+++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs
@@ -39,6 +39,10 @@ pub struct ConstProp;
 
 impl<'tcx> MirLint<'tcx> for ConstProp {
     fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
+        if body.tainted_by_errors.is_some() {
+            return;
+        }
+
         // will be evaluated by miri and produce its errors there
         if body.source.promoted.is_some() {
             return;
diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs
index af616c498fd..bb1f16aa8be 100644
--- a/compiler/rustc_mir_transform/src/coverage/debug.rs
+++ b/compiler/rustc_mir_transform/src/coverage/debug.rs
@@ -44,7 +44,7 @@
 //! points, which can be enabled via environment variable:
 //!
 //! ```shell
-//! RUSTC_LOG=rustc_mir_transform::transform::coverage=debug
+//! RUSTC_LOG=rustc_mir_transform::coverage=debug
 //! ```
 //!
 //! Other module paths with coverage-related debug logs may also be of interest, particularly for
@@ -52,7 +52,7 @@
 //! code generation pass). For example:
 //!
 //! ```shell
-//! RUSTC_LOG=rustc_mir_transform::transform::coverage,rustc_codegen_ssa::coverageinfo,rustc_codegen_llvm::coverageinfo=debug
+//! RUSTC_LOG=rustc_mir_transform::coverage,rustc_codegen_llvm::coverageinfo=debug
 //! ```
 //!
 //! Coverage Debug Options
@@ -108,24 +108,23 @@
 //!         recursively, generating labels with nested operations, enclosed in parentheses
 //!         (for example: `bcb2 + (bcb0 - bcb1)`).
 
-use super::counters::{BcbCounter, CoverageCounters};
-use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph};
-use super::spans::CoverageSpan;
+use std::iter;
+use std::ops::Deref;
+use std::sync::OnceLock;
 
 use itertools::Itertools;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_middle::mir::coverage::*;
 use rustc_middle::mir::create_dump_file;
 use rustc_middle::mir::generic_graphviz::GraphvizWriter;
 use rustc_middle::mir::spanview::{self, SpanViewable};
-
-use rustc_data_structures::fx::FxHashMap;
-use rustc_middle::mir::coverage::*;
 use rustc_middle::mir::{self, BasicBlock};
 use rustc_middle::ty::TyCtxt;
 use rustc_span::Span;
 
-use std::iter;
-use std::ops::Deref;
-use std::sync::OnceLock;
+use super::counters::{BcbCounter, CoverageCounters};
+use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph};
+use super::spans::CoverageSpan;
 
 pub const NESTED_INDENT: &str = "    ";
 
@@ -259,36 +258,42 @@ impl Default for ExpressionFormat {
 /// `DebugCounters` supports a recursive rendering of `Expression` counters, so they can be
 /// presented as nested expressions such as `(bcb3 - (bcb0 + bcb1))`.
 pub(super) struct DebugCounters {
-    some_counters: Option<FxHashMap<Operand, DebugCounter>>,
+    state: Option<DebugCountersState>,
+}
+
+#[derive(Default)]
+struct DebugCountersState {
+    counters: FxHashMap<Operand, DebugCounter>,
 }
 
 impl DebugCounters {
     pub fn new() -> Self {
-        Self { some_counters: None }
+        Self { state: None }
     }
 
     pub fn enable(&mut self) {
         debug_assert!(!self.is_enabled());
-        self.some_counters.replace(FxHashMap::default());
+        self.state = Some(DebugCountersState::default());
     }
 
     pub fn is_enabled(&self) -> bool {
-        self.some_counters.is_some()
+        self.state.is_some()
     }
 
     pub fn add_counter(&mut self, counter_kind: &BcbCounter, some_block_label: Option<String>) {
-        if let Some(counters) = &mut self.some_counters {
-            let id = counter_kind.as_operand();
-            counters
-                .try_insert(id, DebugCounter::new(counter_kind.clone(), some_block_label))
-                .expect("attempt to add the same counter_kind to DebugCounters more than once");
-        }
+        let Some(state) = &mut self.state else { return };
+
+        let id = counter_kind.as_operand();
+        state
+            .counters
+            .try_insert(id, DebugCounter::new(counter_kind.clone(), some_block_label))
+            .expect("attempt to add the same counter_kind to DebugCounters more than once");
     }
 
     pub fn some_block_label(&self, operand: Operand) -> Option<&String> {
-        self.some_counters.as_ref().and_then(|counters| {
-            counters.get(&operand).and_then(|debug_counter| debug_counter.some_block_label.as_ref())
-        })
+        let Some(state) = &self.state else { return None };
+
+        state.counters.get(&operand)?.some_block_label.as_ref()
     }
 
     pub fn format_counter(&self, counter_kind: &BcbCounter) -> String {
@@ -308,7 +313,7 @@ impl DebugCounters {
             if counter_format.operation {
                 return format!(
                     "{}{} {} {}",
-                    if counter_format.id || self.some_counters.is_none() {
+                    if counter_format.id || !self.is_enabled() {
                         format!("#{} = ", id.index())
                     } else {
                         String::new()
@@ -324,10 +329,9 @@ impl DebugCounters {
         }
 
         let id = counter_kind.as_operand();
-        if self.some_counters.is_some() && (counter_format.block || !counter_format.id) {
-            let counters = self.some_counters.as_ref().unwrap();
+        if let Some(state) = &self.state && (counter_format.block || !counter_format.id) {
             if let Some(DebugCounter { some_block_label: Some(block_label), .. }) =
-                counters.get(&id)
+                state.counters.get(&id)
             {
                 return if counter_format.id {
                     format!("{}#{:?}", block_label, id)
@@ -343,8 +347,10 @@ impl DebugCounters {
         if matches!(operand, Operand::Zero) {
             return String::from("0");
         }
-        if let Some(counters) = &self.some_counters {
-            if let Some(DebugCounter { counter_kind, some_block_label }) = counters.get(&operand) {
+        if let Some(state) = &self.state {
+            if let Some(DebugCounter { counter_kind, some_block_label }) =
+                state.counters.get(&operand)
+            {
                 if let BcbCounter::Expression { .. } = counter_kind {
                     if let Some(label) = some_block_label && debug_options().counter_format.block {
                         return format!(
@@ -378,30 +384,29 @@ impl DebugCounter {
 /// If enabled, this data structure captures additional debugging information used when generating
 /// a Graphviz (.dot file) representation of the `CoverageGraph`, for debugging purposes.
 pub(super) struct GraphvizData {
-    some_bcb_to_coverage_spans_with_counters:
-        Option<FxHashMap<BasicCoverageBlock, Vec<(CoverageSpan, BcbCounter)>>>,
-    some_bcb_to_dependency_counters: Option<FxHashMap<BasicCoverageBlock, Vec<BcbCounter>>>,
-    some_edge_to_counter: Option<FxHashMap<(BasicCoverageBlock, BasicBlock), BcbCounter>>,
+    state: Option<GraphvizDataState>,
+}
+
+#[derive(Default)]
+struct GraphvizDataState {
+    bcb_to_coverage_spans_with_counters:
+        FxHashMap<BasicCoverageBlock, Vec<(CoverageSpan, BcbCounter)>>,
+    bcb_to_dependency_counters: FxHashMap<BasicCoverageBlock, Vec<BcbCounter>>,
+    edge_to_counter: FxHashMap<(BasicCoverageBlock, BasicBlock), BcbCounter>,
 }
 
 impl GraphvizData {
     pub fn new() -> Self {
-        Self {
-            some_bcb_to_coverage_spans_with_counters: None,
-            some_bcb_to_dependency_counters: None,
-            some_edge_to_counter: None,
-        }
+        Self { state: None }
     }
 
     pub fn enable(&mut self) {
         debug_assert!(!self.is_enabled());
-        self.some_bcb_to_coverage_spans_with_counters = Some(FxHashMap::default());
-        self.some_bcb_to_dependency_counters = Some(FxHashMap::default());
-        self.some_edge_to_counter = Some(FxHashMap::default());
+        self.state = Some(GraphvizDataState::default());
     }
 
     pub fn is_enabled(&self) -> bool {
-        self.some_bcb_to_coverage_spans_with_counters.is_some()
+        self.state.is_some()
     }
 
     pub fn add_bcb_coverage_span_with_counter(
@@ -410,27 +415,22 @@ impl GraphvizData {
         coverage_span: &CoverageSpan,
         counter_kind: &BcbCounter,
     ) {
-        if let Some(bcb_to_coverage_spans_with_counters) =
-            self.some_bcb_to_coverage_spans_with_counters.as_mut()
-        {
-            bcb_to_coverage_spans_with_counters
-                .entry(bcb)
-                .or_insert_with(Vec::new)
-                .push((coverage_span.clone(), counter_kind.clone()));
-        }
+        let Some(state) = &mut self.state else { return };
+
+        state
+            .bcb_to_coverage_spans_with_counters
+            .entry(bcb)
+            .or_insert_with(Vec::new)
+            .push((coverage_span.clone(), counter_kind.clone()));
     }
 
     pub fn get_bcb_coverage_spans_with_counters(
         &self,
         bcb: BasicCoverageBlock,
     ) -> Option<&[(CoverageSpan, BcbCounter)]> {
-        if let Some(bcb_to_coverage_spans_with_counters) =
-            self.some_bcb_to_coverage_spans_with_counters.as_ref()
-        {
-            bcb_to_coverage_spans_with_counters.get(&bcb).map(Deref::deref)
-        } else {
-            None
-        }
+        let Some(state) = &self.state else { return None };
+
+        state.bcb_to_coverage_spans_with_counters.get(&bcb).map(Deref::deref)
     }
 
     pub fn add_bcb_dependency_counter(
@@ -438,20 +438,19 @@ impl GraphvizData {
         bcb: BasicCoverageBlock,
         counter_kind: &BcbCounter,
     ) {
-        if let Some(bcb_to_dependency_counters) = self.some_bcb_to_dependency_counters.as_mut() {
-            bcb_to_dependency_counters
-                .entry(bcb)
-                .or_insert_with(Vec::new)
-                .push(counter_kind.clone());
-        }
+        let Some(state) = &mut self.state else { return };
+
+        state
+            .bcb_to_dependency_counters
+            .entry(bcb)
+            .or_insert_with(Vec::new)
+            .push(counter_kind.clone());
     }
 
     pub fn get_bcb_dependency_counters(&self, bcb: BasicCoverageBlock) -> Option<&[BcbCounter]> {
-        if let Some(bcb_to_dependency_counters) = self.some_bcb_to_dependency_counters.as_ref() {
-            bcb_to_dependency_counters.get(&bcb).map(Deref::deref)
-        } else {
-            None
-        }
+        let Some(state) = &self.state else { return None };
+
+        state.bcb_to_dependency_counters.get(&bcb).map(Deref::deref)
     }
 
     pub fn set_edge_counter(
@@ -460,11 +459,12 @@ impl GraphvizData {
         to_bb: BasicBlock,
         counter_kind: &BcbCounter,
     ) {
-        if let Some(edge_to_counter) = self.some_edge_to_counter.as_mut() {
-            edge_to_counter
-                .try_insert((from_bcb, to_bb), counter_kind.clone())
-                .expect("invalid attempt to insert more than one edge counter for the same edge");
-        }
+        let Some(state) = &mut self.state else { return };
+
+        state
+            .edge_to_counter
+            .try_insert((from_bcb, to_bb), counter_kind.clone())
+            .expect("invalid attempt to insert more than one edge counter for the same edge");
     }
 
     pub fn get_edge_counter(
@@ -472,11 +472,9 @@ impl GraphvizData {
         from_bcb: BasicCoverageBlock,
         to_bb: BasicBlock,
     ) -> Option<&BcbCounter> {
-        if let Some(edge_to_counter) = self.some_edge_to_counter.as_ref() {
-            edge_to_counter.get(&(from_bcb, to_bb))
-        } else {
-            None
-        }
+        let Some(state) = &self.state else { return None };
+
+        state.edge_to_counter.get(&(from_bcb, to_bb))
     }
 }
 
@@ -485,41 +483,42 @@ impl GraphvizData {
 /// _not_ used are retained in the `unused_expressions` Vec, to be included in debug output (logs
 /// and/or a `CoverageGraph` graphviz output).
 pub(super) struct UsedExpressions {
-    some_used_expression_operands: Option<FxHashMap<Operand, Vec<ExpressionId>>>,
-    some_unused_expressions:
-        Option<Vec<(BcbCounter, Option<BasicCoverageBlock>, BasicCoverageBlock)>>,
+    state: Option<UsedExpressionsState>,
+}
+
+#[derive(Default)]
+struct UsedExpressionsState {
+    used_expression_operands: FxHashSet<Operand>,
+    unused_expressions: Vec<(BcbCounter, Option<BasicCoverageBlock>, BasicCoverageBlock)>,
 }
 
 impl UsedExpressions {
     pub fn new() -> Self {
-        Self { some_used_expression_operands: None, some_unused_expressions: None }
+        Self { state: None }
     }
 
     pub fn enable(&mut self) {
         debug_assert!(!self.is_enabled());
-        self.some_used_expression_operands = Some(FxHashMap::default());
-        self.some_unused_expressions = Some(Vec::new());
+        self.state = Some(UsedExpressionsState::default())
     }
 
     pub fn is_enabled(&self) -> bool {
-        self.some_used_expression_operands.is_some()
+        self.state.is_some()
     }
 
     pub fn add_expression_operands(&mut self, expression: &BcbCounter) {
-        if let Some(used_expression_operands) = self.some_used_expression_operands.as_mut() {
-            if let BcbCounter::Expression { id, lhs, rhs, .. } = *expression {
-                used_expression_operands.entry(lhs).or_insert_with(Vec::new).push(id);
-                used_expression_operands.entry(rhs).or_insert_with(Vec::new).push(id);
-            }
+        let Some(state) = &mut self.state else { return };
+
+        if let BcbCounter::Expression { lhs, rhs, .. } = *expression {
+            state.used_expression_operands.insert(lhs);
+            state.used_expression_operands.insert(rhs);
         }
     }
 
     pub fn expression_is_used(&self, expression: &BcbCounter) -> bool {
-        if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() {
-            used_expression_operands.contains_key(&expression.as_operand())
-        } else {
-            false
-        }
+        let Some(state) = &self.state else { return false };
+
+        state.used_expression_operands.contains(&expression.as_operand())
     }
 
     pub fn add_unused_expression_if_not_found(
@@ -528,14 +527,10 @@ impl UsedExpressions {
         edge_from_bcb: Option<BasicCoverageBlock>,
         target_bcb: BasicCoverageBlock,
     ) {
-        if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() {
-            if !used_expression_operands.contains_key(&expression.as_operand()) {
-                self.some_unused_expressions.as_mut().unwrap().push((
-                    expression.clone(),
-                    edge_from_bcb,
-                    target_bcb,
-                ));
-            }
+        let Some(state) = &mut self.state else { return };
+
+        if !state.used_expression_operands.contains(&expression.as_operand()) {
+            state.unused_expressions.push((expression.clone(), edge_from_bcb, target_bcb));
         }
     }
 
@@ -544,11 +539,9 @@ impl UsedExpressions {
     pub fn get_unused_expressions(
         &self,
     ) -> Vec<(BcbCounter, Option<BasicCoverageBlock>, BasicCoverageBlock)> {
-        if let Some(unused_expressions) = self.some_unused_expressions.as_ref() {
-            unused_expressions.clone()
-        } else {
-            Vec::new()
-        }
+        let Some(state) = &self.state else { return Vec::new() };
+
+        state.unused_expressions.clone()
     }
 
     /// If enabled, validate that every BCB or edge counter not directly associated with a coverage
@@ -562,51 +555,53 @@ impl UsedExpressions {
             BcbCounter,
         )],
     ) {
-        if self.is_enabled() {
-            let mut not_validated = bcb_counters_without_direct_coverage_spans
-                .iter()
-                .map(|(_, _, counter_kind)| counter_kind)
-                .collect::<Vec<_>>();
-            let mut validating_count = 0;
-            while not_validated.len() != validating_count {
-                let to_validate = not_validated.split_off(0);
-                validating_count = to_validate.len();
-                for counter_kind in to_validate {
-                    if self.expression_is_used(counter_kind) {
-                        self.add_expression_operands(counter_kind);
-                    } else {
-                        not_validated.push(counter_kind);
-                    }
+        if !self.is_enabled() {
+            return;
+        }
+
+        let mut not_validated = bcb_counters_without_direct_coverage_spans
+            .iter()
+            .map(|(_, _, counter_kind)| counter_kind)
+            .collect::<Vec<_>>();
+        let mut validating_count = 0;
+        while not_validated.len() != validating_count {
+            let to_validate = not_validated.split_off(0);
+            validating_count = to_validate.len();
+            for counter_kind in to_validate {
+                if self.expression_is_used(counter_kind) {
+                    self.add_expression_operands(counter_kind);
+                } else {
+                    not_validated.push(counter_kind);
                 }
             }
         }
     }
 
     pub fn alert_on_unused_expressions(&self, debug_counters: &DebugCounters) {
-        if let Some(unused_expressions) = self.some_unused_expressions.as_ref() {
-            for (counter_kind, edge_from_bcb, target_bcb) in unused_expressions {
-                let unused_counter_message = if let Some(from_bcb) = edge_from_bcb.as_ref() {
-                    format!(
-                        "non-coverage edge counter found without a dependent expression, in \
+        let Some(state) = &self.state else { return };
+
+        for (counter_kind, edge_from_bcb, target_bcb) in &state.unused_expressions {
+            let unused_counter_message = if let Some(from_bcb) = edge_from_bcb.as_ref() {
+                format!(
+                    "non-coverage edge counter found without a dependent expression, in \
                         {:?}->{:?}; counter={}",
-                        from_bcb,
-                        target_bcb,
-                        debug_counters.format_counter(&counter_kind),
-                    )
-                } else {
-                    format!(
-                        "non-coverage counter found without a dependent expression, in {:?}; \
+                    from_bcb,
+                    target_bcb,
+                    debug_counters.format_counter(&counter_kind),
+                )
+            } else {
+                format!(
+                    "non-coverage counter found without a dependent expression, in {:?}; \
                         counter={}",
-                        target_bcb,
-                        debug_counters.format_counter(&counter_kind),
-                    )
-                };
-
-                if debug_options().allow_unused_expressions {
-                    debug!("WARNING: {}", unused_counter_message);
-                } else {
-                    bug!("{}", unused_counter_message);
-                }
+                    target_bcb,
+                    debug_counters.format_counter(&counter_kind),
+                )
+            };
+
+            if debug_options().allow_unused_expressions {
+                debug!("WARNING: {}", unused_counter_message);
+            } else {
+                bug!("{}", unused_counter_message);
             }
         }
     }
diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs
index aa205655f9d..56365c5d474 100644
--- a/compiler/rustc_mir_transform/src/coverage/query.rs
+++ b/compiler/rustc_mir_transform/src/coverage/query.rs
@@ -1,5 +1,6 @@
 use super::*;
 
+use rustc_data_structures::captures::Captures;
 use rustc_middle::mir::coverage::*;
 use rustc_middle::mir::{self, Body, Coverage, CoverageInfo};
 use rustc_middle::query::Providers;
@@ -12,15 +13,10 @@ pub(crate) fn provide(providers: &mut Providers) {
     providers.covered_code_regions = |tcx, def_id| covered_code_regions(tcx, def_id);
 }
 
-/// The `num_counters` argument to `llvm.instrprof.increment` is the max counter_id + 1, or in
-/// other words, the number of counter value references injected into the MIR (plus 1 for the
-/// reserved `ZERO` counter, which uses counter ID `0` when included in an expression). Injected
-/// counters have a counter ID from `1..num_counters-1`.
-///
-/// `num_expressions` is the number of counter expressions added to the MIR body.
-///
-/// Both `num_counters` and `num_expressions` are used to initialize new vectors, during backend
-/// code generate, to lookup counters and expressions by simple u32 indexes.
+/// Coverage codegen needs to know the total number of counter IDs and expression IDs that have
+/// been used by a function's coverage mappings. These totals are used to create vectors to hold
+/// the relevant counter and expression data, and the maximum counter ID (+ 1) is also needed by
+/// the `llvm.instrprof.increment` intrinsic.
 ///
 /// MIR optimization may split and duplicate some BasicBlock sequences, or optimize out some code
 /// including injected counters. (It is OK if some counters are optimized out, but those counters
@@ -28,71 +24,51 @@ pub(crate) fn provide(providers: &mut Providers) {
 /// calls may not work; but computing the number of counters or expressions by adding `1` to the
 /// highest ID (for a given instrumented function) is valid.
 ///
-/// This visitor runs twice, first with `add_missing_operands` set to `false`, to find the maximum
-/// counter ID and maximum expression ID based on their enum variant `id` fields; then, as a
-/// safeguard, with `add_missing_operands` set to `true`, to find any other counter or expression
-/// IDs referenced by expression operands, if not already seen.
-///
-/// Ideally, each operand ID in a MIR `CoverageKind::Expression` will have a separate MIR `Coverage`
-/// statement for the `Counter` or `Expression` with the referenced ID. but since current or future
-/// MIR optimizations can theoretically optimize out segments of a MIR, it may not be possible to
-/// guarantee this, so the second pass ensures the `CoverageInfo` counts include all referenced IDs.
+/// It's possible for a coverage expression to remain in MIR while one or both of its operands
+/// have been optimized away. To avoid problems in codegen, we include those operands' IDs when
+/// determining the maximum counter/expression ID, even if the underlying counter/expression is
+/// no longer present.
 struct CoverageVisitor {
-    info: CoverageInfo,
-    add_missing_operands: bool,
+    max_counter_id: CounterId,
+    max_expression_id: ExpressionId,
 }
 
 impl CoverageVisitor {
-    /// Updates `num_counters` to the maximum encountered counter ID plus 1.
+    /// Updates `max_counter_id` to the maximum encountered counter ID.
     #[inline(always)]
-    fn update_num_counters(&mut self, counter_id: CounterId) {
-        let counter_id = counter_id.as_u32();
-        self.info.num_counters = std::cmp::max(self.info.num_counters, counter_id + 1);
+    fn update_max_counter_id(&mut self, counter_id: CounterId) {
+        self.max_counter_id = self.max_counter_id.max(counter_id);
     }
 
-    /// Updates `num_expressions` to the maximum encountered expression ID plus 1.
+    /// Updates `max_expression_id` to the maximum encountered expression ID.
     #[inline(always)]
-    fn update_num_expressions(&mut self, expression_id: ExpressionId) {
-        let expression_id = expression_id.as_u32();
-        self.info.num_expressions = std::cmp::max(self.info.num_expressions, expression_id + 1);
+    fn update_max_expression_id(&mut self, expression_id: ExpressionId) {
+        self.max_expression_id = self.max_expression_id.max(expression_id);
     }
 
     fn update_from_expression_operand(&mut self, operand: Operand) {
         match operand {
-            Operand::Counter(id) => self.update_num_counters(id),
-            Operand::Expression(id) => self.update_num_expressions(id),
+            Operand::Counter(id) => self.update_max_counter_id(id),
+            Operand::Expression(id) => self.update_max_expression_id(id),
             Operand::Zero => {}
         }
     }
 
     fn visit_body(&mut self, body: &Body<'_>) {
-        for bb_data in body.basic_blocks.iter() {
-            for statement in bb_data.statements.iter() {
-                if let StatementKind::Coverage(box ref coverage) = statement.kind {
-                    if is_inlined(body, statement) {
-                        continue;
-                    }
-                    self.visit_coverage(coverage);
-                }
-            }
+        for coverage in all_coverage_in_mir_body(body) {
+            self.visit_coverage(coverage);
         }
     }
 
     fn visit_coverage(&mut self, coverage: &Coverage) {
-        if self.add_missing_operands {
-            match coverage.kind {
-                CoverageKind::Expression { lhs, rhs, .. } => {
-                    self.update_from_expression_operand(lhs);
-                    self.update_from_expression_operand(rhs);
-                }
-                _ => {}
-            }
-        } else {
-            match coverage.kind {
-                CoverageKind::Counter { id, .. } => self.update_num_counters(id),
-                CoverageKind::Expression { id, .. } => self.update_num_expressions(id),
-                _ => {}
+        match coverage.kind {
+            CoverageKind::Counter { id, .. } => self.update_max_counter_id(id),
+            CoverageKind::Expression { id, lhs, rhs, .. } => {
+                self.update_max_expression_id(id);
+                self.update_from_expression_operand(lhs);
+                self.update_from_expression_operand(rhs);
             }
+            CoverageKind::Unreachable => {}
         }
     }
 }
@@ -101,37 +77,40 @@ fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, instance_def: ty::InstanceDef<'tcx>) ->
     let mir_body = tcx.instance_mir(instance_def);
 
     let mut coverage_visitor = CoverageVisitor {
-        info: CoverageInfo { num_counters: 0, num_expressions: 0 },
-        add_missing_operands: false,
+        max_counter_id: CounterId::START,
+        max_expression_id: ExpressionId::START,
     };
 
     coverage_visitor.visit_body(mir_body);
 
-    coverage_visitor.add_missing_operands = true;
-    coverage_visitor.visit_body(mir_body);
-
-    coverage_visitor.info
+    // Add 1 to the highest IDs to get the total number of IDs.
+    CoverageInfo {
+        num_counters: (coverage_visitor.max_counter_id + 1).as_u32(),
+        num_expressions: (coverage_visitor.max_expression_id + 1).as_u32(),
+    }
 }
 
 fn covered_code_regions(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<&CodeRegion> {
     let body = mir_body(tcx, def_id);
-    body.basic_blocks
-        .iter()
-        .flat_map(|data| {
-            data.statements.iter().filter_map(|statement| match statement.kind {
-                StatementKind::Coverage(box ref coverage) => {
-                    if is_inlined(body, statement) {
-                        None
-                    } else {
-                        coverage.code_region.as_ref() // may be None
-                    }
-                }
-                _ => None,
-            })
-        })
+    all_coverage_in_mir_body(body)
+        // Not all coverage statements have an attached code region.
+        .filter_map(|coverage| coverage.code_region.as_ref())
         .collect()
 }
 
+fn all_coverage_in_mir_body<'a, 'tcx>(
+    body: &'a Body<'tcx>,
+) -> impl Iterator<Item = &'a Coverage> + Captures<'tcx> {
+    body.basic_blocks.iter().flat_map(|bb_data| &bb_data.statements).filter_map(|statement| {
+        match statement.kind {
+            StatementKind::Coverage(box ref coverage) if !is_inlined(body, statement) => {
+                Some(coverage)
+            }
+            _ => None,
+        }
+    })
+}
+
 fn is_inlined(body: &Body<'_>, statement: &Statement<'_>) -> bool {
     let scope_data = &body.source_scopes[statement.source_info.scope];
     scope_data.inlined.is_some() || scope_data.inlined_parent_scope.is_some()
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
index d4c9163b571..c7c5f17dfec 100644
--- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -3,17 +3,19 @@
 //! Currently, this pass only propagates scalar values.
 
 use rustc_const_eval::const_eval::CheckAlignment;
-use rustc_const_eval::interpret::{ConstValue, ImmTy, Immediate, InterpCx, Scalar};
+use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def::DefKind;
-use rustc_middle::mir::visit::{MutVisitor, Visitor};
+use rustc_middle::mir::interpret::{AllocId, ConstAllocation, ConstValue, InterpResult, Scalar};
+use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
 use rustc_middle::mir::*;
 use rustc_middle::ty::layout::TyAndLayout;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_mir_dataflow::value_analysis::{
-    Map, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace,
+    Map, PlaceIndex, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace,
 };
 use rustc_mir_dataflow::{lattice::FlatSet, Analysis, Results, ResultsVisitor};
+use rustc_span::def_id::DefId;
 use rustc_span::DUMMY_SP;
 use rustc_target::abi::{Align, FieldIdx, VariantIdx};
 
@@ -50,7 +52,7 @@ impl<'tcx> MirPass<'tcx> for DataflowConstProp {
         let place_limit = if tcx.sess.mir_opt_level() < 4 { Some(PLACE_LIMIT) } else { None };
 
         // Decide which places to track during the analysis.
-        let map = Map::from_filter(tcx, body, Ty::is_scalar, place_limit);
+        let map = Map::new(tcx, body, place_limit);
 
         // Perform the actual dataflow analysis.
         let analysis = ConstAnalysis::new(tcx, body, map);
@@ -58,9 +60,10 @@ impl<'tcx> MirPass<'tcx> for DataflowConstProp {
             .in_scope(|| analysis.wrap().into_engine(tcx, body).iterate_to_fixpoint());
 
         // Collect results and patch the body afterwards.
-        let mut visitor = CollectAndPatch::new(tcx);
+        let mut visitor = Collector::new(tcx, &body.local_decls);
         debug_span!("collect").in_scope(|| results.visit_reachable_with(body, &mut visitor));
-        debug_span!("patch").in_scope(|| visitor.visit_body(body));
+        let mut patch = visitor.patch;
+        debug_span!("patch").in_scope(|| patch.visit_body_preserves_cfg(body));
     }
 }
 
@@ -73,7 +76,7 @@ struct ConstAnalysis<'a, 'tcx> {
 }
 
 impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
-    type Value = FlatSet<ScalarTy<'tcx>>;
+    type Value = FlatSet<Scalar>;
 
     const NAME: &'static str = "ConstAnalysis";
 
@@ -107,6 +110,18 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
         state: &mut State<Self::Value>,
     ) {
         match rvalue {
+            Rvalue::Use(operand) => {
+                state.flood(target.as_ref(), self.map());
+                if let Some(target) = self.map.find(target.as_ref()) {
+                    self.assign_operand(state, target, operand);
+                }
+            }
+            Rvalue::CopyForDeref(rhs) => {
+                state.flood(target.as_ref(), self.map());
+                if let Some(target) = self.map.find(target.as_ref()) {
+                    self.assign_operand(state, target, &Operand::Copy(*rhs));
+                }
+            }
             Rvalue::Aggregate(kind, operands) => {
                 // If we assign `target = Enum::Variant#0(operand)`,
                 // we must make sure that all `target as Variant#i` are `Top`.
@@ -134,8 +149,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
                             variant_target_idx,
                             TrackElem::Field(FieldIdx::from_usize(field_index)),
                         ) {
-                            let result = self.handle_operand(operand, state);
-                            state.insert_idx(field, result, self.map());
+                            self.assign_operand(state, field, operand);
                         }
                     }
                 }
@@ -172,9 +186,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
                     if let Some(overflow_target) = overflow_target {
                         let overflow = match overflow {
                             FlatSet::Top => FlatSet::Top,
-                            FlatSet::Elem(overflow) => {
-                                self.wrap_scalar(Scalar::from_bool(overflow), self.tcx.types.bool)
-                            }
+                            FlatSet::Elem(overflow) => FlatSet::Elem(Scalar::from_bool(overflow)),
                             FlatSet::Bottom => FlatSet::Bottom,
                         };
                         // We have flooded `target` earlier.
@@ -182,6 +194,23 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
                     }
                 }
             }
+            Rvalue::Cast(
+                CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize),
+                operand,
+                _,
+            ) => {
+                let pointer = self.handle_operand(operand, state);
+                state.assign(target.as_ref(), pointer, self.map());
+
+                if let Some(target_len) = self.map().find_len(target.as_ref())
+                    && let operand_ty = operand.ty(self.local_decls, self.tcx)
+                    && let Some(operand_ty) = operand_ty.builtin_deref(true)
+                    && let ty::Array(_, len) = operand_ty.ty.kind()
+                    && let Some(len) = ConstantKind::Ty(*len).try_eval_scalar_int(self.tcx, self.param_env)
+                {
+                    state.insert_value_idx(target_len, FlatSet::Elem(len.into()), self.map());
+                }
+            }
             _ => self.super_assign(target, rvalue, state),
         }
     }
@@ -191,47 +220,77 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
         rvalue: &Rvalue<'tcx>,
         state: &mut State<Self::Value>,
     ) -> ValueOrPlace<Self::Value> {
-        match rvalue {
-            Rvalue::Cast(
-                kind @ (CastKind::IntToInt
-                | CastKind::FloatToInt
-                | CastKind::FloatToFloat
-                | CastKind::IntToFloat),
-                operand,
-                ty,
-            ) => match self.eval_operand(operand, state) {
-                FlatSet::Elem(op) => match kind {
-                    CastKind::IntToInt | CastKind::IntToFloat => {
-                        self.ecx.int_to_int_or_float(&op, *ty)
-                    }
-                    CastKind::FloatToInt | CastKind::FloatToFloat => {
-                        self.ecx.float_to_float_or_int(&op, *ty)
-                    }
-                    _ => unreachable!(),
+        let val = match rvalue {
+            Rvalue::Len(place) => {
+                let place_ty = place.ty(self.local_decls, self.tcx);
+                if let ty::Array(_, len) = place_ty.ty.kind() {
+                    ConstantKind::Ty(*len)
+                        .try_eval_scalar(self.tcx, self.param_env)
+                        .map_or(FlatSet::Top, FlatSet::Elem)
+                } else if let [ProjectionElem::Deref] = place.projection[..] {
+                    state.get_len(place.local.into(), self.map())
+                } else {
+                    FlatSet::Top
                 }
-                .map(|result| ValueOrPlace::Value(self.wrap_immediate(result, *ty)))
-                .unwrap_or(ValueOrPlace::TOP),
-                _ => ValueOrPlace::TOP,
-            },
+            }
+            Rvalue::Cast(CastKind::IntToInt | CastKind::IntToFloat, operand, ty) => {
+                match self.eval_operand(operand, state) {
+                    FlatSet::Elem(op) => self
+                        .ecx
+                        .int_to_int_or_float(&op, *ty)
+                        .map_or(FlatSet::Top, |result| self.wrap_immediate(result)),
+                    FlatSet::Bottom => FlatSet::Bottom,
+                    FlatSet::Top => FlatSet::Top,
+                }
+            }
+            Rvalue::Cast(CastKind::FloatToInt | CastKind::FloatToFloat, operand, ty) => {
+                match self.eval_operand(operand, state) {
+                    FlatSet::Elem(op) => self
+                        .ecx
+                        .float_to_float_or_int(&op, *ty)
+                        .map_or(FlatSet::Top, |result| self.wrap_immediate(result)),
+                    FlatSet::Bottom => FlatSet::Bottom,
+                    FlatSet::Top => FlatSet::Top,
+                }
+            }
+            Rvalue::Cast(CastKind::Transmute, operand, _) => {
+                match self.eval_operand(operand, state) {
+                    FlatSet::Elem(op) => self.wrap_immediate(*op),
+                    FlatSet::Bottom => FlatSet::Bottom,
+                    FlatSet::Top => FlatSet::Top,
+                }
+            }
             Rvalue::BinaryOp(op, box (left, right)) => {
                 // Overflows must be ignored here.
                 let (val, _overflow) = self.binary_op(state, *op, left, right);
-                ValueOrPlace::Value(val)
+                val
             }
             Rvalue::UnaryOp(op, operand) => match self.eval_operand(operand, state) {
                 FlatSet::Elem(value) => self
                     .ecx
                     .unary_op(*op, &value)
-                    .map(|val| ValueOrPlace::Value(self.wrap_immty(val)))
-                    .unwrap_or(ValueOrPlace::Value(FlatSet::Top)),
-                FlatSet::Bottom => ValueOrPlace::Value(FlatSet::Bottom),
-                FlatSet::Top => ValueOrPlace::Value(FlatSet::Top),
+                    .map_or(FlatSet::Top, |val| self.wrap_immediate(*val)),
+                FlatSet::Bottom => FlatSet::Bottom,
+                FlatSet::Top => FlatSet::Top,
             },
-            Rvalue::Discriminant(place) => {
-                ValueOrPlace::Value(state.get_discr(place.as_ref(), self.map()))
+            Rvalue::NullaryOp(null_op, ty) => {
+                let Ok(layout) = self.tcx.layout_of(self.param_env.and(*ty)) else {
+                    return ValueOrPlace::Value(FlatSet::Top);
+                };
+                let val = match null_op {
+                    NullOp::SizeOf if layout.is_sized() => layout.size.bytes(),
+                    NullOp::AlignOf if layout.is_sized() => layout.align.abi.bytes(),
+                    NullOp::OffsetOf(fields) => layout
+                        .offset_of_subfield(&self.ecx, fields.iter().map(|f| f.index()))
+                        .bytes(),
+                    _ => return ValueOrPlace::Value(FlatSet::Top),
+                };
+                FlatSet::Elem(Scalar::from_target_usize(val, &self.tcx))
             }
-            _ => self.super_rvalue(rvalue, state),
-        }
+            Rvalue::Discriminant(place) => state.get_discr(place.as_ref(), self.map()),
+            _ => return self.super_rvalue(rvalue, state),
+        };
+        ValueOrPlace::Value(val)
     }
 
     fn handle_constant(
@@ -241,10 +300,8 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
     ) -> Self::Value {
         constant
             .literal
-            .eval(self.tcx, self.param_env)
-            .try_to_scalar()
-            .map(|value| FlatSet::Elem(ScalarTy(value, constant.ty())))
-            .unwrap_or(FlatSet::Top)
+            .try_eval_scalar(self.tcx, self.param_env)
+            .map_or(FlatSet::Top, FlatSet::Elem)
     }
 
     fn handle_switch_int<'mir>(
@@ -261,9 +318,8 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
             // We are branching on uninitialized data, this is UB, treat it as unreachable.
             // This allows the set of visited edges to grow monotonically with the lattice.
             FlatSet::Bottom => TerminatorEdges::None,
-            FlatSet::Elem(ScalarTy(scalar, _)) => {
-                let int = scalar.assert_int();
-                let choice = int.assert_bits(int.size());
+            FlatSet::Elem(scalar) => {
+                let choice = scalar.assert_bits(scalar.size());
                 TerminatorEdges::Single(targets.target_for_value(choice))
             }
             FlatSet::Top => TerminatorEdges::SwitchInt { discr, targets },
@@ -271,16 +327,6 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
     }
 }
 
-#[derive(Clone, PartialEq, Eq)]
-struct ScalarTy<'tcx>(Scalar, Ty<'tcx>);
-
-impl<'tcx> std::fmt::Debug for ScalarTy<'tcx> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        // This is used for dataflow visualization, so we return something more concise.
-        std::fmt::Display::fmt(&ConstantKind::Val(ConstValue::Scalar(self.0), self.1), f)
-    }
-}
-
 impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
     pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, map: Map) -> Self {
         let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
@@ -293,34 +339,144 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
         }
     }
 
+    /// The caller must have flooded `place`.
+    fn assign_operand(
+        &self,
+        state: &mut State<FlatSet<Scalar>>,
+        place: PlaceIndex,
+        operand: &Operand<'tcx>,
+    ) {
+        match operand {
+            Operand::Copy(rhs) | Operand::Move(rhs) => {
+                if let Some(rhs) = self.map.find(rhs.as_ref()) {
+                    state.insert_place_idx(place, rhs, &self.map);
+                } else if rhs.projection.first() == Some(&PlaceElem::Deref)
+                    && let FlatSet::Elem(pointer) = state.get(rhs.local.into(), &self.map)
+                    && let rhs_ty = self.local_decls[rhs.local].ty
+                    && let Ok(rhs_layout) = self.tcx.layout_of(self.param_env.and(rhs_ty))
+                {
+                    let op = ImmTy::from_scalar(pointer, rhs_layout).into();
+                    self.assign_constant(state, place, op, &rhs.projection);
+                }
+            }
+            Operand::Constant(box constant) => {
+                if let Ok(constant) = self.ecx.eval_mir_constant(&constant.literal, None, None) {
+                    self.assign_constant(state, place, constant, &[]);
+                }
+            }
+        }
+    }
+
+    /// The caller must have flooded `place`.
+    ///
+    /// Perform: `place = operand.projection`.
+    #[instrument(level = "trace", skip(self, state))]
+    fn assign_constant(
+        &self,
+        state: &mut State<FlatSet<Scalar>>,
+        place: PlaceIndex,
+        mut operand: OpTy<'tcx>,
+        projection: &[PlaceElem<'tcx>],
+    ) -> Option<!> {
+        for &(mut proj_elem) in projection {
+            if let PlaceElem::Index(index) = proj_elem {
+                if let FlatSet::Elem(index) = state.get(index.into(), &self.map)
+                    && let Ok(offset) = index.to_target_usize(&self.tcx)
+                    && let Some(min_length) = offset.checked_add(1)
+                {
+                    proj_elem = PlaceElem::ConstantIndex { offset, min_length, from_end: false };
+                } else {
+                    return None;
+                }
+            }
+            operand = self.ecx.project(&operand, proj_elem).ok()?;
+        }
+
+        self.map.for_each_projection_value(
+            place,
+            operand,
+            &mut |elem, op| match elem {
+                TrackElem::Field(idx) => self.ecx.project_field(op, idx.as_usize()).ok(),
+                TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).ok(),
+                TrackElem::Discriminant => {
+                    let variant = self.ecx.read_discriminant(op).ok()?;
+                    let discr_value = self.ecx.discriminant_for_variant(op.layout, variant).ok()?;
+                    Some(discr_value.into())
+                }
+                TrackElem::DerefLen => {
+                    let op: OpTy<'_> = self.ecx.deref_pointer(op).ok()?.into();
+                    let len_usize = op.len(&self.ecx).ok()?;
+                    let layout =
+                        self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).unwrap();
+                    Some(ImmTy::from_uint(len_usize, layout).into())
+                }
+            },
+            &mut |place, op| {
+                if let Ok(imm) = self.ecx.read_immediate_raw(op)
+                    && let Some(imm) = imm.right()
+                {
+                    let elem = self.wrap_immediate(*imm);
+                    state.insert_value_idx(place, elem, &self.map);
+                }
+            },
+        );
+
+        None
+    }
+
     fn binary_op(
         &self,
-        state: &mut State<FlatSet<ScalarTy<'tcx>>>,
+        state: &mut State<FlatSet<Scalar>>,
         op: BinOp,
         left: &Operand<'tcx>,
         right: &Operand<'tcx>,
-    ) -> (FlatSet<ScalarTy<'tcx>>, FlatSet<bool>) {
+    ) -> (FlatSet<Scalar>, FlatSet<bool>) {
         let left = self.eval_operand(left, state);
         let right = self.eval_operand(right, state);
+
         match (left, right) {
+            (FlatSet::Bottom, _) | (_, FlatSet::Bottom) => (FlatSet::Bottom, FlatSet::Bottom),
+            // Both sides are known, do the actual computation.
             (FlatSet::Elem(left), FlatSet::Elem(right)) => {
                 match self.ecx.overflowing_binary_op(op, &left, &right) {
-                    Ok((val, overflow, ty)) => (self.wrap_scalar(val, ty), FlatSet::Elem(overflow)),
+                    Ok((val, overflow, _)) => (FlatSet::Elem(val), FlatSet::Elem(overflow)),
                     _ => (FlatSet::Top, FlatSet::Top),
                 }
             }
-            (FlatSet::Bottom, _) | (_, FlatSet::Bottom) => (FlatSet::Bottom, FlatSet::Bottom),
-            (_, _) => {
-                // Could attempt some algebraic simplifications here.
-                (FlatSet::Top, FlatSet::Top)
+            // Exactly one side is known, attempt some algebraic simplifications.
+            (FlatSet::Elem(const_arg), _) | (_, FlatSet::Elem(const_arg)) => {
+                let layout = const_arg.layout;
+                if !matches!(layout.abi, rustc_target::abi::Abi::Scalar(..)) {
+                    return (FlatSet::Top, FlatSet::Top);
+                }
+
+                let arg_scalar = const_arg.to_scalar();
+                let Ok(arg_value) = arg_scalar.to_bits(layout.size) else {
+                    return (FlatSet::Top, FlatSet::Top);
+                };
+
+                match op {
+                    BinOp::BitAnd if arg_value == 0 => (FlatSet::Elem(arg_scalar), FlatSet::Bottom),
+                    BinOp::BitOr
+                        if arg_value == layout.size.truncate(u128::MAX)
+                            || (layout.ty.is_bool() && arg_value == 1) =>
+                    {
+                        (FlatSet::Elem(arg_scalar), FlatSet::Bottom)
+                    }
+                    BinOp::Mul if layout.ty.is_integral() && arg_value == 0 => {
+                        (FlatSet::Elem(arg_scalar), FlatSet::Elem(false))
+                    }
+                    _ => (FlatSet::Top, FlatSet::Top),
+                }
             }
+            (FlatSet::Top, FlatSet::Top) => (FlatSet::Top, FlatSet::Top),
         }
     }
 
     fn eval_operand(
         &self,
         op: &Operand<'tcx>,
-        state: &mut State<FlatSet<ScalarTy<'tcx>>>,
+        state: &mut State<FlatSet<Scalar>>,
     ) -> FlatSet<ImmTy<'tcx>> {
         let value = match self.handle_operand(op, state) {
             ValueOrPlace::Value(value) => value,
@@ -328,76 +484,85 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
         };
         match value {
             FlatSet::Top => FlatSet::Top,
-            FlatSet::Elem(ScalarTy(scalar, ty)) => self
-                .tcx
-                .layout_of(self.param_env.and(ty))
-                .map(|layout| FlatSet::Elem(ImmTy::from_scalar(scalar, layout)))
-                .unwrap_or(FlatSet::Top),
+            FlatSet::Elem(scalar) => {
+                let ty = op.ty(self.local_decls, self.tcx);
+                self.tcx.layout_of(self.param_env.and(ty)).map_or(FlatSet::Top, |layout| {
+                    FlatSet::Elem(ImmTy::from_scalar(scalar.into(), layout))
+                })
+            }
             FlatSet::Bottom => FlatSet::Bottom,
         }
     }
 
-    fn eval_discriminant(
-        &self,
-        enum_ty: Ty<'tcx>,
-        variant_index: VariantIdx,
-    ) -> Option<ScalarTy<'tcx>> {
+    fn eval_discriminant(&self, enum_ty: Ty<'tcx>, variant_index: VariantIdx) -> Option<Scalar> {
         if !enum_ty.is_enum() {
             return None;
         }
-        let discr = enum_ty.discriminant_for_variant(self.tcx, variant_index)?;
-        let discr_layout = self.tcx.layout_of(self.param_env.and(discr.ty)).ok()?;
-        let discr_value = Scalar::try_from_uint(discr.val, discr_layout.size)?;
-        Some(ScalarTy(discr_value, discr.ty))
+        let enum_ty_layout = self.tcx.layout_of(self.param_env.and(enum_ty)).ok()?;
+        let discr_value = self.ecx.discriminant_for_variant(enum_ty_layout, variant_index).ok()?;
+        Some(discr_value.to_scalar())
     }
 
-    fn wrap_scalar(&self, scalar: Scalar, ty: Ty<'tcx>) -> FlatSet<ScalarTy<'tcx>> {
-        FlatSet::Elem(ScalarTy(scalar, ty))
-    }
-
-    fn wrap_immediate(&self, imm: Immediate, ty: Ty<'tcx>) -> FlatSet<ScalarTy<'tcx>> {
+    fn wrap_immediate(&self, imm: Immediate) -> FlatSet<Scalar> {
         match imm {
-            Immediate::Scalar(scalar) => self.wrap_scalar(scalar, ty),
+            Immediate::Scalar(scalar) => FlatSet::Elem(scalar),
+            Immediate::Uninit => FlatSet::Bottom,
             _ => FlatSet::Top,
         }
     }
-
-    fn wrap_immty(&self, val: ImmTy<'tcx>) -> FlatSet<ScalarTy<'tcx>> {
-        self.wrap_immediate(*val, val.layout.ty)
-    }
 }
 
-struct CollectAndPatch<'tcx> {
+pub(crate) struct Patch<'tcx> {
     tcx: TyCtxt<'tcx>,
 
     /// For a given MIR location, this stores the values of the operands used by that location. In
     /// particular, this is before the effect, such that the operands of `_1 = _1 + _2` are
     /// properly captured. (This may become UB soon, but it is currently emitted even by safe code.)
-    before_effect: FxHashMap<(Location, Place<'tcx>), ScalarTy<'tcx>>,
+    pub(crate) before_effect: FxHashMap<(Location, Place<'tcx>), ConstantKind<'tcx>>,
 
     /// Stores the assigned values for assignments where the Rvalue is constant.
-    assignments: FxHashMap<Location, ScalarTy<'tcx>>,
+    pub(crate) assignments: FxHashMap<Location, ConstantKind<'tcx>>,
 }
 
-impl<'tcx> CollectAndPatch<'tcx> {
-    fn new(tcx: TyCtxt<'tcx>) -> Self {
+impl<'tcx> Patch<'tcx> {
+    pub(crate) fn new(tcx: TyCtxt<'tcx>) -> Self {
         Self { tcx, before_effect: FxHashMap::default(), assignments: FxHashMap::default() }
     }
 
-    fn make_operand(&self, scalar: ScalarTy<'tcx>) -> Operand<'tcx> {
-        Operand::Constant(Box::new(Constant {
-            span: DUMMY_SP,
-            user_ty: None,
-            literal: ConstantKind::Val(ConstValue::Scalar(scalar.0), scalar.1),
-        }))
+    fn make_operand(&self, literal: ConstantKind<'tcx>) -> Operand<'tcx> {
+        Operand::Constant(Box::new(Constant { span: DUMMY_SP, user_ty: None, literal }))
+    }
+}
+
+struct Collector<'tcx, 'locals> {
+    patch: Patch<'tcx>,
+    local_decls: &'locals LocalDecls<'tcx>,
+}
+
+impl<'tcx, 'locals> Collector<'tcx, 'locals> {
+    pub(crate) fn new(tcx: TyCtxt<'tcx>, local_decls: &'locals LocalDecls<'tcx>) -> Self {
+        Self { patch: Patch::new(tcx), local_decls }
+    }
+
+    fn try_make_constant(
+        &self,
+        place: Place<'tcx>,
+        state: &State<FlatSet<Scalar>>,
+        map: &Map,
+    ) -> Option<ConstantKind<'tcx>> {
+        let FlatSet::Elem(Scalar::Int(value)) = state.get(place.as_ref(), &map) else {
+            return None;
+        };
+        let ty = place.ty(self.local_decls, self.patch.tcx).ty;
+        Some(ConstantKind::Val(ConstValue::Scalar(value.into()), ty))
     }
 }
 
 impl<'mir, 'tcx>
     ResultsVisitor<'mir, 'tcx, Results<'tcx, ValueAnalysisWrapper<ConstAnalysis<'_, 'tcx>>>>
-    for CollectAndPatch<'tcx>
+    for Collector<'tcx, '_>
 {
-    type FlowState = State<FlatSet<ScalarTy<'tcx>>>;
+    type FlowState = State<FlatSet<Scalar>>;
 
     fn visit_statement_before_primary_effect(
         &mut self,
@@ -427,14 +592,8 @@ impl<'mir, 'tcx>
                 // Don't overwrite the assignment if it already uses a constant (to keep the span).
             }
             StatementKind::Assign(box (place, _)) => {
-                match state.get(place.as_ref(), &results.analysis.0.map) {
-                    FlatSet::Top => (),
-                    FlatSet::Elem(value) => {
-                        self.assignments.insert(location, value);
-                    }
-                    FlatSet::Bottom => {
-                        // This assignment is either unreachable, or an uninitialized value is assigned.
-                    }
+                if let Some(value) = self.try_make_constant(place, state, &results.analysis.0.map) {
+                    self.patch.assignments.insert(location, value);
                 }
             }
             _ => (),
@@ -453,8 +612,8 @@ impl<'mir, 'tcx>
     }
 }
 
-impl<'tcx> MutVisitor<'tcx> for CollectAndPatch<'tcx> {
-    fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+impl<'tcx> MutVisitor<'tcx> for Patch<'tcx> {
+    fn tcx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
 
@@ -462,7 +621,7 @@ impl<'tcx> MutVisitor<'tcx> for CollectAndPatch<'tcx> {
         if let Some(value) = self.assignments.get(&location) {
             match &mut statement.kind {
                 StatementKind::Assign(box (_, rvalue)) => {
-                    *rvalue = Rvalue::Use(self.make_operand(value.clone()));
+                    *rvalue = Rvalue::Use(self.make_operand(*value));
                 }
                 _ => bug!("found assignment info for non-assign statement"),
             }
@@ -475,33 +634,61 @@ impl<'tcx> MutVisitor<'tcx> for CollectAndPatch<'tcx> {
         match operand {
             Operand::Copy(place) | Operand::Move(place) => {
                 if let Some(value) = self.before_effect.get(&(location, *place)) {
-                    *operand = self.make_operand(value.clone());
+                    *operand = self.make_operand(*value);
+                } else if !place.projection.is_empty() {
+                    self.super_operand(operand, location)
                 }
             }
-            _ => (),
+            Operand::Constant(_) => {}
+        }
+    }
+
+    fn process_projection_elem(
+        &mut self,
+        elem: PlaceElem<'tcx>,
+        location: Location,
+    ) -> Option<PlaceElem<'tcx>> {
+        if let PlaceElem::Index(local) = elem {
+            let offset = self.before_effect.get(&(location, local.into()))?;
+            let offset = offset.try_to_scalar()?;
+            let offset = offset.to_target_usize(&self.tcx).ok()?;
+            let min_length = offset.checked_add(1)?;
+            Some(PlaceElem::ConstantIndex { offset, min_length, from_end: false })
+        } else {
+            None
         }
     }
 }
 
-struct OperandCollector<'tcx, 'map, 'a> {
-    state: &'a State<FlatSet<ScalarTy<'tcx>>>,
-    visitor: &'a mut CollectAndPatch<'tcx>,
+struct OperandCollector<'tcx, 'map, 'locals, 'a> {
+    state: &'a State<FlatSet<Scalar>>,
+    visitor: &'a mut Collector<'tcx, 'locals>,
     map: &'map Map,
 }
 
-impl<'tcx, 'map, 'a> Visitor<'tcx> for OperandCollector<'tcx, 'map, 'a> {
+impl<'tcx> Visitor<'tcx> for OperandCollector<'tcx, '_, '_, '_> {
+    fn visit_projection_elem(
+        &mut self,
+        _: PlaceRef<'tcx>,
+        elem: PlaceElem<'tcx>,
+        _: PlaceContext,
+        location: Location,
+    ) {
+        if let PlaceElem::Index(local) = elem
+            && let Some(value) = self.visitor.try_make_constant(local.into(), self.state, self.map)
+        {
+            self.visitor.patch.before_effect.insert((location, local.into()), value);
+        }
+    }
+
     fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
-        match operand {
-            Operand::Copy(place) | Operand::Move(place) => {
-                match self.state.get(place.as_ref(), self.map) {
-                    FlatSet::Top => (),
-                    FlatSet::Elem(value) => {
-                        self.visitor.before_effect.insert((location, *place), value);
-                    }
-                    FlatSet::Bottom => (),
-                }
+        if let Some(place) = operand.place() {
+            if let Some(value) = self.visitor.try_make_constant(place, self.state, self.map) {
+                self.visitor.patch.before_effect.insert((location, place), value);
+            } else if !place.projection.is_empty() {
+                // Try to propagate into `Index` projections.
+                self.super_operand(operand, location)
             }
-            _ => (),
         }
     }
 }
@@ -513,8 +700,11 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm
     type MemoryKind = !;
     const PANIC_ON_ALLOC_FAIL: bool = true;
 
+    #[inline(always)]
     fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment {
-        unimplemented!()
+        // We do not check for alignment to avoid having to carry an `Align`
+        // in `ConstValue::ByRef`.
+        CheckAlignment::No
     }
 
     fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool {
@@ -529,6 +719,27 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm
         unimplemented!()
     }
 
+    fn before_access_global(
+        _tcx: TyCtxt<'tcx>,
+        _machine: &Self,
+        _alloc_id: AllocId,
+        alloc: ConstAllocation<'tcx>,
+        _static_def_id: Option<DefId>,
+        is_write: bool,
+    ) -> InterpResult<'tcx> {
+        if is_write {
+            crate::const_prop::throw_machine_stop_str!("can't write to global");
+        }
+
+        // If the static allocation is mutable, then we can't const prop it as its content
+        // might be different at runtime.
+        if alloc.inner().mutability.is_mut() {
+            crate::const_prop::throw_machine_stop_str!("can't access mutable globals in ConstProp");
+        }
+
+        Ok(())
+    }
+
     fn find_mir_or_eval_fn(
         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
         _instance: ty::Instance<'tcx>,
@@ -572,7 +783,7 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm
         _bin_op: BinOp,
         _left: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>,
         _right: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>,
-    ) -> interpret::InterpResult<'tcx, (interpret::Scalar<Self::Provenance>, bool, Ty<'tcx>)> {
+    ) -> interpret::InterpResult<'tcx, (Scalar<Self::Provenance>, bool, Ty<'tcx>)> {
         throw_unsup!(Unsupported("".into()))
     }
 
@@ -597,7 +808,8 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm
         _ecx: &'a InterpCx<'mir, 'tcx, Self>,
     ) -> &'a [rustc_const_eval::interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>]
     {
-        unimplemented!()
+        // Return an empty stack instead of panicking, as `cur_span` uses it to evaluate constants.
+        &[]
     }
 
     fn stack_mut<'a>(
diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs
index 4b796d79ef6..35373bcaa41 100644
--- a/compiler/rustc_mir_transform/src/errors.rs
+++ b/compiler/rustc_mir_transform/src/errors.rs
@@ -258,10 +258,3 @@ pub(crate) struct MustNotSuspendReason {
     pub span: Span,
     pub reason: String,
 }
-
-#[derive(Diagnostic)]
-#[diag(mir_transform_simd_shuffle_last_const)]
-pub(crate) struct SimdShuffleLastConst {
-    #[primary_span]
-    pub span: Span,
-}
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 7d4c4a823a8..4dc7c3b6444 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -390,7 +390,12 @@ impl<'tcx> Inliner<'tcx> {
 
         // Reachability pass defines which functions are eligible for inlining. Generally inlining
         // other functions is incorrect because they could reference symbols that aren't exported.
-        let is_generic = callsite.callee.args.non_erasable_generics().next().is_some();
+        let is_generic = callsite
+            .callee
+            .args
+            .non_erasable_generics(self.tcx, callsite.callee.def_id())
+            .next()
+            .is_some();
         if !is_generic && !callee_attrs.requests_inline() {
             return Err("not exported");
         }
diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs
index 8b0a0903d18..6e191b285be 100644
--- a/compiler/rustc_mir_transform/src/instsimplify.rs
+++ b/compiler/rustc_mir_transform/src/instsimplify.rs
@@ -136,7 +136,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> {
                     return;
                 }
 
-                let literal = ConstantKind::from_const(len, self.tcx);
+                let literal = ConstantKind::from_ty_const(len, self.tcx);
                 let constant = Constant { span: source_info.span, literal, user_ty: None };
                 *rvalue = Rvalue::Use(Operand::Constant(Box::new(constant)));
             }
diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs
index 19108dabdf4..8afbe418502 100644
--- a/compiler/rustc_mir_transform/src/large_enums.rs
+++ b/compiler/rustc_mir_transform/src/large_enums.rs
@@ -114,7 +114,7 @@ impl EnumSizeOpt {
             tcx.data_layout.ptr_sized_integer().align(&tcx.data_layout).abi,
             Mutability::Not,
         );
-        let alloc = tcx.create_memory_alloc(tcx.mk_const_alloc(alloc));
+        let alloc = tcx.reserve_and_set_memory_alloc(tcx.mk_const_alloc(alloc));
         Some((*adt_def, num_discrs, *alloc_cache.entry(ty).or_insert(alloc)))
     }
     fn optim<'tcx>(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
@@ -139,7 +139,6 @@ impl EnumSizeOpt {
 
                     let (adt_def, num_variants, alloc_id) =
                         self.candidate(tcx, param_env, ty, &mut alloc_cache)?;
-                    let alloc = tcx.global_alloc(alloc_id).unwrap_memory();
 
                     let tmp_ty = Ty::new_array(tcx, tcx.types.usize, num_variants as u64);
 
@@ -154,7 +153,7 @@ impl EnumSizeOpt {
                         span,
                         user_ty: None,
                         literal: ConstantKind::Val(
-                            interpret::ConstValue::ByRef { alloc, offset: Size::ZERO },
+                            interpret::ConstValue::Indirect { alloc_id, offset: Size::ZERO },
                             tmp_ty,
                         ),
                     };
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index bf798adee19..70d4ea74d1a 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -2,6 +2,7 @@
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
 #![feature(box_patterns)]
+#![feature(decl_macro)]
 #![feature(is_sorted)]
 #![feature(let_chains)]
 #![feature(map_try_insert)]
@@ -605,6 +606,11 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> {
     let body = tcx.mir_drops_elaborated_and_const_checked(did).steal();
     let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::NotConst);
     debug!("body: {:#?}", body);
+
+    if body.tainted_by_errors.is_some() {
+        return body;
+    }
+
     run_optimization_passes(tcx, &mut body);
 
     body
diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
index a8dc91ca439..13277d62bf4 100644
--- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs
+++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
@@ -1,11 +1,10 @@
 //! Lowers intrinsic calls
 
-use crate::{errors, MirPass};
+use crate::MirPass;
 use rustc_middle::mir::*;
 use rustc_middle::ty::GenericArgsRef;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::symbol::{sym, Symbol};
-use rustc_span::Span;
 use rustc_target::abi::{FieldIdx, VariantIdx};
 
 pub struct LowerIntrinsics;
@@ -304,9 +303,6 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
                             terminator.kind = TerminatorKind::Unreachable;
                         }
                     }
-                    sym::simd_shuffle => {
-                        validate_simd_shuffle(tcx, args, terminator.source_info.span);
-                    }
                     _ => {}
                 }
             }
@@ -325,9 +321,3 @@ fn resolve_rust_intrinsic<'tcx>(
     }
     None
 }
-
-fn validate_simd_shuffle<'tcx>(tcx: TyCtxt<'tcx>, args: &[Operand<'tcx>], span: Span) {
-    if !matches!(args[2], Operand::Constant(_)) {
-        tcx.sess.emit_err(errors::SimdShuffleLastConst { span });
-    }
-}
diff --git a/compiler/rustc_mir_transform/src/normalize_array_len.rs b/compiler/rustc_mir_transform/src/normalize_array_len.rs
index 6c3b7c58fab..1b846987d38 100644
--- a/compiler/rustc_mir_transform/src/normalize_array_len.rs
+++ b/compiler/rustc_mir_transform/src/normalize_array_len.rs
@@ -93,7 +93,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> {
             *rvalue = Rvalue::Use(Operand::Constant(Box::new(Constant {
                 span: rustc_span::DUMMY_SP,
                 user_ty: None,
-                literal: ConstantKind::from_const(len, self.tcx),
+                literal: ConstantKind::from_ty_const(len, self.tcx),
             })));
         }
         self.super_rvalue(rvalue, loc);
diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs
index 057f5fe8293..5abb2f3d041 100644
--- a/compiler/rustc_mir_transform/src/pass_manager.rs
+++ b/compiler/rustc_mir_transform/src/pass_manager.rs
@@ -94,6 +94,8 @@ fn run_passes_inner<'tcx>(
     let overridden_passes = &tcx.sess.opts.unstable_opts.mir_enable_passes;
     trace!(?overridden_passes);
 
+    let prof_arg = tcx.sess.prof.enabled().then(|| format!("{:?}", body.source.def_id()));
+
     if !body.should_skip() {
         for pass in passes {
             let name = pass.name();
@@ -121,7 +123,14 @@ fn run_passes_inner<'tcx>(
                 validate_body(tcx, body, format!("before pass {name}"));
             }
 
-            tcx.sess.time(name, || pass.run_pass(tcx, body));
+            if let Some(prof_arg) = &prof_arg {
+                tcx.sess
+                    .prof
+                    .generic_activity_with_arg(pass.profiler_name(), &**prof_arg)
+                    .run(|| pass.run_pass(tcx, body));
+            } else {
+                pass.run_pass(tcx, body);
+            }
 
             if dump_enabled {
                 dump_mir_for_pass(tcx, body, &name, true);
diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs
index 9c6c55b0811..c13bafa9fbb 100644
--- a/compiler/rustc_mir_transform/src/remove_zsts.rs
+++ b/compiler/rustc_mir_transform/src/remove_zsts.rs
@@ -87,11 +87,6 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
                     var_debug_info.value = VarDebugInfoContents::Const(self.make_zst(place_ty))
                 }
             }
-            VarDebugInfoContents::Composite { ty, fragments: _ } => {
-                if self.known_to_be_zst(ty) {
-                    var_debug_info.value = VarDebugInfoContents::Const(self.make_zst(ty))
-                }
-            }
         }
     }
 
diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs
index e66ae8ff884..c21b1724cbb 100644
--- a/compiler/rustc_mir_transform/src/sroa.rs
+++ b/compiler/rustc_mir_transform/src/sroa.rs
@@ -1,4 +1,5 @@
 use crate::MirPass;
+use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
 use rustc_index::bit_set::{BitSet, GrowableBitSet};
 use rustc_index::IndexVec;
 use rustc_middle::mir::patch::MirPatch;
@@ -147,7 +148,7 @@ fn escaping_locals<'tcx>(
         }
 
         // We ignore anything that happens in debuginfo, since we expand it using
-        // `VarDebugInfoContents::Composite`.
+        // `VarDebugInfoFragment`.
         fn visit_var_debug_info(&mut self, _: &VarDebugInfo<'tcx>) {}
     }
 }
@@ -246,9 +247,7 @@ fn replace_flattened_locals<'tcx>(
     for (index, annotation) in body.user_type_annotations.iter_enumerated_mut() {
         visitor.visit_user_type_annotation(index, annotation);
     }
-    for var_debug_info in &mut body.var_debug_info {
-        visitor.visit_var_debug_info(var_debug_info);
-    }
+    visitor.expand_var_debug_info(&mut body.var_debug_info);
     let ReplacementVisitor { patch, all_dead_locals, .. } = visitor;
     patch.apply(body);
     all_dead_locals
@@ -256,7 +255,7 @@ fn replace_flattened_locals<'tcx>(
 
 struct ReplacementVisitor<'tcx, 'll> {
     tcx: TyCtxt<'tcx>,
-    /// This is only used to compute the type for `VarDebugInfoContents::Composite`.
+    /// This is only used to compute the type for `VarDebugInfoFragment`.
     local_decls: &'ll LocalDecls<'tcx>,
     /// Work to do.
     replacements: &'ll ReplacementMap<'tcx>,
@@ -266,16 +265,38 @@ struct ReplacementVisitor<'tcx, 'll> {
 }
 
 impl<'tcx> ReplacementVisitor<'tcx, '_> {
-    fn gather_debug_info_fragments(&self, local: Local) -> Option<Vec<VarDebugInfoFragment<'tcx>>> {
-        let mut fragments = Vec::new();
-        let parts = self.replacements.place_fragments(local.into())?;
-        for (field, ty, replacement_local) in parts {
-            fragments.push(VarDebugInfoFragment {
-                projection: vec![PlaceElem::Field(field, ty)],
-                contents: Place::from(replacement_local),
-            });
-        }
-        Some(fragments)
+    #[instrument(level = "trace", skip(self))]
+    fn expand_var_debug_info(&mut self, var_debug_info: &mut Vec<VarDebugInfo<'tcx>>) {
+        var_debug_info.flat_map_in_place(|mut var_debug_info| {
+            let place = match var_debug_info.value {
+                VarDebugInfoContents::Const(_) => return vec![var_debug_info],
+                VarDebugInfoContents::Place(ref mut place) => place,
+            };
+
+            if let Some(repl) = self.replacements.replace_place(self.tcx, place.as_ref()) {
+                *place = repl;
+                return vec![var_debug_info];
+            }
+
+            let Some(parts) = self.replacements.place_fragments(*place) else {
+                return vec![var_debug_info];
+            };
+
+            let ty = place.ty(self.local_decls, self.tcx).ty;
+
+            parts
+                .map(|(field, field_ty, replacement_local)| {
+                    let mut var_debug_info = var_debug_info.clone();
+                    let composite = var_debug_info.composite.get_or_insert_with(|| {
+                        Box::new(VarDebugInfoFragment { ty, projection: Vec::new() })
+                    });
+                    composite.projection.push(PlaceElem::Field(field, field_ty));
+
+                    var_debug_info.value = VarDebugInfoContents::Place(replacement_local.into());
+                    var_debug_info
+                })
+                .collect()
+        });
     }
 }
 
@@ -422,48 +443,6 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
         self.super_statement(statement, location)
     }
 
-    #[instrument(level = "trace", skip(self))]
-    fn visit_var_debug_info(&mut self, var_debug_info: &mut VarDebugInfo<'tcx>) {
-        match &mut var_debug_info.value {
-            VarDebugInfoContents::Place(ref mut place) => {
-                if let Some(repl) = self.replacements.replace_place(self.tcx, place.as_ref()) {
-                    *place = repl;
-                } else if let Some(local) = place.as_local()
-                    && let Some(fragments) = self.gather_debug_info_fragments(local)
-                {
-                    let ty = place.ty(self.local_decls, self.tcx).ty;
-                    var_debug_info.value = VarDebugInfoContents::Composite { ty, fragments };
-                }
-            }
-            VarDebugInfoContents::Composite { ty: _, ref mut fragments } => {
-                let mut new_fragments = Vec::new();
-                debug!(?fragments);
-                fragments.retain_mut(|fragment| {
-                    if let Some(repl) =
-                            self.replacements.replace_place(self.tcx, fragment.contents.as_ref())
-                        {
-                            fragment.contents = repl;
-                            true
-                        } else if let Some(local) = fragment.contents.as_local()
-                            && let Some(frg) = self.gather_debug_info_fragments(local)
-                        {
-                            new_fragments.extend(frg.into_iter().map(|mut f| {
-                                f.projection.splice(0..0, fragment.projection.iter().copied());
-                                f
-                            }));
-                            false
-                        } else {
-                            true
-                        }
-                });
-                debug!(?fragments);
-                debug!(?new_fragments);
-                fragments.extend(new_fragments);
-            }
-            VarDebugInfoContents::Const(_) => {}
-        }
-    }
-
     fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) {
         assert!(!self.all_dead_locals.contains(*local));
     }
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 8cbb68fc8c1..f5cfc4153b8 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -459,7 +459,7 @@ fn collect_items_rec<'tcx>(
     // Check for PMEs and emit a diagnostic if one happened. To try to show relevant edges of the
     // mono item graph.
     if tcx.sess.diagnostic().err_count() > error_count
-        && starting_item.node.is_generic_fn()
+        && starting_item.node.is_generic_fn(tcx)
         && starting_item.node.is_user_defined()
     {
         let formatted_item = with_no_trimmed_paths!(starting_item.node.to_string());
@@ -749,39 +749,15 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
     #[instrument(skip(self), level = "debug")]
     fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: Location) {
         let literal = self.monomorphize(constant.literal);
-        let val = match literal {
-            mir::ConstantKind::Val(val, _) => val,
-            mir::ConstantKind::Ty(ct) => match ct.kind() {
-                ty::ConstKind::Value(val) => self.tcx.valtree_to_const_val((ct.ty(), val)),
-                ty::ConstKind::Unevaluated(ct) => {
-                    debug!(?ct);
-                    let param_env = ty::ParamEnv::reveal_all();
-                    match self.tcx.const_eval_resolve(param_env, ct.expand(), None) {
-                        // The `monomorphize` call should have evaluated that constant already.
-                        Ok(val) => val,
-                        Err(ErrorHandled::Reported(_)) => return,
-                        Err(ErrorHandled::TooGeneric) => span_bug!(
-                            self.body.source_info(location).span,
-                            "collection encountered polymorphic constant: {:?}",
-                            literal
-                        ),
-                    }
-                }
-                _ => return,
-            },
-            mir::ConstantKind::Unevaluated(uv, _) => {
-                let param_env = ty::ParamEnv::reveal_all();
-                match self.tcx.const_eval_resolve(param_env, uv, None) {
-                    // The `monomorphize` call should have evaluated that constant already.
-                    Ok(val) => val,
-                    Err(ErrorHandled::Reported(_)) => return,
-                    Err(ErrorHandled::TooGeneric) => span_bug!(
-                        self.body.source_info(location).span,
-                        "collection encountered polymorphic constant: {:?}",
-                        literal
-                    ),
-                }
-            }
+        let param_env = ty::ParamEnv::reveal_all();
+        let val = match literal.eval(self.tcx, param_env, None) {
+            Ok(v) => v,
+            Err(ErrorHandled::Reported(_)) => return,
+            Err(ErrorHandled::TooGeneric) => span_bug!(
+                self.body.source_info(location).span,
+                "collection encountered polymorphic constant: {:?}",
+                literal
+            ),
         };
         collect_const_value(self.tcx, val, self.output);
         MirVisitor::visit_ty(self, literal.ty(), TyContext::Location(location));
@@ -1315,6 +1291,7 @@ fn create_mono_items_for_default_impls<'tcx>(
     // it, to validate whether or not the impl is legal to instantiate at all.
     let only_region_params = |param: &ty::GenericParamDef, _: &_| match param.kind {
         GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
+        GenericParamDefKind::Const { is_host_effect: true, .. } => tcx.consts.true_.into(),
         GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
             unreachable!(
                 "`own_requires_monomorphization` check means that \
@@ -1470,8 +1447,9 @@ fn collect_const_value<'tcx>(
 ) {
     match value {
         ConstValue::Scalar(Scalar::Ptr(ptr, _size)) => collect_alloc(tcx, ptr.provenance, output),
-        ConstValue::Slice { data: alloc, start: _, end: _ } | ConstValue::ByRef { alloc, .. } => {
-            for &id in alloc.inner().provenance().ptrs().values() {
+        ConstValue::Indirect { alloc_id, .. } => collect_alloc(tcx, alloc_id, output),
+        ConstValue::Slice { data, start: _, end: _ } => {
+            for &id in data.inner().provenance().ptrs().values() {
                 collect_alloc(tcx, id, output);
             }
         }
diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs
index de6db8ae6ae..c993e64477b 100644
--- a/compiler/rustc_monomorphize/src/partitioning.rs
+++ b/compiler/rustc_monomorphize/src/partitioning.rs
@@ -221,7 +221,7 @@ where
         }
 
         let characteristic_def_id = characteristic_def_id_of_mono_item(cx.tcx, mono_item);
-        let is_volatile = is_incremental_build && mono_item.is_generic_fn();
+        let is_volatile = is_incremental_build && mono_item.is_generic_fn(cx.tcx);
 
         let cgu_name = match characteristic_def_id {
             Some(def_id) => compute_codegen_unit_name(
@@ -801,7 +801,7 @@ fn mono_item_visibility<'tcx>(
         return Visibility::Hidden;
     }
 
-    let is_generic = instance.args.non_erasable_generics().next().is_some();
+    let is_generic = instance.args.non_erasable_generics(tcx, def_id).next().is_some();
 
     // Upstream `DefId` instances get different handling than local ones.
     let Some(def_id) = def_id.as_local() else {
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 34cc0998c9b..2c4bc7bb568 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -196,6 +196,9 @@ parse_expected_else_block = expected `{"{"}`, found {$first_tok}
     .suggestion = add an `if` if this is the condition of a chained `else if` statement
 
 parse_expected_expression_found_let = expected expression, found `let` statement
+    .note = only supported directly in conditions of `if` and `while` expressions
+    .not_supported_or = `||` operators are not supported in let chain expressions
+    .not_supported_parentheses = `let`s wrapped in parentheses are not supported in a context with let chains
 
 parse_expected_fn_path_found_fn_keyword = expected identifier, found keyword `fn`
     .suggestion = use `Fn` to refer to the trait
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index e0b1e3678e4..5d3ec683552 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -10,7 +10,7 @@ use rustc_span::symbol::Ident;
 use rustc_span::{Span, Symbol};
 
 use crate::fluent_generated as fluent;
-use crate::parser::TokenDescription;
+use crate::parser::{ForbiddenLetReason, TokenDescription};
 
 #[derive(Diagnostic)]
 #[diag(parse_maybe_report_ambiguous_plus)]
@@ -392,9 +392,12 @@ pub(crate) struct IfExpressionMissingCondition {
 
 #[derive(Diagnostic)]
 #[diag(parse_expected_expression_found_let)]
+#[note]
 pub(crate) struct ExpectedExpressionFoundLet {
     #[primary_span]
     pub span: Span,
+    #[subdiagnostic]
+    pub reason: ForbiddenLetReason,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index 5d6c574baa6..c4e8d9006e6 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -106,7 +106,7 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
         let mut cursor_snapshot = self.cursor_snapshot.clone();
         let tokens =
             std::iter::once((FlatToken::Token(self.start_token.0.clone()), self.start_token.1))
-                .chain((0..self.num_calls).map(|_| {
+                .chain(std::iter::repeat_with(|| {
                     let token = cursor_snapshot.next();
                     (FlatToken::Token(token.0), token.1)
                 }))
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 5898c6565e6..f4cee3a661e 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -8,6 +8,7 @@ use super::{
 
 use crate::errors;
 use crate::maybe_recover_from_interpolated_ty_qpath;
+use ast::mut_visit::{noop_visit_expr, MutVisitor};
 use ast::{Path, PathSegment};
 use core::mem;
 use rustc_ast::ptr::P;
@@ -27,6 +28,7 @@ use rustc_errors::{
     AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic,
     PResult, StashKey,
 };
+use rustc_macros::Subdiagnostic;
 use rustc_session::errors::{report_lit_error, ExprParenthesesNeeded};
 use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
 use rustc_session::lint::BuiltinLintDiagnostics;
@@ -122,8 +124,8 @@ impl<'a> Parser<'a> {
         self.parse_expr().map(|value| AnonConst { id: DUMMY_NODE_ID, value })
     }
 
-    fn parse_expr_catch_underscore(&mut self) -> PResult<'a, P<Expr>> {
-        match self.parse_expr() {
+    fn parse_expr_catch_underscore(&mut self, restrictions: Restrictions) -> PResult<'a, P<Expr>> {
+        match self.parse_expr_res(restrictions, None) {
             Ok(expr) => Ok(expr),
             Err(mut err) => match self.token.ident() {
                 Some((Ident { name: kw::Underscore, .. }, false))
@@ -141,7 +143,8 @@ impl<'a> Parser<'a> {
 
     /// Parses a sequence of expressions delimited by parentheses.
     fn parse_expr_paren_seq(&mut self) -> PResult<'a, ThinVec<P<Expr>>> {
-        self.parse_paren_comma_seq(|p| p.parse_expr_catch_underscore()).map(|(r, _)| r)
+        self.parse_paren_comma_seq(|p| p.parse_expr_catch_underscore(Restrictions::empty()))
+            .map(|(r, _)| r)
     }
 
     /// Parses an expression, subject to the given restrictions.
@@ -1345,110 +1348,113 @@ impl<'a> Parser<'a> {
         // Outer attributes are already parsed and will be
         // added to the return value after the fact.
 
-        // Note: when adding new syntax here, don't forget to adjust `TokenKind::can_begin_expr()`.
-        let lo = self.token.span;
-        if let token::Literal(_) = self.token.kind {
-            // This match arm is a special-case of the `_` match arm below and
-            // could be removed without changing functionality, but it's faster
-            // to have it here, especially for programs with large constants.
-            self.parse_expr_lit()
-        } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
-            self.parse_expr_tuple_parens()
-        } else if self.check(&token::OpenDelim(Delimiter::Brace)) {
-            self.parse_expr_block(None, lo, BlockCheckMode::Default)
-        } else if self.check(&token::BinOp(token::Or)) || self.check(&token::OrOr) {
-            self.parse_expr_closure().map_err(|mut err| {
-                // If the input is something like `if a { 1 } else { 2 } | if a { 3 } else { 4 }`
-                // then suggest parens around the lhs.
-                if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&lo) {
-                    err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
+        let restrictions = self.restrictions;
+        self.with_res(restrictions - Restrictions::ALLOW_LET, |this| {
+            // Note: when adding new syntax here, don't forget to adjust `TokenKind::can_begin_expr()`.
+            let lo = this.token.span;
+            if let token::Literal(_) = this.token.kind {
+                // This match arm is a special-case of the `_` match arm below and
+                // could be removed without changing functionality, but it's faster
+                // to have it here, especially for programs with large constants.
+                this.parse_expr_lit()
+            } else if this.check(&token::OpenDelim(Delimiter::Parenthesis)) {
+                this.parse_expr_tuple_parens(restrictions)
+            } else if this.check(&token::OpenDelim(Delimiter::Brace)) {
+                this.parse_expr_block(None, lo, BlockCheckMode::Default)
+            } else if this.check(&token::BinOp(token::Or)) || this.check(&token::OrOr) {
+                this.parse_expr_closure().map_err(|mut err| {
+                    // If the input is something like `if a { 1 } else { 2 } | if a { 3 } else { 4 }`
+                    // then suggest parens around the lhs.
+                    if let Some(sp) = this.sess.ambiguous_block_expr_parse.borrow().get(&lo) {
+                        err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
+                    }
+                    err
+                })
+            } else if this.check(&token::OpenDelim(Delimiter::Bracket)) {
+                this.parse_expr_array_or_repeat(Delimiter::Bracket)
+            } else if this.is_builtin() {
+                this.parse_expr_builtin()
+            } else if this.check_path() {
+                this.parse_expr_path_start()
+            } else if this.check_keyword(kw::Move)
+                || this.check_keyword(kw::Static)
+                || this.check_const_closure()
+            {
+                this.parse_expr_closure()
+            } else if this.eat_keyword(kw::If) {
+                this.parse_expr_if()
+            } else if this.check_keyword(kw::For) {
+                if this.choose_generics_over_qpath(1) {
+                    this.parse_expr_closure()
+                } else {
+                    assert!(this.eat_keyword(kw::For));
+                    this.parse_expr_for(None, this.prev_token.span)
                 }
-                err
-            })
-        } else if self.check(&token::OpenDelim(Delimiter::Bracket)) {
-            self.parse_expr_array_or_repeat(Delimiter::Bracket)
-        } else if self.is_builtin() {
-            self.parse_expr_builtin()
-        } else if self.check_path() {
-            self.parse_expr_path_start()
-        } else if self.check_keyword(kw::Move)
-            || self.check_keyword(kw::Static)
-            || self.check_const_closure()
-        {
-            self.parse_expr_closure()
-        } else if self.eat_keyword(kw::If) {
-            self.parse_expr_if()
-        } else if self.check_keyword(kw::For) {
-            if self.choose_generics_over_qpath(1) {
-                self.parse_expr_closure()
-            } else {
-                assert!(self.eat_keyword(kw::For));
-                self.parse_expr_for(None, self.prev_token.span)
-            }
-        } else if self.eat_keyword(kw::While) {
-            self.parse_expr_while(None, self.prev_token.span)
-        } else if let Some(label) = self.eat_label() {
-            self.parse_expr_labeled(label, true)
-        } else if self.eat_keyword(kw::Loop) {
-            let sp = self.prev_token.span;
-            self.parse_expr_loop(None, self.prev_token.span).map_err(|mut err| {
-                err.span_label(sp, "while parsing this `loop` expression");
-                err
-            })
-        } else if self.eat_keyword(kw::Match) {
-            let match_sp = self.prev_token.span;
-            self.parse_expr_match().map_err(|mut err| {
-                err.span_label(match_sp, "while parsing this `match` expression");
-                err
-            })
-        } else if self.eat_keyword(kw::Unsafe) {
-            let sp = self.prev_token.span;
-            self.parse_expr_block(None, lo, BlockCheckMode::Unsafe(ast::UserProvided)).map_err(
-                |mut err| {
-                    err.span_label(sp, "while parsing this `unsafe` expression");
+            } else if this.eat_keyword(kw::While) {
+                this.parse_expr_while(None, this.prev_token.span)
+            } else if let Some(label) = this.eat_label() {
+                this.parse_expr_labeled(label, true)
+            } else if this.eat_keyword(kw::Loop) {
+                let sp = this.prev_token.span;
+                this.parse_expr_loop(None, this.prev_token.span).map_err(|mut err| {
+                    err.span_label(sp, "while parsing this `loop` expression");
                     err
-                },
-            )
-        } else if self.check_inline_const(0) {
-            self.parse_const_block(lo.to(self.token.span), false)
-        } else if self.may_recover() && self.is_do_catch_block() {
-            self.recover_do_catch()
-        } else if self.is_try_block() {
-            self.expect_keyword(kw::Try)?;
-            self.parse_try_block(lo)
-        } else if self.eat_keyword(kw::Return) {
-            self.parse_expr_return()
-        } else if self.eat_keyword(kw::Continue) {
-            self.parse_expr_continue(lo)
-        } else if self.eat_keyword(kw::Break) {
-            self.parse_expr_break()
-        } else if self.eat_keyword(kw::Yield) {
-            self.parse_expr_yield()
-        } else if self.is_do_yeet() {
-            self.parse_expr_yeet()
-        } else if self.eat_keyword(kw::Become) {
-            self.parse_expr_become()
-        } else if self.check_keyword(kw::Let) {
-            self.parse_expr_let()
-        } else if self.eat_keyword(kw::Underscore) {
-            Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore))
-        } else if self.token.uninterpolated_span().at_least_rust_2018() {
-            // `Span:.at_least_rust_2018()` is somewhat expensive; don't get it repeatedly.
-            if self.check_keyword(kw::Async) {
-                if self.is_async_block() {
-                    // Check for `async {` and `async move {`.
-                    self.parse_async_block()
+                })
+            } else if this.eat_keyword(kw::Match) {
+                let match_sp = this.prev_token.span;
+                this.parse_expr_match().map_err(|mut err| {
+                    err.span_label(match_sp, "while parsing this `match` expression");
+                    err
+                })
+            } else if this.eat_keyword(kw::Unsafe) {
+                let sp = this.prev_token.span;
+                this.parse_expr_block(None, lo, BlockCheckMode::Unsafe(ast::UserProvided)).map_err(
+                    |mut err| {
+                        err.span_label(sp, "while parsing this `unsafe` expression");
+                        err
+                    },
+                )
+            } else if this.check_inline_const(0) {
+                this.parse_const_block(lo.to(this.token.span), false)
+            } else if this.may_recover() && this.is_do_catch_block() {
+                this.recover_do_catch()
+            } else if this.is_try_block() {
+                this.expect_keyword(kw::Try)?;
+                this.parse_try_block(lo)
+            } else if this.eat_keyword(kw::Return) {
+                this.parse_expr_return()
+            } else if this.eat_keyword(kw::Continue) {
+                this.parse_expr_continue(lo)
+            } else if this.eat_keyword(kw::Break) {
+                this.parse_expr_break()
+            } else if this.eat_keyword(kw::Yield) {
+                this.parse_expr_yield()
+            } else if this.is_do_yeet() {
+                this.parse_expr_yeet()
+            } else if this.eat_keyword(kw::Become) {
+                this.parse_expr_become()
+            } else if this.check_keyword(kw::Let) {
+                this.parse_expr_let(restrictions)
+            } else if this.eat_keyword(kw::Underscore) {
+                Ok(this.mk_expr(this.prev_token.span, ExprKind::Underscore))
+            } else if this.token.uninterpolated_span().at_least_rust_2018() {
+                // `Span:.at_least_rust_2018()` is somewhat expensive; don't get it repeatedly.
+                if this.check_keyword(kw::Async) {
+                    if this.is_async_block() {
+                        // Check for `async {` and `async move {`.
+                        this.parse_async_block()
+                    } else {
+                        this.parse_expr_closure()
+                    }
+                } else if this.eat_keyword(kw::Await) {
+                    this.recover_incorrect_await_syntax(lo, this.prev_token.span)
                 } else {
-                    self.parse_expr_closure()
+                    this.parse_expr_lit()
                 }
-            } else if self.eat_keyword(kw::Await) {
-                self.recover_incorrect_await_syntax(lo, self.prev_token.span)
             } else {
-                self.parse_expr_lit()
+                this.parse_expr_lit()
             }
-        } else {
-            self.parse_expr_lit()
-        }
+        })
     }
 
     fn parse_expr_lit(&mut self) -> PResult<'a, P<Expr>> {
@@ -1462,13 +1468,13 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn parse_expr_tuple_parens(&mut self) -> PResult<'a, P<Expr>> {
+    fn parse_expr_tuple_parens(&mut self, restrictions: Restrictions) -> PResult<'a, P<Expr>> {
         let lo = self.token.span;
         self.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
         let (es, trailing_comma) = match self.parse_seq_to_end(
             &token::CloseDelim(Delimiter::Parenthesis),
             SeqSep::trailing_allowed(token::Comma),
-            |p| p.parse_expr_catch_underscore(),
+            |p| p.parse_expr_catch_underscore(restrictions.intersection(Restrictions::ALLOW_LET)),
         ) {
             Ok(x) => x,
             Err(err) => {
@@ -2231,7 +2237,8 @@ impl<'a> Parser<'a> {
         let decl_hi = self.prev_token.span;
         let mut body = match fn_decl.output {
             FnRetTy::Default(_) => {
-                let restrictions = self.restrictions - Restrictions::STMT_EXPR;
+                let restrictions =
+                    self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET;
                 self.parse_expr_res(restrictions, None)?
             }
             _ => {
@@ -2436,10 +2443,12 @@ impl<'a> Parser<'a> {
 
     /// Parses the condition of a `if` or `while` expression.
     fn parse_expr_cond(&mut self) -> PResult<'a, P<Expr>> {
-        let cond =
+        let mut cond =
             self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, None)?;
 
-        if let ExprKind::Let(..) = cond.kind {
+        CondChecker { parser: self, forbid_let_reason: None }.visit_expr(&mut cond);
+
+        if let ExprKind::Let(_, _, _, None) = cond.kind {
             // Remove the last feature gating of a `let` expression since it's stable.
             self.sess.gated_spans.ungate_last(sym::let_chains, cond.span);
         }
@@ -2448,18 +2457,15 @@ impl<'a> Parser<'a> {
     }
 
     /// Parses a `let $pat = $expr` pseudo-expression.
-    fn parse_expr_let(&mut self) -> PResult<'a, P<Expr>> {
-        // This is a *approximate* heuristic that detects if `let` chains are
-        // being parsed in the right position. It's approximate because it
-        // doesn't deny all invalid `let` expressions, just completely wrong usages.
-        let not_in_chain = !matches!(
-            self.prev_token.kind,
-            TokenKind::AndAnd | TokenKind::Ident(kw::If, _) | TokenKind::Ident(kw::While, _)
-        );
-        if !self.restrictions.contains(Restrictions::ALLOW_LET) || not_in_chain {
-            self.sess.emit_err(errors::ExpectedExpressionFoundLet { span: self.token.span });
-        }
-
+    fn parse_expr_let(&mut self, restrictions: Restrictions) -> PResult<'a, P<Expr>> {
+        let is_recovered = if !restrictions.contains(Restrictions::ALLOW_LET) {
+            Some(self.sess.emit_err(errors::ExpectedExpressionFoundLet {
+                span: self.token.span,
+                reason: ForbiddenLetReason::OtherForbidden,
+            }))
+        } else {
+            None
+        };
         self.bump(); // Eat `let` token
         let lo = self.prev_token.span;
         let pat = self.parse_pat_allow_top_alt(
@@ -2479,8 +2485,7 @@ impl<'a> Parser<'a> {
         }
         let expr = self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), None.into())?;
         let span = lo.to(expr.span);
-        self.sess.gated_spans.gate(sym::let_chains, span);
-        Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span)))
+        Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, is_recovered)))
     }
 
     /// Parses an `else { ... }` expression (`else` token already eaten).
@@ -2829,7 +2834,10 @@ impl<'a> Parser<'a> {
             )?;
             let guard = if this.eat_keyword(kw::If) {
                 let if_span = this.prev_token.span;
-                let cond = this.parse_expr_res(Restrictions::ALLOW_LET, None)?;
+                let mut cond = this.parse_expr_res(Restrictions::ALLOW_LET, None)?;
+
+                CondChecker { parser: this, forbid_let_reason: None }.visit_expr(&mut cond);
+
                 let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond);
                 if has_let_expr {
                     if does_not_have_bin_op {
@@ -3414,3 +3422,130 @@ impl<'a> Parser<'a> {
         })
     }
 }
+
+/// Used to forbid `let` expressions in certain syntactic locations.
+#[derive(Clone, Copy, Subdiagnostic)]
+pub(crate) enum ForbiddenLetReason {
+    /// `let` is not valid and the source environment is not important
+    OtherForbidden,
+    /// A let chain with the `||` operator
+    #[note(parse_not_supported_or)]
+    NotSupportedOr(#[primary_span] Span),
+    /// A let chain with invalid parentheses
+    ///
+    /// For example, `let 1 = 1 && (expr && expr)` is allowed
+    /// but `(let 1 = 1 && (let 1 = 1 && (let 1 = 1))) && let a = 1` is not
+    #[note(parse_not_supported_parentheses)]
+    NotSupportedParentheses(#[primary_span] Span),
+}
+
+/// Visitor to check for invalid/unstable use of `ExprKind::Let` that can't
+/// easily be caught in parsing. For example:
+///
+/// ```rust,ignore (example)
+/// // Only know that the let isn't allowed once the `||` token is reached
+/// if let Some(x) = y || true {}
+/// // Only know that the let isn't allowed once the second `=` token is reached.
+/// if let Some(x) = y && z = 1 {}
+/// ```
+struct CondChecker<'a> {
+    parser: &'a Parser<'a>,
+    forbid_let_reason: Option<ForbiddenLetReason>,
+}
+
+impl MutVisitor for CondChecker<'_> {
+    fn visit_expr(&mut self, e: &mut P<Expr>) {
+        use ForbiddenLetReason::*;
+
+        let span = e.span;
+        match e.kind {
+            ExprKind::Let(_, _, _, ref mut is_recovered @ None) => {
+                if let Some(reason) = self.forbid_let_reason {
+                    *is_recovered = Some(
+                        self.parser
+                            .sess
+                            .emit_err(errors::ExpectedExpressionFoundLet { span, reason }),
+                    );
+                } else {
+                    self.parser.sess.gated_spans.gate(sym::let_chains, span);
+                }
+            }
+            ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, _, _) => {
+                noop_visit_expr(e, self);
+            }
+            ExprKind::Binary(Spanned { node: BinOpKind::Or, span: or_span }, _, _)
+                if let None | Some(NotSupportedOr(_)) = self.forbid_let_reason =>
+            {
+                let forbid_let_reason = self.forbid_let_reason;
+                self.forbid_let_reason = Some(NotSupportedOr(or_span));
+                noop_visit_expr(e, self);
+                self.forbid_let_reason = forbid_let_reason;
+            }
+            ExprKind::Paren(ref inner)
+                if let None | Some(NotSupportedParentheses(_)) = self.forbid_let_reason =>
+            {
+                let forbid_let_reason = self.forbid_let_reason;
+                self.forbid_let_reason = Some(NotSupportedParentheses(inner.span));
+                noop_visit_expr(e, self);
+                self.forbid_let_reason = forbid_let_reason;
+            }
+            ExprKind::Unary(_, _)
+            | ExprKind::Await(_, _)
+            | ExprKind::Assign(_, _, _)
+            | ExprKind::AssignOp(_, _, _)
+            | ExprKind::Range(_, _, _)
+            | ExprKind::Try(_)
+            | ExprKind::AddrOf(_, _, _)
+            | ExprKind::Binary(_, _, _)
+            | ExprKind::Field(_, _)
+            | ExprKind::Index(_, _, _)
+            | ExprKind::Call(_, _)
+            | ExprKind::MethodCall(_)
+            | ExprKind::Tup(_)
+            | ExprKind::Paren(_) => {
+                let forbid_let_reason = self.forbid_let_reason;
+                self.forbid_let_reason = Some(OtherForbidden);
+                noop_visit_expr(e, self);
+                self.forbid_let_reason = forbid_let_reason;
+            }
+            ExprKind::Cast(ref mut op, _)
+            | ExprKind::Type(ref mut op, _) => {
+                let forbid_let_reason = self.forbid_let_reason;
+                self.forbid_let_reason = Some(OtherForbidden);
+                self.visit_expr(op);
+                self.forbid_let_reason = forbid_let_reason;
+            }
+            ExprKind::Let(_, _, _, Some(_))
+            | ExprKind::Array(_)
+            | ExprKind::ConstBlock(_)
+            | ExprKind::Lit(_)
+            | ExprKind::If(_, _, _)
+            | ExprKind::While(_, _, _)
+            | ExprKind::ForLoop(_, _, _, _)
+            | ExprKind::Loop(_, _, _)
+            | ExprKind::Match(_, _)
+            | ExprKind::Closure(_)
+            | ExprKind::Block(_, _)
+            | ExprKind::Async(_, _)
+            | ExprKind::TryBlock(_)
+            | ExprKind::Underscore
+            | ExprKind::Path(_, _)
+            | ExprKind::Break(_, _)
+            | ExprKind::Continue(_)
+            | ExprKind::Ret(_)
+            | ExprKind::InlineAsm(_)
+            | ExprKind::OffsetOf(_, _)
+            | ExprKind::MacCall(_)
+            | ExprKind::Struct(_)
+            | ExprKind::Repeat(_, _)
+            | ExprKind::Yield(_)
+            | ExprKind::Yeet(_)
+            | ExprKind::Become(_)
+            | ExprKind::IncludedBytes(_)
+            | ExprKind::FormatArgs(_)
+            | ExprKind::Err => {
+                // These would forbid any let expressions they contain already.
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 233c7016417..aad4edaba90 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -73,12 +73,16 @@ impl<'a> Parser<'a> {
             if !self.maybe_consume_incorrect_semicolon(&items) {
                 let msg = format!("expected item, found {token_str}");
                 let mut err = self.struct_span_err(self.token.span, msg);
-                let label = if self.is_kw_followed_by_ident(kw::Let) {
-                    "consider using `const` or `static` instead of `let` for global variables"
+                let span = self.token.span;
+                if self.is_kw_followed_by_ident(kw::Let) {
+                    err.span_label(
+                        span,
+                        "consider using `const` or `static` instead of `let` for global variables",
+                    );
                 } else {
-                    "expected item"
+                    err.span_label(span, "expected item")
+                        .note("for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>");
                 };
-                err.span_label(self.token.span, label);
                 return Err(err);
             }
         }
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 77c59bb3881..e84d8f5b358 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -13,6 +13,7 @@ mod ty;
 use crate::lexer::UnmatchedDelim;
 pub use attr_wrapper::AttrWrapper;
 pub use diagnostics::AttemptLocalParseRecovery;
+pub(crate) use expr::ForbiddenLetReason;
 pub(crate) use item::FnParseMode;
 pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma};
 pub use path::PathStyle;
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 4aadb7d7ca5..3e4e9278910 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -830,7 +830,8 @@ impl<'a> Parser<'a> {
     ) -> PResult<'a, PatKind> {
         let ident = self.parse_ident()?;
 
-        if !matches!(syntax_loc, Some(PatternLocation::FunctionParameter))
+        if self.may_recover()
+            && !matches!(syntax_loc, Some(PatternLocation::FunctionParameter))
             && self.check_noexpect(&token::Lt)
             && self.look_ahead(1, |t| t.can_begin_type())
         {
diff --git a/compiler/rustc_parse_format/Cargo.toml b/compiler/rustc_parse_format/Cargo.toml
index 72da398d3fc..14330353239 100644
--- a/compiler/rustc_parse_format/Cargo.toml
+++ b/compiler/rustc_parse_format/Cargo.toml
@@ -5,4 +5,4 @@ edition = "2021"
 
 [dependencies]
 rustc_lexer = { path = "../rustc_lexer" }
-rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_index = { path = "../rustc_index", default-features = false }
diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs
index 90b94f51ea7..90ac436a91f 100644
--- a/compiler/rustc_parse_format/src/lib.rs
+++ b/compiler/rustc_parse_format/src/lib.rs
@@ -1051,7 +1051,7 @@ fn unescape_string(string: &str) -> Option<string::String> {
 
 // Assert a reasonable size for `Piece`
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(Piece<'_>, 16);
+rustc_index::static_assert_size!(Piece<'_>, 16);
 
 #[cfg(test)]
 mod tests;
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 57598cf8bcf..56f4b387df8 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -4,14 +4,14 @@
 -passes_see_issue =
     see issue #{$issue} <https://github.com/rust-lang/rust/issues/{$issue}> for more information
 
-passes_abi =
-    abi: {$abi}
-
+passes_abi_invalid_attribute =
+    `#[rustc_abi]` can only be applied to function items, type aliases, and associated functions
+passes_abi_ne =
+    ABIs are not compatible
+    left ABI = {$left}
+    right ABI = {$right}
 passes_abi_of =
-    fn_abi_of_instance({$fn_name}) = {$fn_abi}
-
-passes_align =
-    align: {$align}
+    fn_abi_of({$fn_name}) = {$fn_abi}
 
 passes_allow_incoherent_impl =
     `rustc_allow_incoherent_impl` attribute should be applied to impl items.
@@ -104,15 +104,24 @@ passes_collapse_debuginfo =
 passes_confusables = attribute should be applied to an inherent method
     .label = not an inherent method
 
-passes_const_impl_const_trait =
-    const `impl`s must be for traits marked with `#[const_trait]`
-    .note = this trait must be annotated with `#[const_trait]`
-
 passes_continue_labeled_block =
     `continue` pointing to a labeled block
     .label = labeled blocks cannot be `continue`'d
     .block_label = labeled block the `continue` points to
 
+passes_coverage_fn_defn =
+    `#[coverage]` may only be applied to function definitions
+
+passes_coverage_ignored_function_prototype =
+    `#[coverage]` is ignored on function prototypes
+
+passes_coverage_not_coverable =
+    `#[coverage]` must be applied to coverable code
+    .label = not coverable code
+
+passes_coverage_propagate =
+    `#[coverage]` does not propagate into items and must be applied to the contained functions directly
+
 passes_dead_codes =
     { $multiple ->
       *[true] multiple {$descr}s are
@@ -318,9 +327,6 @@ passes_has_incoherent_inherent_impl =
     `rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits.
     .label = only adts, extern types and traits are supported
 
-passes_homogeneous_aggregate =
-    homogeneous_aggregate: {$homogeneous_aggregate}
-
 passes_ignored_attr =
     `#[{$sym}]` is ignored on struct fields and match arms
     .warn = {-passes_previously_accepted}
@@ -404,9 +410,18 @@ passes_lang_item_on_incorrect_target =
 
 passes_layout =
     layout error: {$layout_error}
-
+passes_layout_abi =
+    abi: {$abi}
+passes_layout_align =
+    align: {$align}
+passes_layout_homogeneous_aggregate =
+    homogeneous_aggregate: {$homogeneous_aggregate}
+passes_layout_invalid_attribute =
+    `#[rustc_layout]` can only be applied to `struct`/`enum`/`union` declarations and type aliases
 passes_layout_of =
     layout_of({$normalized_ty}) = {$ty_layout}
+passes_layout_size =
+    size: {$size}
 
 passes_link =
     attribute should be applied to an `extern` block with non-Rust ABI
@@ -497,19 +512,6 @@ passes_naked_functions_operands =
 passes_naked_tracked_caller =
     cannot use `#[track_caller]` with `#[naked]`
 
-passes_no_coverage_fn_defn =
-    `#[no_coverage]` may only be applied to function definitions
-
-passes_no_coverage_ignored_function_prototype =
-    `#[no_coverage]` is ignored on function prototypes
-
-passes_no_coverage_not_coverable =
-    `#[no_coverage]` must be applied to coverable code
-    .label = not coverable code
-
-passes_no_coverage_propagate =
-    `#[no_coverage]` does not propagate into items and must be applied to the contained functions directly
-
 passes_no_link =
     attribute should be applied to an `extern crate` item
     .label = not an `extern crate` item
@@ -662,9 +664,6 @@ passes_should_be_applied_to_trait =
     attribute should be applied to a trait
     .label = not a trait
 
-passes_size =
-    size: {$size}
-
 passes_skipping_const_checks = skipping const checks
 
 passes_stability_promotable =
diff --git a/compiler/rustc_passes/src/abi_test.rs b/compiler/rustc_passes/src/abi_test.rs
index 5c0438e78ae..9e4d960af14 100644
--- a/compiler/rustc_passes/src/abi_test.rs
+++ b/compiler/rustc_passes/src/abi_test.rs
@@ -1,44 +1,66 @@
 use rustc_ast::Attribute;
 use rustc_hir::def::DefKind;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::LocalDefId;
 use rustc_middle::ty::layout::{FnAbiError, LayoutError};
-use rustc_middle::ty::{self, GenericArgs, Instance, TyCtxt};
+use rustc_middle::ty::{self, GenericArgs, Instance, Ty, TyCtxt};
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::sym;
+use rustc_target::abi::call::FnAbi;
 
-use crate::errors::{AbiOf, UnrecognizedField};
+use super::layout_test::ensure_wf;
+use crate::errors::{AbiInvalidAttribute, AbiNe, AbiOf, UnrecognizedField};
 
 pub fn test_abi(tcx: TyCtxt<'_>) {
     if !tcx.features().rustc_attrs {
         // if the `rustc_attrs` feature is not enabled, don't bother testing ABI
         return;
     }
-    for id in tcx.hir().items() {
-        match tcx.def_kind(id.owner_id) {
-            DefKind::Fn => {
-                for attr in tcx.get_attrs(id.owner_id, sym::rustc_abi) {
-                    dump_abi_of(tcx, id.owner_id.def_id.into(), attr);
+    for id in tcx.hir_crate_items(()).definitions() {
+        for attr in tcx.get_attrs(id, sym::rustc_abi) {
+            match tcx.def_kind(id) {
+                DefKind::Fn | DefKind::AssocFn => {
+                    dump_abi_of_fn_item(tcx, id, attr);
                 }
-            }
-            DefKind::Impl { .. } => {
-                // To find associated functions we need to go into the child items here.
-                for &id in tcx.associated_item_def_ids(id.owner_id) {
-                    if matches!(tcx.def_kind(id), DefKind::AssocFn) {
-                        for attr in tcx.get_attrs(id, sym::rustc_abi) {
-                            dump_abi_of(tcx, id, attr);
-                        }
-                    }
+                DefKind::TyAlias { .. } => {
+                    dump_abi_of_fn_type(tcx, id, attr);
+                }
+                _ => {
+                    tcx.sess.emit_err(AbiInvalidAttribute { span: tcx.def_span(id) });
                 }
             }
-            _ => {}
         }
     }
 }
 
-fn dump_abi_of(tcx: TyCtxt<'_>, item_def_id: DefId, attr: &Attribute) {
+fn unwrap_fn_abi<'tcx>(
+    abi: Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>>,
+    tcx: TyCtxt<'tcx>,
+    item_def_id: LocalDefId,
+) -> &'tcx FnAbi<'tcx, Ty<'tcx>> {
+    match abi {
+        Ok(abi) => abi,
+        Err(FnAbiError::Layout(layout_error)) => {
+            tcx.sess.emit_fatal(Spanned {
+                node: layout_error.into_diagnostic(),
+                span: tcx.def_span(item_def_id),
+            });
+        }
+        Err(FnAbiError::AdjustForForeignAbi(e)) => {
+            // Sadly there seems to be no `into_diagnostic` for this case... and I am not sure if
+            // this can even be reached. Anyway this is a perma-unstable debug attribute, an ICE
+            // isn't the worst thing. Also this matches what codegen does.
+            span_bug!(
+                tcx.def_span(item_def_id),
+                "error computing fn_abi_of_instance, cannot adjust for foreign ABI: {e:?}",
+            )
+        }
+    }
+}
+
+fn dump_abi_of_fn_item(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
     let param_env = tcx.param_env(item_def_id);
     let args = GenericArgs::identity_for_item(tcx, item_def_id);
-    let instance = match Instance::resolve(tcx, param_env, item_def_id, args) {
+    let instance = match Instance::resolve(tcx, param_env, item_def_id.into(), args) {
         Ok(Some(instance)) => instance,
         Ok(None) => {
             // Not sure what to do here, but `LayoutError::Unknown` seems reasonable?
@@ -51,43 +73,125 @@ fn dump_abi_of(tcx: TyCtxt<'_>, item_def_id: DefId, attr: &Attribute) {
         }
         Err(_guaranteed) => return,
     };
-    match tcx.fn_abi_of_instance(param_env.and((instance, /* extra_args */ ty::List::empty()))) {
-        Ok(abi) => {
-            // Check out the `#[rustc_abi(..)]` attribute to tell what to dump.
-            // The `..` are the names of fields to dump.
-            let meta_items = attr.meta_item_list().unwrap_or_default();
-            for meta_item in meta_items {
-                match meta_item.name_or_empty() {
-                    sym::debug => {
-                        let fn_name = tcx.item_name(item_def_id);
-                        tcx.sess.emit_err(AbiOf {
-                            span: tcx.def_span(item_def_id),
-                            fn_name,
-                            fn_abi: format!("{:#?}", abi),
-                        });
-                    }
+    let abi = unwrap_fn_abi(
+        tcx.fn_abi_of_instance(param_env.and((instance, /* extra_args */ ty::List::empty()))),
+        tcx,
+        item_def_id,
+    );
 
-                    name => {
-                        tcx.sess.emit_err(UnrecognizedField { span: meta_item.span(), name });
-                    }
-                }
+    // Check out the `#[rustc_abi(..)]` attribute to tell what to dump.
+    // The `..` are the names of fields to dump.
+    let meta_items = attr.meta_item_list().unwrap_or_default();
+    for meta_item in meta_items {
+        match meta_item.name_or_empty() {
+            sym::debug => {
+                let fn_name = tcx.item_name(item_def_id.into());
+                tcx.sess.emit_err(AbiOf {
+                    span: tcx.def_span(item_def_id),
+                    fn_name,
+                    // FIXME: using the `Debug` impl here isn't ideal.
+                    fn_abi: format!("{:#?}", abi),
+                });
             }
-        }
 
-        Err(FnAbiError::Layout(layout_error)) => {
-            tcx.sess.emit_fatal(Spanned {
-                node: layout_error.into_diagnostic(),
-                span: tcx.def_span(item_def_id),
-            });
+            name => {
+                tcx.sess.emit_err(UnrecognizedField { span: meta_item.span(), name });
+            }
         }
-        Err(FnAbiError::AdjustForForeignAbi(e)) => {
-            // Sadly there seems to be no `into_diagnostic` for this case... and I am not sure if
-            // this can even be reached. Anyway this is a perma-unstable debug attribute, an ICE
-            // isn't the worst thing. Also this matches what codegen does.
-            span_bug!(
-                tcx.def_span(item_def_id),
-                "error computing fn_abi_of_instance, cannot adjust for foreign ABI: {e:?}",
-            )
+    }
+}
+
+fn test_abi_eq<'tcx>(abi1: &'tcx FnAbi<'tcx, Ty<'tcx>>, abi2: &'tcx FnAbi<'tcx, Ty<'tcx>>) -> bool {
+    if abi1.conv != abi2.conv
+        || abi1.args.len() != abi2.args.len()
+        || abi1.c_variadic != abi2.c_variadic
+        || abi1.fixed_count != abi2.fixed_count
+        || abi1.can_unwind != abi2.can_unwind
+    {
+        return false;
+    }
+
+    abi1.ret.eq_abi(&abi2.ret)
+        && abi1.args.iter().zip(abi2.args.iter()).all(|(arg1, arg2)| arg1.eq_abi(arg2))
+}
+
+fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
+    let param_env = tcx.param_env(item_def_id);
+    let ty = tcx.type_of(item_def_id).instantiate_identity();
+    let span = tcx.def_span(item_def_id);
+    if !ensure_wf(tcx, param_env, ty, item_def_id, span) {
+        return;
+    }
+    let meta_items = attr.meta_item_list().unwrap_or_default();
+    for meta_item in meta_items {
+        match meta_item.name_or_empty() {
+            sym::debug => {
+                let ty::FnPtr(sig) = ty.kind() else {
+                    span_bug!(
+                        meta_item.span(),
+                        "`#[rustc_abi(debug)]` on a type alias requires function pointer type"
+                    );
+                };
+                let abi = unwrap_fn_abi(
+                    tcx.fn_abi_of_fn_ptr(param_env.and((*sig, /* extra_args */ ty::List::empty()))),
+                    tcx,
+                    item_def_id,
+                );
+
+                let fn_name = tcx.item_name(item_def_id.into());
+                tcx.sess.emit_err(AbiOf { span, fn_name, fn_abi: format!("{:#?}", abi) });
+            }
+            sym::assert_eq => {
+                let ty::Tuple(fields) = ty.kind() else {
+                    span_bug!(
+                        meta_item.span(),
+                        "`#[rustc_abi(assert_eq)]` on a type alias requires pair type"
+                    );
+                };
+                let [field1, field2] = ***fields else {
+                    span_bug!(
+                        meta_item.span(),
+                        "`#[rustc_abi(assert_eq)]` on a type alias requires pair type"
+                    );
+                };
+                let ty::FnPtr(sig1) = field1.kind() else {
+                    span_bug!(
+                        meta_item.span(),
+                        "`#[rustc_abi(assert_eq)]` on a type alias requires pair of function pointer types"
+                    );
+                };
+                let abi1 = unwrap_fn_abi(
+                    tcx.fn_abi_of_fn_ptr(
+                        param_env.and((*sig1, /* extra_args */ ty::List::empty())),
+                    ),
+                    tcx,
+                    item_def_id,
+                );
+                let ty::FnPtr(sig2) = field2.kind() else {
+                    span_bug!(
+                        meta_item.span(),
+                        "`#[rustc_abi(assert_eq)]` on a type alias requires pair of function pointer types"
+                    );
+                };
+                let abi2 = unwrap_fn_abi(
+                    tcx.fn_abi_of_fn_ptr(
+                        param_env.and((*sig2, /* extra_args */ ty::List::empty())),
+                    ),
+                    tcx,
+                    item_def_id,
+                );
+
+                if !test_abi_eq(abi1, abi2) {
+                    tcx.sess.emit_err(AbiNe {
+                        span,
+                        left: format!("{:#?}", abi1),
+                        right: format!("{:#?}", abi2),
+                    });
+                }
+            }
+            name => {
+                tcx.sess.emit_err(UnrecognizedField { span: meta_item.span(), name });
+            }
         }
     }
 }
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 50a087c7847..d10bc33db52 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -107,7 +107,7 @@ impl CheckAttrVisitor<'_> {
             match attr.name_or_empty() {
                 sym::do_not_recommend => self.check_do_not_recommend(attr.span, target),
                 sym::inline => self.check_inline(hir_id, attr, span, target),
-                sym::no_coverage => self.check_no_coverage(hir_id, attr, span, target),
+                sym::coverage => self.check_coverage(hir_id, attr, span, target),
                 sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target),
                 sym::marker => self.check_marker(hir_id, attr, span, target),
                 sym::target_feature => self.check_target_feature(hir_id, attr, span, target),
@@ -139,6 +139,9 @@ impl CheckAttrVisitor<'_> {
                     self.check_rustc_std_internal_symbol(&attr, span, target)
                 }
                 sym::naked => self.check_naked(hir_id, attr, span, target),
+                sym::rustc_never_returns_null_ptr => {
+                    self.check_applied_to_fn_or_method(hir_id, attr, span, target)
+                }
                 sym::rustc_legacy_const_generics => {
                     self.check_rustc_legacy_const_generics(hir_id, &attr, span, target, item)
                 }
@@ -327,16 +330,10 @@ impl CheckAttrVisitor<'_> {
         }
     }
 
-    /// Checks if a `#[no_coverage]` is applied directly to a function
-    fn check_no_coverage(
-        &self,
-        hir_id: HirId,
-        attr: &Attribute,
-        span: Span,
-        target: Target,
-    ) -> bool {
+    /// Checks if a `#[coverage]` is applied directly to a function
+    fn check_coverage(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
         match target {
-            // no_coverage on function is fine
+            // #[coverage] on function is fine
             Target::Fn
             | Target::Closure
             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
@@ -347,7 +344,7 @@ impl CheckAttrVisitor<'_> {
                     UNUSED_ATTRIBUTES,
                     hir_id,
                     attr.span,
-                    errors::IgnoredNoCoverageFnProto,
+                    errors::IgnoredCoverageFnProto,
                 );
                 true
             }
@@ -357,7 +354,7 @@ impl CheckAttrVisitor<'_> {
                     UNUSED_ATTRIBUTES,
                     hir_id,
                     attr.span,
-                    errors::IgnoredNoCoveragePropagate,
+                    errors::IgnoredCoveragePropagate,
                 );
                 true
             }
@@ -367,13 +364,13 @@ impl CheckAttrVisitor<'_> {
                     UNUSED_ATTRIBUTES,
                     hir_id,
                     attr.span,
-                    errors::IgnoredNoCoverageFnDefn,
+                    errors::IgnoredCoverageFnDefn,
                 );
                 true
             }
 
             _ => {
-                self.tcx.sess.emit_err(errors::IgnoredNoCoverageNotCoverable {
+                self.tcx.sess.emit_err(errors::IgnoredCoverageNotCoverable {
                     attr_span: attr.span,
                     defn_span: span,
                 });
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 32dd02a4aa9..8b65e301b74 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -64,20 +64,20 @@ pub struct InlineNotFnOrClosure {
 }
 
 #[derive(LintDiagnostic)]
-#[diag(passes_no_coverage_ignored_function_prototype)]
-pub struct IgnoredNoCoverageFnProto;
+#[diag(passes_coverage_ignored_function_prototype)]
+pub struct IgnoredCoverageFnProto;
 
 #[derive(LintDiagnostic)]
-#[diag(passes_no_coverage_propagate)]
-pub struct IgnoredNoCoveragePropagate;
+#[diag(passes_coverage_propagate)]
+pub struct IgnoredCoveragePropagate;
 
 #[derive(LintDiagnostic)]
-#[diag(passes_no_coverage_fn_defn)]
-pub struct IgnoredNoCoverageFnDefn;
+#[diag(passes_coverage_fn_defn)]
+pub struct IgnoredCoverageFnDefn;
 
 #[derive(Diagnostic)]
-#[diag(passes_no_coverage_not_coverable, code = "E0788")]
-pub struct IgnoredNoCoverageNotCoverable {
+#[diag(passes_coverage_not_coverable, code = "E0788")]
+pub struct IgnoredCoverageNotCoverable {
     #[primary_span]
     pub attr_span: Span,
     #[label]
@@ -873,32 +873,32 @@ pub struct DuplicateDiagnosticItemInCrate {
 }
 
 #[derive(Diagnostic)]
-#[diag(passes_abi)]
-pub struct Abi {
+#[diag(passes_layout_abi)]
+pub struct LayoutAbi {
     #[primary_span]
     pub span: Span,
     pub abi: String,
 }
 
 #[derive(Diagnostic)]
-#[diag(passes_align)]
-pub struct Align {
+#[diag(passes_layout_align)]
+pub struct LayoutAlign {
     #[primary_span]
     pub span: Span,
     pub align: String,
 }
 
 #[derive(Diagnostic)]
-#[diag(passes_size)]
-pub struct Size {
+#[diag(passes_layout_size)]
+pub struct LayoutSize {
     #[primary_span]
     pub span: Span,
     pub size: String,
 }
 
 #[derive(Diagnostic)]
-#[diag(passes_homogeneous_aggregate)]
-pub struct HomogeneousAggregate {
+#[diag(passes_layout_homogeneous_aggregate)]
+pub struct LayoutHomogeneousAggregate {
     #[primary_span]
     pub span: Span,
     pub homogeneous_aggregate: String,
@@ -914,6 +914,13 @@ pub struct LayoutOf {
 }
 
 #[derive(Diagnostic)]
+#[diag(passes_layout_invalid_attribute)]
+pub struct LayoutInvalidAttribute {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(passes_abi_of)]
 pub struct AbiOf {
     #[primary_span]
@@ -923,6 +930,22 @@ pub struct AbiOf {
 }
 
 #[derive(Diagnostic)]
+#[diag(passes_abi_ne)]
+pub struct AbiNe {
+    #[primary_span]
+    pub span: Span,
+    pub left: String,
+    pub right: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes_abi_invalid_attribute)]
+pub struct AbiInvalidAttribute {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(passes_unrecognized_field)]
 pub struct UnrecognizedField {
     #[primary_span]
diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs
index d839fee07a6..e3a63f2e004 100644
--- a/compiler/rustc_passes/src/layout_test.rs
+++ b/compiler/rustc_passes/src/layout_test.rs
@@ -2,34 +2,76 @@ use rustc_ast::Attribute;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout};
-use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
+use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 use rustc_target::abi::{HasDataLayout, TargetDataLayout};
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
+use rustc_trait_selection::{infer::TyCtxtInferExt, traits};
 
-use crate::errors::{Abi, Align, HomogeneousAggregate, LayoutOf, Size, UnrecognizedField};
+use crate::errors::{
+    LayoutAbi, LayoutAlign, LayoutHomogeneousAggregate, LayoutInvalidAttribute, LayoutOf,
+    LayoutSize, UnrecognizedField,
+};
 
 pub fn test_layout(tcx: TyCtxt<'_>) {
     if !tcx.features().rustc_attrs {
         // if the `rustc_attrs` feature is not enabled, don't bother testing layout
         return;
     }
-    for id in tcx.hir().items() {
-        if matches!(
-            tcx.def_kind(id.owner_id),
-            DefKind::TyAlias { .. } | DefKind::Enum | DefKind::Struct | DefKind::Union
-        ) {
-            for attr in tcx.get_attrs(id.owner_id, sym::rustc_layout) {
-                dump_layout_of(tcx, id.owner_id.def_id, attr);
+    for id in tcx.hir_crate_items(()).definitions() {
+        for attr in tcx.get_attrs(id, sym::rustc_layout) {
+            match tcx.def_kind(id) {
+                DefKind::TyAlias { .. } | DefKind::Enum | DefKind::Struct | DefKind::Union => {
+                    dump_layout_of(tcx, id, attr);
+                }
+                _ => {
+                    tcx.sess.emit_err(LayoutInvalidAttribute { span: tcx.def_span(id) });
+                }
             }
         }
     }
 }
 
+pub fn ensure_wf<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    param_env: ParamEnv<'tcx>,
+    ty: Ty<'tcx>,
+    def_id: LocalDefId,
+    span: Span,
+) -> bool {
+    let pred = ty::ClauseKind::WellFormed(ty.into());
+    let obligation = traits::Obligation::new(
+        tcx,
+        traits::ObligationCause::new(
+            span,
+            def_id,
+            traits::ObligationCauseCode::WellFormed(Some(traits::WellFormedLoc::Ty(def_id))),
+        ),
+        param_env,
+        pred,
+    );
+    let infcx = tcx.infer_ctxt().build();
+    let ocx = traits::ObligationCtxt::new(&infcx);
+    ocx.register_obligation(obligation);
+    let errors = ocx.select_all_or_error();
+    if !errors.is_empty() {
+        infcx.err_ctxt().report_fulfillment_errors(&errors);
+        false
+    } else {
+        // looks WF!
+        true
+    }
+}
+
 fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
     let param_env = tcx.param_env(item_def_id);
     let ty = tcx.type_of(item_def_id).instantiate_identity();
+    let span = tcx.def_span(item_def_id.to_def_id());
+    if !ensure_wf(tcx, param_env, ty, item_def_id, span) {
+        return;
+    }
     match tcx.layout_of(param_env.and(ty)) {
         Ok(ty_layout) => {
             // Check out the `#[rustc_layout(..)]` attribute to tell what to dump.
@@ -38,29 +80,24 @@ fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
             for meta_item in meta_items {
                 match meta_item.name_or_empty() {
                     sym::abi => {
-                        tcx.sess.emit_err(Abi {
-                            span: tcx.def_span(item_def_id.to_def_id()),
-                            abi: format!("{:?}", ty_layout.abi),
-                        });
+                        tcx.sess.emit_err(LayoutAbi { span, abi: format!("{:?}", ty_layout.abi) });
                     }
 
                     sym::align => {
-                        tcx.sess.emit_err(Align {
-                            span: tcx.def_span(item_def_id.to_def_id()),
+                        tcx.sess.emit_err(LayoutAlign {
+                            span,
                             align: format!("{:?}", ty_layout.align),
                         });
                     }
 
                     sym::size => {
-                        tcx.sess.emit_err(Size {
-                            span: tcx.def_span(item_def_id.to_def_id()),
-                            size: format!("{:?}", ty_layout.size),
-                        });
+                        tcx.sess
+                            .emit_err(LayoutSize { span, size: format!("{:?}", ty_layout.size) });
                     }
 
                     sym::homogeneous_aggregate => {
-                        tcx.sess.emit_err(HomogeneousAggregate {
-                            span: tcx.def_span(item_def_id.to_def_id()),
+                        tcx.sess.emit_err(LayoutHomogeneousAggregate {
+                            span,
                             homogeneous_aggregate: format!(
                                 "{:?}",
                                 ty_layout.homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env })
@@ -70,18 +107,15 @@ fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
 
                     sym::debug => {
                         let normalized_ty = format!(
-                            "{:?}",
+                            "{}",
                             tcx.normalize_erasing_regions(
                                 param_env.with_reveal_all_normalized(tcx),
                                 ty,
                             )
                         );
+                        // FIXME: using the `Debug` impl here isn't ideal.
                         let ty_layout = format!("{:#?}", *ty_layout);
-                        tcx.sess.emit_err(LayoutOf {
-                            span: tcx.def_span(item_def_id.to_def_id()),
-                            normalized_ty,
-                            ty_layout,
-                        });
+                        tcx.sess.emit_err(LayoutOf { span, normalized_ty, ty_layout });
                     }
 
                     name => {
@@ -92,11 +126,7 @@ fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
         }
 
         Err(layout_error) => {
-            tcx.sess.emit_fatal(Spanned {
-                node: layout_error.into_diagnostic(),
-
-                span: tcx.def_span(item_def_id.to_def_id()),
-            });
+            tcx.sess.emit_fatal(Spanned { node: layout_error.into_diagnostic(), span });
         }
     }
 }
diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs
index e62833b358b..1239d6d91ac 100644
--- a/compiler/rustc_passes/src/reachable.rs
+++ b/compiler/rustc_passes/src/reachable.rs
@@ -90,6 +90,10 @@ impl<'tcx> Visitor<'tcx> for ReachableContext<'tcx> {
                 .typeck_results()
                 .type_dependent_def(expr.hir_id)
                 .map(|(kind, def_id)| Res::Def(kind, def_id)),
+            hir::ExprKind::Closure(&hir::Closure { def_id, .. }) => {
+                self.reachable_symbols.insert(def_id);
+                None
+            }
             _ => None,
         };
 
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 906a36cdb25..1c2303ae9e0 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -836,7 +836,7 @@ impl ReachEverythingInTheInterfaceVisitor<'_, '_> {
                         self.visit(self.ev.tcx.type_of(param.def_id).instantiate_identity());
                     }
                 }
-                GenericParamDefKind::Const { has_default } => {
+                GenericParamDefKind::Const { has_default, .. } => {
                     self.visit(self.ev.tcx.type_of(param.def_id).instantiate_identity());
                     if has_default {
                         self.visit(
@@ -1463,14 +1463,15 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
         };
 
         let vis = self.tcx.local_visibility(local_def_id);
-        let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id);
         let span = self.tcx.def_span(self.item_def_id.to_def_id());
         let vis_span = self.tcx.def_span(def_id);
         if self.in_assoc_ty && !vis.is_at_least(self.required_visibility, self.tcx) {
             let vis_descr = match vis {
                 ty::Visibility::Public => "public",
                 ty::Visibility::Restricted(vis_def_id) => {
-                    if vis_def_id == self.tcx.parent_module(hir_id).to_local_def_id() {
+                    if vis_def_id
+                        == self.tcx.parent_module_from_def_id(local_def_id).to_local_def_id()
+                    {
                         "private"
                     } else if vis_def_id.is_top_level_module() {
                         "crate-private"
@@ -1504,7 +1505,7 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
             };
             self.tcx.emit_spanned_lint(
                 lint,
-                hir_id,
+                self.tcx.hir().local_def_id_to_hir_id(self.item_def_id),
                 span,
                 PrivateInterfacesOrBoundsLint {
                     item_span: span,
diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
index a30ea7c1ddc..f2fdf066d15 100644
--- a/compiler/rustc_query_impl/src/plumbing.rs
+++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -348,8 +348,7 @@ pub(crate) fn encode_query_results<'a, 'tcx, Q>(
     Q: super::QueryConfigRestored<'tcx>,
     Q::RestoredValue: Encodable<CacheEncoder<'a, 'tcx>>,
 {
-    let _timer =
-        qcx.profiler().verbose_generic_activity_with_arg("encode_query_results_for", query.name());
+    let _timer = qcx.profiler().generic_activity_with_arg("encode_query_results_for", query.name());
 
     assert!(query.query_state(qcx).all_inactive());
     let cache = query.query_cache(qcx);
diff --git a/compiler/rustc_query_system/src/dep_graph/edges.rs b/compiler/rustc_query_system/src/dep_graph/edges.rs
new file mode 100644
index 00000000000..6ba3924f65e
--- /dev/null
+++ b/compiler/rustc_query_system/src/dep_graph/edges.rs
@@ -0,0 +1,73 @@
+use crate::dep_graph::DepNodeIndex;
+use smallvec::SmallVec;
+use std::hash::{Hash, Hasher};
+use std::iter::Extend;
+use std::ops::Deref;
+
+#[derive(Default, Debug)]
+pub struct EdgesVec {
+    max: u32,
+    edges: SmallVec<[DepNodeIndex; EdgesVec::INLINE_CAPACITY]>,
+}
+
+impl Hash for EdgesVec {
+    #[inline]
+    fn hash<H: Hasher>(&self, hasher: &mut H) {
+        Hash::hash(&self.edges, hasher)
+    }
+}
+
+impl EdgesVec {
+    pub const INLINE_CAPACITY: usize = 8;
+
+    #[inline]
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    #[inline]
+    pub fn push(&mut self, edge: DepNodeIndex) {
+        self.max = self.max.max(edge.as_u32());
+        self.edges.push(edge);
+    }
+
+    #[inline]
+    pub fn max_index(&self) -> u32 {
+        self.max
+    }
+}
+
+impl Deref for EdgesVec {
+    type Target = [DepNodeIndex];
+
+    #[inline]
+    fn deref(&self) -> &Self::Target {
+        self.edges.as_slice()
+    }
+}
+
+impl FromIterator<DepNodeIndex> for EdgesVec {
+    #[inline]
+    fn from_iter<T>(iter: T) -> Self
+    where
+        T: IntoIterator<Item = DepNodeIndex>,
+    {
+        let mut vec = EdgesVec::new();
+        for index in iter {
+            vec.push(index)
+        }
+        vec
+    }
+}
+
+impl Extend<DepNodeIndex> for EdgesVec {
+    #[inline]
+    fn extend<T>(&mut self, iter: T)
+    where
+        T: IntoIterator<Item = DepNodeIndex>,
+    {
+        for elem in iter {
+            self.push(elem);
+        }
+    }
+}
diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs
index 0d4d13ac20d..fa54e1a2e6a 100644
--- a/compiler/rustc_query_system/src/dep_graph/graph.rs
+++ b/compiler/rustc_query_system/src/dep_graph/graph.rs
@@ -8,7 +8,6 @@ use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc, Ordering};
 use rustc_data_structures::unord::UnordMap;
 use rustc_index::IndexVec;
 use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
-use smallvec::{smallvec, SmallVec};
 use std::assert_matches::assert_matches;
 use std::collections::hash_map::Entry;
 use std::fmt::Debug;
@@ -19,6 +18,7 @@ use std::sync::atomic::Ordering::Relaxed;
 use super::query::DepGraphQuery;
 use super::serialized::{GraphEncoder, SerializedDepGraph, SerializedDepNodeIndex};
 use super::{DepContext, DepKind, DepNode, HasDepContext, WorkProductId};
+use crate::dep_graph::EdgesVec;
 use crate::ich::StableHashingContext;
 use crate::query::{QueryContext, QuerySideEffects};
 
@@ -137,7 +137,7 @@ impl<K: DepKind> DepGraph<K> {
         let _green_node_index = current.intern_new_node(
             profiler,
             DepNode { kind: DepKind::NULL, hash: current.anon_id_seed.into() },
-            smallvec![],
+            EdgesVec::new(),
             Fingerprint::ZERO,
         );
         assert_eq!(_green_node_index, DepNodeIndex::SINGLETON_DEPENDENCYLESS_ANON_NODE);
@@ -147,7 +147,7 @@ impl<K: DepKind> DepGraph<K> {
             profiler,
             &prev_graph,
             DepNode { kind: DepKind::RED, hash: Fingerprint::ZERO.into() },
-            smallvec![],
+            EdgesVec::new(),
             None,
             false,
         );
@@ -356,12 +356,12 @@ impl<K: DepKind> DepGraphData<K> {
 
         let with_deps = |task_deps| K::with_deps(task_deps, || task(cx, arg));
         let (result, edges) = if cx.dep_context().is_eval_always(key.kind) {
-            (with_deps(TaskDepsRef::EvalAlways), smallvec![])
+            (with_deps(TaskDepsRef::EvalAlways), EdgesVec::new())
         } else {
             let task_deps = Lock::new(TaskDeps {
                 #[cfg(debug_assertions)]
                 node: Some(key),
-                reads: SmallVec::new(),
+                reads: EdgesVec::new(),
                 read_set: Default::default(),
                 phantom_data: PhantomData,
             });
@@ -486,14 +486,14 @@ impl<K: DepKind> DepGraph<K> {
 
                 // As long as we only have a low number of reads we can avoid doing a hash
                 // insert and potentially allocating/reallocating the hashmap
-                let new_read = if task_deps.reads.len() < TASK_DEPS_READS_CAP {
+                let new_read = if task_deps.reads.len() < EdgesVec::INLINE_CAPACITY {
                     task_deps.reads.iter().all(|other| *other != dep_node_index)
                 } else {
                     task_deps.read_set.insert(dep_node_index)
                 };
                 if new_read {
                     task_deps.reads.push(dep_node_index);
-                    if task_deps.reads.len() == TASK_DEPS_READS_CAP {
+                    if task_deps.reads.len() == EdgesVec::INLINE_CAPACITY {
                         // Fill `read_set` with what we have so far so we can use the hashset
                         // next time
                         task_deps.read_set.extend(task_deps.reads.iter().copied());
@@ -572,7 +572,7 @@ impl<K: DepKind> DepGraph<K> {
                 }
             }
 
-            let mut edges = SmallVec::new();
+            let mut edges = EdgesVec::new();
             K::read_deps(|task_deps| match task_deps {
                 TaskDepsRef::Allow(deps) => edges.extend(deps.lock().reads.iter().copied()),
                 TaskDepsRef::EvalAlways => {
@@ -629,12 +629,7 @@ impl<K: DepKind> DepGraphData<K> {
         if let Some(prev_index) = self.previous.node_to_index_opt(dep_node) {
             self.current.prev_index_to_index.lock()[prev_index]
         } else {
-            self.current
-                .new_node_to_index
-                .get_shard_by_value(dep_node)
-                .lock()
-                .get(dep_node)
-                .copied()
+            self.current.new_node_to_index.lock_shard_by_value(dep_node).get(dep_node).copied()
         }
     }
 
@@ -872,7 +867,7 @@ impl<K: DepKind> DepGraphData<K> {
 
         let prev_deps = self.previous.edge_targets_from(prev_dep_node_index);
 
-        for &dep_dep_node_index in prev_deps {
+        for dep_dep_node_index in prev_deps {
             self.try_mark_parent_green(qcx, dep_dep_node_index, dep_node, Some(&frame))?;
         }
 
@@ -1201,8 +1196,7 @@ impl<K: DepKind> CurrentDepGraph<K> {
         edges: EdgesVec,
         current_fingerprint: Fingerprint,
     ) -> DepNodeIndex {
-        let dep_node_index = match self.new_node_to_index.get_shard_by_value(&key).lock().entry(key)
-        {
+        let dep_node_index = match self.new_node_to_index.lock_shard_by_value(&key).entry(key) {
             Entry::Occupied(entry) => *entry.get(),
             Entry::Vacant(entry) => {
                 let dep_node_index =
@@ -1308,8 +1302,7 @@ impl<K: DepKind> CurrentDepGraph<K> {
                 let key = prev_graph.index_to_node(prev_index);
                 let edges = prev_graph
                     .edge_targets_from(prev_index)
-                    .iter()
-                    .map(|i| prev_index_to_index[*i].unwrap())
+                    .map(|i| prev_index_to_index[i].unwrap())
                     .collect();
                 let fingerprint = prev_graph.fingerprint_by_index(prev_index);
                 let dep_node_index = self.encoder.borrow().send(profiler, key, fingerprint, edges);
@@ -1329,16 +1322,12 @@ impl<K: DepKind> CurrentDepGraph<K> {
     ) {
         let node = &prev_graph.index_to_node(prev_index);
         debug_assert!(
-            !self.new_node_to_index.get_shard_by_value(node).lock().contains_key(node),
+            !self.new_node_to_index.lock_shard_by_value(node).contains_key(node),
             "node from previous graph present in new node collection"
         );
     }
 }
 
-/// The capacity of the `reads` field `SmallVec`
-const TASK_DEPS_READS_CAP: usize = 8;
-type EdgesVec = SmallVec<[DepNodeIndex; TASK_DEPS_READS_CAP]>;
-
 #[derive(Debug, Clone, Copy)]
 pub enum TaskDepsRef<'a, K: DepKind> {
     /// New dependencies can be added to the
diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs
index 0fd9e35d6dc..272028d35bf 100644
--- a/compiler/rustc_query_system/src/dep_graph/mod.rs
+++ b/compiler/rustc_query_system/src/dep_graph/mod.rs
@@ -1,10 +1,12 @@
 pub mod debug;
 mod dep_node;
+mod edges;
 mod graph;
 mod query;
 mod serialized;
 
 pub use dep_node::{DepKindStruct, DepNode, DepNodeParams, WorkProductId};
+pub use edges::EdgesVec;
 pub use graph::{
     hash_result, DepGraph, DepGraphData, DepNodeColor, DepNodeIndex, TaskDeps, TaskDepsRef,
     WorkProduct, WorkProductMap,
@@ -157,4 +159,10 @@ pub trait DepKind: Copy + fmt::Debug + Eq + Hash + Send + Encodable<FileEncoder>
     fn read_deps<OP>(op: OP)
     where
         OP: for<'a> FnOnce(TaskDepsRef<'a, Self>);
+
+    fn from_u16(u: u16) -> Self;
+
+    fn to_u16(self) -> u16;
+
+    const MAX: u16;
 }
diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs
index edddfda6242..213e5c8ba68 100644
--- a/compiler/rustc_query_system/src/dep_graph/serialized.rs
+++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs
@@ -1,6 +1,6 @@
 //! The data that we will serialize and deserialize.
 //!
-//! The dep-graph is serialized as a sequence of NodeInfo, with the dependencies
+//! Notionally, the dep-graph is a sequence of NodeInfo with the dependencies
 //! specified inline. The total number of nodes and edges are stored as the last
 //! 16 bytes of the file, so we can find them easily at decoding time.
 //!
@@ -11,17 +11,44 @@
 //! sequence of NodeInfos to the different arrays in SerializedDepGraph. Since the
 //! node and edge count are stored at the end of the file, all the arrays can be
 //! pre-allocated with the right length.
+//!
+//! The encoding of the de-pgraph is generally designed around the fact that fixed-size
+//! reads of encoded data are generally faster than variable-sized reads. Ergo we adopt
+//! essentially the same varint encoding scheme used in the rmeta format; the edge lists
+//! for each node on the graph store a 2-bit integer which is the number of bytes per edge
+//! index in that node's edge list. We effectively ignore that an edge index of 0 could be
+//! encoded with 0 bytes in order to not require 3 bits to store the byte width of the edges.
+//! The overhead of calculating the correct byte width for each edge is mitigated by
+//! building edge lists with [`EdgesVec`] which keeps a running max of the edges in a node.
+//!
+//! When we decode this data, we do not immediately create [`SerializedDepNodeIndex`] and
+//! instead keep the data in its denser serialized form which lets us turn our on-disk size
+//! efficiency directly into a peak memory reduction. When we convert these encoded-in-memory
+//! values into their fully-deserialized type, we use a fixed-size read of the encoded array
+//! then mask off any errant bytes we read. The array of edge index bytes is padded to permit this.
+//!
+//! We also encode and decode the entire rest of each node using [`SerializedNodeHeader`]
+//! to let this encoding and decoding be done in one fixed-size operation. These headers contain
+//! two [`Fingerprint`]s along with the serialized [`DepKind`], and the number of edge indices
+//! in the node and the number of bytes used to encode the edge indices for this node. The
+//! [`DepKind`], number of edges, and bytes per edge are all bit-packed together, if they fit.
+//! If the number of edges in this node does not fit in the bits available in the header, we
+//! store it directly after the header with leb128.
 
 use super::query::DepGraphQuery;
 use super::{DepKind, DepNode, DepNodeIndex};
+use crate::dep_graph::EdgesVec;
 use rustc_data_structures::fingerprint::Fingerprint;
+use rustc_data_structures::fingerprint::PackedFingerprint;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::profiling::SelfProfilerRef;
 use rustc_data_structures::sync::Lock;
+use rustc_data_structures::unhash::UnhashMap;
 use rustc_index::{Idx, IndexVec};
 use rustc_serialize::opaque::{FileEncodeResult, FileEncoder, IntEncodedWithFixedSize, MemDecoder};
-use rustc_serialize::{Decodable, Decoder, Encodable};
-use smallvec::SmallVec;
+use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
+use std::iter;
+use std::marker::PhantomData;
 
 // The maximum value of `SerializedDepNodeIndex` leaves the upper two bits
 // unused so that we can store multiple index types in `CompressedHybridIndex`,
@@ -31,6 +58,16 @@ rustc_index::newtype_index! {
     pub struct SerializedDepNodeIndex {}
 }
 
+const DEP_NODE_SIZE: usize = std::mem::size_of::<SerializedDepNodeIndex>();
+/// Amount of padding we need to add to the edge list data so that we can retrieve every
+/// SerializedDepNodeIndex with a fixed-size read then mask.
+const DEP_NODE_PAD: usize = DEP_NODE_SIZE - 1;
+/// Number of bits we need to store the number of used bytes in a SerializedDepNodeIndex.
+/// Note that wherever we encode byte widths like this we actually store the number of bytes used
+/// minus 1; for a 4-byte value we technically would have 5 widths to store, but using one byte to
+/// store zeroes (which are relatively rare) is a decent tradeoff to save a bit in our bitfields.
+const DEP_NODE_WIDTH_BITS: usize = DEP_NODE_SIZE / 2;
+
 /// Data for use when recompiling the **current crate**.
 #[derive(Debug)]
 pub struct SerializedDepGraph<K: DepKind> {
@@ -42,12 +79,13 @@ pub struct SerializedDepGraph<K: DepKind> {
     /// For each DepNode, stores the list of edges originating from that
     /// DepNode. Encoded as a [start, end) pair indexing into edge_list_data,
     /// which holds the actual DepNodeIndices of the target nodes.
-    edge_list_indices: IndexVec<SerializedDepNodeIndex, (u32, u32)>,
-    /// A flattened list of all edge targets in the graph. Edge sources are
-    /// implicit in edge_list_indices.
-    edge_list_data: Vec<SerializedDepNodeIndex>,
-    /// Reciprocal map to `nodes`.
-    index: FxHashMap<DepNode<K>, SerializedDepNodeIndex>,
+    edge_list_indices: IndexVec<SerializedDepNodeIndex, EdgeHeader>,
+    /// A flattened list of all edge targets in the graph, stored in the same
+    /// varint encoding that we use on disk. Edge sources are implicit in edge_list_indices.
+    edge_list_data: Vec<u8>,
+    /// Stores a map from fingerprints to nodes per dep node kind.
+    /// This is the reciprocal of `nodes`.
+    index: Vec<UnhashMap<PackedFingerprint, SerializedDepNodeIndex>>,
 }
 
 impl<K: DepKind> Default for SerializedDepGraph<K> {
@@ -64,9 +102,35 @@ impl<K: DepKind> Default for SerializedDepGraph<K> {
 
 impl<K: DepKind> SerializedDepGraph<K> {
     #[inline]
-    pub fn edge_targets_from(&self, source: SerializedDepNodeIndex) -> &[SerializedDepNodeIndex] {
-        let targets = self.edge_list_indices[source];
-        &self.edge_list_data[targets.0 as usize..targets.1 as usize]
+    pub fn edge_targets_from(
+        &self,
+        source: SerializedDepNodeIndex,
+    ) -> impl Iterator<Item = SerializedDepNodeIndex> + '_ {
+        let header = self.edge_list_indices[source];
+        let mut raw = &self.edge_list_data[header.start()..];
+        // Figure out where the edge list for `source` ends by getting the start index of the next
+        // edge list, or the end of the array if this is the last edge.
+        let end = self
+            .edge_list_indices
+            .get(source + 1)
+            .map(|h| h.start())
+            .unwrap_or_else(|| self.edge_list_data.len() - DEP_NODE_PAD);
+
+        // The number of edges for this node is implicitly stored in the combination of the byte
+        // width and the length.
+        let bytes_per_index = header.bytes_per_index();
+        let len = (end - header.start()) / bytes_per_index;
+
+        // LLVM doesn't hoist EdgeHeader::mask so we do it ourselves.
+        let mask = header.mask();
+        (0..len).map(move |_| {
+            // Doing this slicing in this order ensures that the first bounds check suffices for
+            // all the others.
+            let index = &raw[..DEP_NODE_SIZE];
+            raw = &raw[bytes_per_index..];
+            let index = u32::from_le_bytes(index.try_into().unwrap()) & mask;
+            SerializedDepNodeIndex::from_u32(index)
+        })
     }
 
     #[inline]
@@ -76,7 +140,7 @@ impl<K: DepKind> SerializedDepGraph<K> {
 
     #[inline]
     pub fn node_to_index_opt(&self, dep_node: &DepNode<K>) -> Option<SerializedDepNodeIndex> {
-        self.index.get(dep_node).cloned()
+        self.index.get(dep_node.kind.to_u16() as usize)?.get(&dep_node.hash).cloned()
     }
 
     #[inline]
@@ -84,9 +148,40 @@ impl<K: DepKind> SerializedDepGraph<K> {
         self.fingerprints[dep_node_index]
     }
 
+    #[inline]
     pub fn node_count(&self) -> usize {
-        self.index.len()
+        self.nodes.len()
+    }
+}
+
+/// A packed representation of an edge's start index and byte width.
+///
+/// This is packed by stealing 2 bits from the start index, which means we only accomodate edge
+/// data arrays up to a quarter of our address space. Which seems fine.
+#[derive(Debug, Clone, Copy)]
+struct EdgeHeader {
+    repr: usize,
+}
+
+impl EdgeHeader {
+    #[inline]
+    fn start(self) -> usize {
+        self.repr >> DEP_NODE_WIDTH_BITS
+    }
+
+    #[inline]
+    fn bytes_per_index(self) -> usize {
+        (self.repr & mask(DEP_NODE_WIDTH_BITS)) + 1
     }
+
+    #[inline]
+    fn mask(self) -> u32 {
+        mask(self.bytes_per_index() * 8) as u32
+    }
+}
+
+fn mask(bits: usize) -> usize {
+    usize::MAX >> ((std::mem::size_of::<usize>() * 8) - bits)
 }
 
 impl<'a, K: DepKind + Decodable<MemDecoder<'a>>> Decodable<MemDecoder<'a>>
@@ -107,44 +202,220 @@ impl<'a, K: DepKind + Decodable<MemDecoder<'a>>> Decodable<MemDecoder<'a>>
 
         debug!(?node_count, ?edge_count);
 
+        let graph_bytes = d.len() - (2 * IntEncodedWithFixedSize::ENCODED_SIZE) - d.position();
+
         let mut nodes = IndexVec::with_capacity(node_count);
         let mut fingerprints = IndexVec::with_capacity(node_count);
         let mut edge_list_indices = IndexVec::with_capacity(node_count);
-        let mut edge_list_data = Vec::with_capacity(edge_count);
+        // This estimation assumes that all of the encoded bytes are for the edge lists or for the
+        // fixed-size node headers. But that's not necessarily true; if any edge list has a length
+        // that spills out of the size we can bit-pack into SerializedNodeHeader then some of the
+        // total serialized size is also used by leb128-encoded edge list lengths. Neglecting that
+        // contribution to graph_bytes means our estimation of the bytes needed for edge_list_data
+        // slightly overshoots. But it cannot overshoot by much; consider that the worse case is
+        // for a node with length 64, which means the spilled 1-byte leb128 length is 1 byte of at
+        // least (34 byte header + 1 byte len + 64 bytes edge data), which is ~1%. A 2-byte leb128
+        // length is about the same fractional overhead and it amortizes for yet greater lengths.
+        let mut edge_list_data = Vec::with_capacity(
+            graph_bytes - node_count * std::mem::size_of::<SerializedNodeHeader<K>>(),
+        );
 
         for _index in 0..node_count {
-            let dep_node: DepNode<K> = Decodable::decode(d);
-            let _i: SerializedDepNodeIndex = nodes.push(dep_node);
+            // Decode the header for this edge; the header packs together as many of the fixed-size
+            // fields as possible to limit the number of times we update decoder state.
+            let node_header =
+                SerializedNodeHeader::<K> { bytes: d.read_array(), _marker: PhantomData };
+
+            let _i: SerializedDepNodeIndex = nodes.push(node_header.node());
             debug_assert_eq!(_i.index(), _index);
 
-            let fingerprint: Fingerprint = Decodable::decode(d);
-            let _i: SerializedDepNodeIndex = fingerprints.push(fingerprint);
+            let _i: SerializedDepNodeIndex = fingerprints.push(node_header.fingerprint());
             debug_assert_eq!(_i.index(), _index);
 
-            // Deserialize edges -- sequence of DepNodeIndex
-            let len = d.read_usize();
-            let start = edge_list_data.len().try_into().unwrap();
-            for _ in 0..len {
-                let edge = Decodable::decode(d);
-                edge_list_data.push(edge);
-            }
-            let end = edge_list_data.len().try_into().unwrap();
-            let _i: SerializedDepNodeIndex = edge_list_indices.push((start, end));
+            // If the length of this node's edge list is small, the length is stored in the header.
+            // If it is not, we fall back to another decoder call.
+            let num_edges = node_header.len().unwrap_or_else(|| d.read_usize());
+
+            // The edges index list uses the same varint strategy as rmeta tables; we select the
+            // number of byte elements per-array not per-element. This lets us read the whole edge
+            // list for a node with one decoder call and also use the on-disk format in memory.
+            let edges_len_bytes = node_header.bytes_per_index() * num_edges;
+            // The in-memory structure for the edges list stores the byte width of the edges on
+            // this node with the offset into the global edge data array.
+            let edges_header = node_header.edges_header(&edge_list_data);
+
+            edge_list_data.extend(d.read_raw_bytes(edges_len_bytes));
+
+            let _i: SerializedDepNodeIndex = edge_list_indices.push(edges_header);
             debug_assert_eq!(_i.index(), _index);
         }
 
-        let index: FxHashMap<_, _> =
-            nodes.iter_enumerated().map(|(idx, &dep_node)| (dep_node, idx)).collect();
+        // When we access the edge list data, we do a fixed-size read from the edge list data then
+        // mask off the bytes that aren't for that edge index, so the last read may dangle off the
+        // end of the array. This padding ensure it doesn't.
+        edge_list_data.extend(&[0u8; DEP_NODE_PAD]);
+
+        // Read the number of each dep kind and use it to create an hash map with a suitable size.
+        let mut index: Vec<_> = (0..(K::MAX as usize + 1))
+            .map(|_| UnhashMap::with_capacity_and_hasher(d.read_u32() as usize, Default::default()))
+            .collect();
+
+        for (idx, node) in nodes.iter_enumerated() {
+            index[node.kind.to_u16() as usize].insert(node.hash, idx);
+        }
 
         SerializedDepGraph { nodes, fingerprints, edge_list_indices, edge_list_data, index }
     }
 }
 
-#[derive(Debug, Encodable, Decodable)]
-pub struct NodeInfo<K: DepKind> {
+/// A packed representation of all the fixed-size fields in a `NodeInfo`.
+///
+/// This stores in one byte array:
+/// * The `Fingerprint` in the `NodeInfo`
+/// * The `Fingerprint` in `DepNode` that is in this `NodeInfo`
+/// * The `DepKind`'s discriminant (a u16, but not all bits are used...)
+/// * The byte width of the encoded edges for this node
+/// * In whatever bits remain, the length of the edge list for this node, if it fits
+struct SerializedNodeHeader<K> {
+    // 2 bytes for the DepNode
+    // 16 for Fingerprint in DepNode
+    // 16 for Fingerprint in NodeInfo
+    bytes: [u8; 34],
+    _marker: PhantomData<K>,
+}
+
+// The fields of a `SerializedNodeHeader`, this struct is an implementation detail and exists only
+// to make the implementation of `SerializedNodeHeader` simpler.
+struct Unpacked<K> {
+    len: Option<usize>,
+    bytes_per_index: usize,
+    kind: K,
+    hash: PackedFingerprint,
+    fingerprint: Fingerprint,
+}
+
+// Bit fields, where
+// M: bits used to store the length of a node's edge list
+// N: bits used to store the byte width of elements of the edge list
+// are
+// 0..M    length of the edge
+// M..M+N  bytes per index
+// M+N..16 kind
+impl<K: DepKind> SerializedNodeHeader<K> {
+    const TOTAL_BITS: usize = std::mem::size_of::<K>() * 8;
+    const LEN_BITS: usize = Self::TOTAL_BITS - Self::KIND_BITS - Self::WIDTH_BITS;
+    const WIDTH_BITS: usize = DEP_NODE_WIDTH_BITS;
+    const KIND_BITS: usize = Self::TOTAL_BITS - K::MAX.leading_zeros() as usize;
+    const MAX_INLINE_LEN: usize = (u16::MAX as usize >> (Self::TOTAL_BITS - Self::LEN_BITS)) - 1;
+
+    #[inline]
+    fn new(node_info: &NodeInfo<K>) -> Self {
+        debug_assert_eq!(Self::TOTAL_BITS, Self::LEN_BITS + Self::WIDTH_BITS + Self::KIND_BITS);
+
+        let NodeInfo { node, fingerprint, edges } = node_info;
+
+        let mut head = node.kind.to_u16();
+
+        let free_bytes = edges.max_index().leading_zeros() as usize / 8;
+        let bytes_per_index = (DEP_NODE_SIZE - free_bytes).saturating_sub(1);
+        head |= (bytes_per_index as u16) << Self::KIND_BITS;
+
+        // Encode number of edges + 1 so that we can reserve 0 to indicate that the len doesn't fit
+        // in this bitfield.
+        if edges.len() <= Self::MAX_INLINE_LEN {
+            head |= (edges.len() as u16 + 1) << (Self::KIND_BITS + Self::WIDTH_BITS);
+        }
+
+        let hash: Fingerprint = node.hash.into();
+
+        // Using half-open ranges ensures an unconditional panic if we get the magic numbers wrong.
+        let mut bytes = [0u8; 34];
+        bytes[..2].copy_from_slice(&head.to_le_bytes());
+        bytes[2..18].copy_from_slice(&hash.to_le_bytes());
+        bytes[18..].copy_from_slice(&fingerprint.to_le_bytes());
+
+        #[cfg(debug_assertions)]
+        {
+            let res = Self { bytes, _marker: PhantomData };
+            assert_eq!(node_info.fingerprint, res.fingerprint());
+            assert_eq!(node_info.node, res.node());
+            if let Some(len) = res.len() {
+                assert_eq!(node_info.edges.len(), len);
+            }
+        }
+        Self { bytes, _marker: PhantomData }
+    }
+
+    #[inline]
+    fn unpack(&self) -> Unpacked<K> {
+        let head = u16::from_le_bytes(self.bytes[..2].try_into().unwrap());
+        let hash = self.bytes[2..18].try_into().unwrap();
+        let fingerprint = self.bytes[18..].try_into().unwrap();
+
+        let kind = head & mask(Self::KIND_BITS) as u16;
+        let bytes_per_index = (head >> Self::KIND_BITS) & mask(Self::WIDTH_BITS) as u16;
+        let len = (head as usize) >> (Self::WIDTH_BITS + Self::KIND_BITS);
+
+        Unpacked {
+            len: len.checked_sub(1),
+            bytes_per_index: bytes_per_index as usize + 1,
+            kind: DepKind::from_u16(kind),
+            hash: Fingerprint::from_le_bytes(hash).into(),
+            fingerprint: Fingerprint::from_le_bytes(fingerprint),
+        }
+    }
+
+    #[inline]
+    fn len(&self) -> Option<usize> {
+        self.unpack().len
+    }
+
+    #[inline]
+    fn bytes_per_index(&self) -> usize {
+        self.unpack().bytes_per_index
+    }
+
+    #[inline]
+    fn fingerprint(&self) -> Fingerprint {
+        self.unpack().fingerprint
+    }
+
+    #[inline]
+    fn node(&self) -> DepNode<K> {
+        let Unpacked { kind, hash, .. } = self.unpack();
+        DepNode { kind, hash }
+    }
+
+    #[inline]
+    fn edges_header(&self, edge_list_data: &[u8]) -> EdgeHeader {
+        EdgeHeader {
+            repr: (edge_list_data.len() << DEP_NODE_WIDTH_BITS) | (self.bytes_per_index() - 1),
+        }
+    }
+}
+
+#[derive(Debug)]
+struct NodeInfo<K: DepKind> {
     node: DepNode<K>,
     fingerprint: Fingerprint,
-    edges: SmallVec<[DepNodeIndex; 8]>,
+    edges: EdgesVec,
+}
+
+impl<K: DepKind> Encodable<FileEncoder> for NodeInfo<K> {
+    fn encode(&self, e: &mut FileEncoder) {
+        let header = SerializedNodeHeader::new(self);
+        e.emit_raw_bytes(&header.bytes);
+
+        if header.len().is_none() {
+            e.emit_usize(self.edges.len());
+        }
+
+        let bytes_per_index = header.bytes_per_index();
+        for node_index in self.edges.iter() {
+            let bytes = node_index.as_u32().to_le_bytes();
+            e.emit_raw_bytes(&bytes[..bytes_per_index]);
+        }
+    }
 }
 
 struct Stat<K: DepKind> {
@@ -158,6 +429,9 @@ struct EncoderState<K: DepKind> {
     total_node_count: usize,
     total_edge_count: usize,
     stats: Option<FxHashMap<K, Stat<K>>>,
+
+    /// Stores the number of times we've encoded each dep kind.
+    kind_stats: Vec<u32>,
 }
 
 impl<K: DepKind> EncoderState<K> {
@@ -167,6 +441,7 @@ impl<K: DepKind> EncoderState<K> {
             total_edge_count: 0,
             total_node_count: 0,
             stats: record_stats.then(FxHashMap::default),
+            kind_stats: iter::repeat(0).take(K::MAX as usize + 1).collect(),
         }
     }
 
@@ -177,6 +452,7 @@ impl<K: DepKind> EncoderState<K> {
     ) -> DepNodeIndex {
         let index = DepNodeIndex::new(self.total_node_count);
         self.total_node_count += 1;
+        self.kind_stats[node.node.kind.to_u16() as usize] += 1;
 
         let edge_count = node.edges.len();
         self.total_edge_count += edge_count;
@@ -202,11 +478,16 @@ impl<K: DepKind> EncoderState<K> {
     }
 
     fn finish(self, profiler: &SelfProfilerRef) -> FileEncodeResult {
-        let Self { mut encoder, total_node_count, total_edge_count, stats: _ } = self;
+        let Self { mut encoder, total_node_count, total_edge_count, stats: _, kind_stats } = self;
 
         let node_count = total_node_count.try_into().unwrap();
         let edge_count = total_edge_count.try_into().unwrap();
 
+        // Encode the number of each dep kind encountered
+        for count in kind_stats.iter() {
+            count.encode(&mut encoder);
+        }
+
         debug!(?node_count, ?edge_count);
         debug!("position: {:?}", encoder.position());
         IntEncodedWithFixedSize(node_count).encode(&mut encoder);
@@ -303,7 +584,7 @@ impl<K: DepKind + Encodable<FileEncoder>> GraphEncoder<K> {
         profiler: &SelfProfilerRef,
         node: DepNode<K>,
         fingerprint: Fingerprint,
-        edges: SmallVec<[DepNodeIndex; 8]>,
+        edges: EdgesVec,
     ) -> DepNodeIndex {
         let _prof_timer = profiler.generic_activity("incr_comp_encode_dep_graph");
         let node = NodeInfo { node, fingerprint, edges };
diff --git a/compiler/rustc_query_system/src/ich/impls_syntax.rs b/compiler/rustc_query_system/src/ich/impls_syntax.rs
index f2b13f95def..b2177be0e36 100644
--- a/compiler/rustc_query_system/src/ich/impls_syntax.rs
+++ b/compiler/rustc_query_system/src/ich/impls_syntax.rs
@@ -79,15 +79,16 @@ impl<'a> HashStable<StableHashingContext<'a>> for SourceFile {
 
         src_hash.hash_stable(hcx, hasher);
 
-        // We are always in `Lines` form by the time we reach here.
-        assert!(self.lines.borrow().is_lines());
-        self.lines(|lines| {
+        {
+            // We are always in `Lines` form by the time we reach here.
+            assert!(self.lines.read().is_lines());
+            let lines = self.lines();
             // We only hash the relative position within this source_file
             lines.len().hash_stable(hcx, hasher);
             for &line in lines.iter() {
                 line.hash_stable(hcx, hasher);
             }
-        });
+        }
 
         // We only hash the relative position within this source_file
         multibyte_chars.len().hash_stable(hcx, hasher);
diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs
index 8c9e9cfad60..1944ac443ea 100644
--- a/compiler/rustc_query_system/src/lib.rs
+++ b/compiler/rustc_query_system/src/lib.rs
@@ -4,6 +4,7 @@
 #![feature(min_specialization)]
 #![feature(extern_types)]
 #![feature(let_chains)]
+#![feature(inline_const)]
 #![allow(rustc::potential_query_instability)]
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
diff --git a/compiler/rustc_query_system/src/query/caches.rs b/compiler/rustc_query_system/src/query/caches.rs
index d8aa377af42..0240f012da0 100644
--- a/compiler/rustc_query_system/src/query/caches.rs
+++ b/compiler/rustc_query_system/src/query/caches.rs
@@ -55,7 +55,7 @@ where
     #[inline(always)]
     fn lookup(&self, key: &K) -> Option<(V, DepNodeIndex)> {
         let key_hash = sharded::make_hash(key);
-        let lock = self.cache.get_shard_by_hash(key_hash).lock();
+        let lock = self.cache.lock_shard_by_hash(key_hash);
         let result = lock.raw_entry().from_key_hashed_nocheck(key_hash, key);
 
         if let Some((_, value)) = result { Some(*value) } else { None }
@@ -63,7 +63,7 @@ where
 
     #[inline]
     fn complete(&self, key: K, value: V, index: DepNodeIndex) {
-        let mut lock = self.cache.get_shard_by_value(&key).lock();
+        let mut lock = self.cache.lock_shard_by_value(&key);
         // We may be overwriting another value. This is all right, since the dep-graph
         // will check that the fingerprint matches.
         lock.insert(key, (value, index));
@@ -148,13 +148,13 @@ where
 
     #[inline(always)]
     fn lookup(&self, key: &K) -> Option<(V, DepNodeIndex)> {
-        let lock = self.cache.get_shard_by_hash(key.index() as u64).lock();
+        let lock = self.cache.lock_shard_by_hash(key.index() as u64);
         if let Some(Some(value)) = lock.get(*key) { Some(*value) } else { None }
     }
 
     #[inline]
     fn complete(&self, key: K, value: V, index: DepNodeIndex) {
-        let mut lock = self.cache.get_shard_by_hash(key.index() as u64).lock();
+        let mut lock = self.cache.lock_shard_by_hash(key.index() as u64);
         lock.insert(key, (value, index));
     }
 
diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs
index 9cba549a3b5..6c01dc3c00d 100644
--- a/compiler/rustc_query_system/src/query/job.rs
+++ b/compiler/rustc_query_system/src/query/job.rs
@@ -552,7 +552,9 @@ pub fn deadlock<D: DepKind>(query_map: QueryMap<D>, registry: &rayon_core::Regis
     // which in turn will wait on X causing a deadlock. We have a false dependency from
     // X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here
     // only considers the true dependency and won't detect a cycle.
-    assert!(found_cycle);
+    if !found_cycle {
+        panic!("deadlock detected");
+    }
 
     // FIXME: Ensure this won't cause a deadlock before we return
     for waiter in wakelist.into_iter() {
diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs
index 4fa168965a7..07db15e6d8b 100644
--- a/compiler/rustc_query_system/src/query/plumbing.rs
+++ b/compiler/rustc_query_system/src/query/plumbing.rs
@@ -158,7 +158,7 @@ where
         cache.complete(key, result, dep_node_index);
 
         let job = {
-            let mut lock = state.active.get_shard_by_value(&key).lock();
+            let mut lock = state.active.lock_shard_by_value(&key);
             match lock.remove(&key).unwrap() {
                 QueryResult::Started(job) => job,
                 QueryResult::Poisoned => panic!(),
@@ -180,7 +180,7 @@ where
         // Poison the query so jobs waiting on it panic.
         let state = self.state;
         let job = {
-            let mut shard = state.active.get_shard_by_value(&self.key).lock();
+            let mut shard = state.active.lock_shard_by_value(&self.key);
             let job = match shard.remove(&self.key).unwrap() {
                 QueryResult::Started(job) => job,
                 QueryResult::Poisoned => panic!(),
@@ -303,7 +303,7 @@ where
     Qcx: QueryContext,
 {
     let state = query.query_state(qcx);
-    let mut state_lock = state.active.get_shard_by_value(&key).lock();
+    let mut state_lock = state.active.lock_shard_by_value(&key);
 
     // For the parallel compiler we need to check both the query cache and query state structures
     // while holding the state lock to ensure that 1) the query has not yet completed and 2) the
diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl
index f98918cba88..9a970f5db39 100644
--- a/compiler/rustc_resolve/messages.ftl
+++ b/compiler/rustc_resolve/messages.ftl
@@ -58,15 +58,16 @@ resolve_cannot_determine_import_resolution =
     cannot determine resolution for the import
     .note = import resolution is stuck, try simplifying other imports
 
+resolve_cannot_determine_macro_resolution =
+    cannot determine resolution for the {$kind} `{$path}`
+    .note = import resolution is stuck, try simplifying macro imports
+
 resolve_cannot_find_ident_in_this_scope =
     cannot find {$expected} `{$ident}` in this scope
 
 resolve_cannot_glob_import_possible_crates =
     cannot glob-import all possible crates
 
-resolve_cannot_use_self_type_here =
-    can't use `Self` here
-
 resolve_change_import_binding =
     you can use `as` to change the binding name of the import
 
@@ -86,9 +87,6 @@ resolve_const_not_member_of_trait =
     const `{$const_}` is not a member of trait `{$trait_}`
     .label = not a member of trait `{$trait_}`
 
-resolve_const_param_from_outer_fn =
-    const parameter from outer function
-
 resolve_const_param_in_enum_discriminant =
     const parameters may not be used in enum discriminant values
 
@@ -115,10 +113,19 @@ resolve_forward_declared_generic_param =
     generic parameters with a default cannot use forward declared identifiers
     .label = defaulted generic parameters cannot be forward declared
 
-resolve_generic_params_from_outer_function =
-    can't use generic parameters from outer function
-    .label = use of generic parameter from outer function
-    .suggestion = try using a local generic parameter instead
+resolve_generic_params_from_outer_item =
+    can't use generic parameters from outer item
+    .label = use of generic parameter from outer item
+    .refer_to_type_directly = refer to the type directly here instead
+    .suggestion = try introducing a local generic parameter here
+
+resolve_generic_params_from_outer_item_const_param = const parameter from outer item
+
+resolve_generic_params_from_outer_item_self_ty_alias = `Self` type implicitly declared here, by this `impl`
+
+resolve_generic_params_from_outer_item_self_ty_param = can't use `Self` here
+
+resolve_generic_params_from_outer_item_ty_param = type parameter from outer item
 
 resolve_glob_import_doesnt_reexport =
     glob import doesn't reexport anything because no candidate is public enough
@@ -273,9 +280,6 @@ resolve_type_not_member_of_trait =
     type `{$type_}` is not a member of trait `{$trait_}`
     .label = not a member of trait `{$trait_}`
 
-resolve_type_param_from_outer_fn =
-    type parameter from outer function
-
 resolve_type_param_in_enum_discriminant =
     type parameters may not be used in enum discriminant values
 
@@ -311,9 +315,6 @@ resolve_unreachable_label_suggestion_use_similarly_named =
 resolve_unreachable_label_with_similar_name_exists =
     a label with a similar name exists but is unreachable
 
-resolve_use_a_type_here_instead =
-    use a type here instead
-
 resolve_variable_bound_with_different_mode =
     variable `{$variable_name}` is bound inconsistently across alternatives separated by `|`
     .label = bound in different ways
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 30db450870b..2c36c83ae72 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -1239,7 +1239,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
                     use_span_with_attributes: span,
                     use_span: span,
                     root_span: span,
-                    span: span,
+                    span,
                     module_path: Vec::new(),
                     vis: Cell::new(Some(vis)),
                     used: Cell::new(true),
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index d99fc07a7cd..9b7fd76d103 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -553,43 +553,40 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         resolution_error: ResolutionError<'a>,
     ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
         match resolution_error {
-            ResolutionError::GenericParamsFromOuterFunction(outer_res, has_generic_params) => {
-                let mut err = struct_span_err!(
-                    self.tcx.sess,
+            ResolutionError::GenericParamsFromOuterItem(outer_res, has_generic_params) => {
+                use errs::GenericParamsFromOuterItemLabel as Label;
+                let mut err = errs::GenericParamsFromOuterItem {
                     span,
-                    E0401,
-                    "can't use generic parameters from outer function",
-                );
-                err.span_label(span, "use of generic parameter from outer function");
+                    label: None,
+                    refer_to_type_directly: None,
+                    sugg: None,
+                };
 
                 let sm = self.tcx.sess.source_map();
                 let def_id = match outer_res {
                     Res::SelfTyParam { .. } => {
-                        err.span_label(span, "can't use `Self` here");
-                        return err;
+                        err.label = Some(Label::SelfTyParam(span));
+                        return self.tcx.sess.create_err(err);
                     }
                     Res::SelfTyAlias { alias_to: def_id, .. } => {
-                        err.span_label(
-                            reduce_impl_span_to_impl_keyword(sm, self.def_span(def_id)),
-                            "`Self` type implicitly declared here, by this `impl`",
-                        );
-                        err.span_label(span, "use a type here instead");
-                        return err;
+                        err.label = Some(Label::SelfTyAlias(reduce_impl_span_to_impl_keyword(
+                            sm,
+                            self.def_span(def_id),
+                        )));
+                        err.refer_to_type_directly = Some(span);
+                        return self.tcx.sess.create_err(err);
                     }
                     Res::Def(DefKind::TyParam, def_id) => {
-                        err.span_label(self.def_span(def_id), "type parameter from outer function");
+                        err.label = Some(Label::TyParam(self.def_span(def_id)));
                         def_id
                     }
                     Res::Def(DefKind::ConstParam, def_id) => {
-                        err.span_label(
-                            self.def_span(def_id),
-                            "const parameter from outer function",
-                        );
+                        err.label = Some(Label::ConstParam(self.def_span(def_id)));
                         def_id
                     }
                     _ => {
                         bug!(
-                            "GenericParamsFromOuterFunction should only be used with \
+                            "GenericParamsFromOuterItem should only be used with \
                             Res::SelfTyParam, Res::SelfTyAlias, DefKind::TyParam or \
                             DefKind::ConstParam"
                         );
@@ -597,9 +594,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 };
 
                 if let HasGenericParams::Yes(span) = has_generic_params {
-                    // Try to retrieve the span of the function signature and generate a new
-                    // message with a local type or const parameter.
-                    let sugg_msg = "try using a local generic parameter instead";
                     let name = self.tcx.item_name(def_id);
                     let (span, snippet) = if span.is_empty() {
                         let snippet = format!("<{name}>");
@@ -609,11 +603,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                         let snippet = format!("{name}, ");
                         (span, snippet)
                     };
-                    // Suggest the modification to the user
-                    err.span_suggestion(span, sugg_msg, snippet, Applicability::MaybeIncorrect);
+                    err.sugg = Some(errs::GenericParamsFromOuterItemSugg { span, snippet });
                 }
 
-                err
+                self.tcx.sess.create_err(err)
             }
             ResolutionError::NameAlreadyUsedInParameterList(name, first_use_span) => self
                 .tcx
@@ -2753,7 +2746,13 @@ fn search_for_any_use_in_items(items: &[P<ast::Item>]) -> Option<Span> {
     for item in items {
         if let ItemKind::Use(..) = item.kind {
             if is_span_suitable_for_use_injection(item.span) {
-                return Some(item.span.shrink_to_lo());
+                let mut lo = item.span.lo();
+                for attr in &item.attrs {
+                    if attr.span.eq_ctxt(item.span) {
+                        lo = std::cmp::min(lo, attr.span.lo());
+                    }
+                }
+                return Some(Span::new(lo, lo, item.span.ctxt(), item.span.parent()));
             }
         }
     }
diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs
index e4b89c65853..72ff959bbd6 100644
--- a/compiler/rustc_resolve/src/errors.rs
+++ b/compiler/rustc_resolve/src/errors.rs
@@ -33,6 +33,40 @@ pub(crate) struct CrateRootNamesMustBeNamedExplicitly(#[primary_span] pub(crate)
 pub(crate) struct ResolutionError(#[primary_span] pub(crate) Span);
 
 #[derive(Diagnostic)]
+#[diag(resolve_generic_params_from_outer_item, code = "E0401")]
+pub(crate) struct GenericParamsFromOuterItem {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+    #[subdiagnostic]
+    pub(crate) label: Option<GenericParamsFromOuterItemLabel>,
+    #[label(resolve_refer_to_type_directly)]
+    pub(crate) refer_to_type_directly: Option<Span>,
+    #[subdiagnostic]
+    pub(crate) sugg: Option<GenericParamsFromOuterItemSugg>,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum GenericParamsFromOuterItemLabel {
+    #[label(resolve_generic_params_from_outer_item_self_ty_param)]
+    SelfTyParam(#[primary_span] Span),
+    #[label(resolve_generic_params_from_outer_item_self_ty_alias)]
+    SelfTyAlias(#[primary_span] Span),
+    #[label(resolve_generic_params_from_outer_item_ty_param)]
+    TyParam(#[primary_span] Span),
+    #[label(resolve_generic_params_from_outer_item_const_param)]
+    ConstParam(#[primary_span] Span),
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(resolve_suggestion, code = "{snippet}", applicability = "maybe-incorrect")]
+pub(crate) struct GenericParamsFromOuterItemSugg {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) snippet: String,
+}
+
+#[derive(Diagnostic)]
 #[diag(resolve_name_is_already_used_as_generic_parameter, code = "E0403")]
 pub(crate) struct NameAlreadyUsedInParameterList {
     #[primary_span]
@@ -655,6 +689,16 @@ pub(crate) struct CannotDetermineImportResolution {
 }
 
 #[derive(Diagnostic)]
+#[diag(resolve_cannot_determine_macro_resolution)]
+#[note]
+pub(crate) struct CannotDetermineMacroResolution {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) kind: &'static str,
+    pub(crate) path: String,
+}
+
+#[derive(Diagnostic)]
 #[diag(resolve_cannot_be_reexported_private, code = "E0364")]
 pub(crate) struct CannotBeReexportedPrivate {
     #[primary_span]
diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs
index 61e05b65f90..54388f80f15 100644
--- a/compiler/rustc_resolve/src/ident.rs
+++ b/compiler/rustc_resolve/src/ident.rs
@@ -972,9 +972,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         // progress, we have to ignore those potential unresolved invocations from other modules
         // and prohibit access to macro-expanded `macro_export` macros instead (unless restricted
         // shadowing is enabled, see `macro_expanded_macro_export_errors`).
-        let unexpanded_macros = !module.unexpanded_invocations.borrow().is_empty();
         if let Some(binding) = binding {
-            if !unexpanded_macros || ns == MacroNS || restricted_shadowing {
+            if binding.determined() || ns == MacroNS || restricted_shadowing {
                 return check_usable(self, binding);
             } else {
                 return Err((Undetermined, Weak::No));
@@ -991,7 +990,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
         // Check if one of unexpanded macros can still define the name,
         // if it can then our "no resolution" result is not determined and can be invalidated.
-        if unexpanded_macros {
+        if !module.unexpanded_invocations.borrow().is_empty() {
             return Err((Undetermined, Weak::Yes));
         }
 
@@ -1229,10 +1228,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     if let Some(span) = finalize {
                         self.report_error(
                             span,
-                            ResolutionError::GenericParamsFromOuterFunction(
-                                res,
-                                has_generic_params,
-                            ),
+                            ResolutionError::GenericParamsFromOuterItem(res, has_generic_params),
                         );
                     }
                     return Res::Err;
@@ -1296,10 +1292,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     if let Some(span) = finalize {
                         self.report_error(
                             span,
-                            ResolutionError::GenericParamsFromOuterFunction(
-                                res,
-                                has_generic_params,
-                            ),
+                            ResolutionError::GenericParamsFromOuterItem(res, has_generic_params),
                         );
                     }
                     return Res::Err;
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index a175d9f6c7f..820ac9ef61b 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -319,10 +319,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                             // We should replace the `old_binding` with `binding` regardless
                             // of whether they has same resolution or not when they are
                             // imported from the same glob-import statement.
-                            // However we currently using `Some(old_binding)` for back compact
-                            // purposes.
-                            // This case can be removed after once `Undetermined` is prepared
-                            // for glob-imports.
+                            resolution.binding = Some(binding);
                         } else if res != old_binding.res() {
                             let binding = if warn_ambiguity {
                                 this.warn_ambiguity(
@@ -805,13 +802,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     // For better failure detection, pretend that the import will
                     // not define any names while resolving its module path.
                     let orig_vis = import.vis.take();
-                    let binding = this.resolve_ident_in_module(
+                    let binding = this.maybe_resolve_ident_in_module(
                         module,
                         source,
                         ns,
                         &import.parent_scope,
-                        None,
-                        None,
                     );
                     import.vis.set(orig_vis);
                     source_bindings[ns].set(binding);
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 486d60eab21..01f9f060594 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -2450,7 +2450,11 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
             ItemKind::Const(box ast::ConstItem { ref generics, ref ty, ref expr, .. }) => {
                 self.with_generic_param_rib(
                     &generics.params,
-                    RibKind::Item(HasGenericParams::Yes(generics.span)),
+                    RibKind::Item(if self.r.tcx.features().generic_const_items {
+                        HasGenericParams::Yes(generics.span)
+                    } else {
+                        HasGenericParams::No
+                    }),
                     LifetimeRibKind::Generics {
                         binder: item.id,
                         kind: LifetimeBinderKind::ConstItem,
@@ -4176,7 +4180,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
                 self.resolve_expr(e, Some(&expr));
             }
 
-            ExprKind::Let(ref pat, ref scrutinee, _) => {
+            ExprKind::Let(ref pat, ref scrutinee, _, _) => {
                 self.visit_expr(scrutinee);
                 self.resolve_pattern_top(pat, PatternSource::Let);
             }
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index b757f42eaa0..949c6ab5ac0 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -34,7 +34,7 @@ use rustc_ast::{AngleBracketedArg, Crate, Expr, ExprKind, GenericArg, GenericArg
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_data_structures::intern::Interned;
 use rustc_data_structures::steal::Steal;
-use rustc_data_structures::sync::{Lrc, MappedReadGuard};
+use rustc_data_structures::sync::{FreezeReadGuard, Lrc};
 use rustc_errors::{
     Applicability, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed, SubdiagnosticMessage,
 };
@@ -186,8 +186,8 @@ struct BindingError {
 
 #[derive(Debug)]
 enum ResolutionError<'a> {
-    /// Error E0401: can't use type or const parameters from outer function.
-    GenericParamsFromOuterFunction(Res, HasGenericParams),
+    /// Error E0401: can't use type or const parameters from outer item.
+    GenericParamsFromOuterItem(Res, HasGenericParams),
     /// Error E0403: the name is already used for a type or const parameter in this generic
     /// parameter list.
     NameAlreadyUsedInParameterList(Symbol, Span),
@@ -881,6 +881,19 @@ impl<'a> NameBindingData<'a> {
             invoc_parent_expansion.is_descendant_of(self_parent_expansion);
         !(certainly_before_other_or_simultaneously || certainly_before_invoc_or_simultaneously)
     }
+
+    // Its purpose is to postpone the determination of a single binding because
+    // we can't predict whether it will be overwritten by recently expanded macros.
+    // FIXME: How can we integrate it with the `update_resolution`?
+    fn determined(&self) -> bool {
+        match &self.kind {
+            NameBindingKind::Import { binding, import, .. } if import.is_glob() => {
+                import.parent_scope.module.unexpanded_invocations.borrow().is_empty()
+                    && binding.determined()
+            }
+            _ => true,
+        }
+    }
 }
 
 #[derive(Default, Clone)]
@@ -1544,7 +1557,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         ))
     }
 
-    fn cstore(&self) -> MappedReadGuard<'_, CStore> {
+    fn cstore(&self) -> FreezeReadGuard<'_, CStore> {
         CStore::from_tcx(self.tcx)
     }
 
@@ -1599,7 +1612,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         });
 
         // Make sure we don't mutate the cstore from here on.
-        self.tcx.untracked().cstore.leak();
+        self.tcx.untracked().cstore.freeze();
     }
 
     fn traits_in_scope(
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 1199290a4d1..82060716575 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -2,7 +2,8 @@
 //! interface provided by `Resolver` to macro expander.
 
 use crate::errors::{
-    self, AddAsNonDerive, CannotFindIdentInThisScope, MacroExpectedFound, RemoveSurroundingDerive,
+    self, AddAsNonDerive, CannotDetermineMacroResolution, CannotFindIdentInThisScope,
+    MacroExpectedFound, RemoveSurroundingDerive,
 };
 use crate::Namespace::*;
 use crate::{BuiltinMacroState, Determinacy};
@@ -719,13 +720,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 // even if speculative `resolve_path` returned nothing previously, so we skip this
                 // less informative error if the privacy error is reported elsewhere.
                 if this.privacy_errors.is_empty() {
-                    let msg = format!(
-                        "cannot determine resolution for the {} `{}`",
-                        kind.descr(),
-                        Segment::names_to_string(path)
-                    );
-                    let msg_note = "import resolution is stuck, try simplifying macro imports";
-                    this.tcx.sess.struct_span_err(span, msg).note(msg_note).emit();
+                    this.tcx.sess.emit_err(CannotDetermineMacroResolution {
+                        span,
+                        kind: kind.descr(),
+                        path: Segment::names_to_string(path),
+                    });
                 }
             }
         };
diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs
index ba7417b6dda..7c41c32d022 100644
--- a/compiler/rustc_resolve/src/rustdoc.rs
+++ b/compiler/rustc_resolve/src/rustdoc.rs
@@ -2,9 +2,11 @@ use pulldown_cmark::{BrokenLink, CowStr, Event, LinkType, Options, Parser, Tag};
 use rustc_ast as ast;
 use rustc_ast::util::comments::beautify_doc_string;
 use rustc_data_structures::fx::FxHashMap;
+use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::DefId;
 use rustc_span::symbol::{kw, sym, Symbol};
-use rustc_span::Span;
+use rustc_span::{InnerSpan, Span, DUMMY_SP};
+use std::ops::Range;
 use std::{cmp, mem};
 
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
@@ -462,3 +464,88 @@ fn collect_link_data<'input, 'callback>(
 
     display_text.map(String::into_boxed_str)
 }
+
+/// Returns a span encompassing all the document fragments.
+pub fn span_of_fragments(fragments: &[DocFragment]) -> Option<Span> {
+    if fragments.is_empty() {
+        return None;
+    }
+    let start = fragments[0].span;
+    if start == DUMMY_SP {
+        return None;
+    }
+    let end = fragments.last().expect("no doc strings provided").span;
+    Some(start.to(end))
+}
+
+/// Attempts to match a range of bytes from parsed markdown to a `Span` in the source code.
+///
+/// This method will return `None` if we cannot construct a span from the source map or if the
+/// fragments are not all sugared doc comments. It's difficult to calculate the correct span in
+/// that case due to escaping and other source features.
+pub fn source_span_for_markdown_range(
+    tcx: TyCtxt<'_>,
+    markdown: &str,
+    md_range: &Range<usize>,
+    fragments: &[DocFragment],
+) -> Option<Span> {
+    let is_all_sugared_doc = fragments.iter().all(|frag| frag.kind == DocFragmentKind::SugaredDoc);
+
+    if !is_all_sugared_doc {
+        return None;
+    }
+
+    let snippet = tcx.sess.source_map().span_to_snippet(span_of_fragments(fragments)?).ok()?;
+
+    let starting_line = markdown[..md_range.start].matches('\n').count();
+    let ending_line = starting_line + markdown[md_range.start..md_range.end].matches('\n').count();
+
+    // We use `split_terminator('\n')` instead of `lines()` when counting bytes so that we treat
+    // CRLF and LF line endings the same way.
+    let mut src_lines = snippet.split_terminator('\n');
+    let md_lines = markdown.split_terminator('\n');
+
+    // The number of bytes from the source span to the markdown span that are not part
+    // of the markdown, like comment markers.
+    let mut start_bytes = 0;
+    let mut end_bytes = 0;
+
+    'outer: for (line_no, md_line) in md_lines.enumerate() {
+        loop {
+            let source_line = src_lines.next()?;
+            match source_line.find(md_line) {
+                Some(offset) => {
+                    if line_no == starting_line {
+                        start_bytes += offset;
+
+                        if starting_line == ending_line {
+                            break 'outer;
+                        }
+                    } else if line_no == ending_line {
+                        end_bytes += offset;
+                        break 'outer;
+                    } else if line_no < starting_line {
+                        start_bytes += source_line.len() - md_line.len();
+                    } else {
+                        end_bytes += source_line.len() - md_line.len();
+                    }
+                    break;
+                }
+                None => {
+                    // Since this is a source line that doesn't include a markdown line,
+                    // we have to count the newline that we split from earlier.
+                    if line_no <= starting_line {
+                        start_bytes += source_line.len() + 1;
+                    } else {
+                        end_bytes += source_line.len() + 1;
+                    }
+                }
+            }
+        }
+    }
+
+    Some(span_of_fragments(fragments)?.from_inner(InnerSpan::new(
+        md_range.start + start_bytes,
+        md_range.end + start_bytes + end_bytes,
+    )))
+}
diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs
index 0ffc537eee0..f1b7e8d9ae0 100644
--- a/compiler/rustc_serialize/src/opaque.rs
+++ b/compiler/rustc_serialize/src/opaque.rs
@@ -353,7 +353,7 @@ impl<'a> MemDecoder<'a> {
     }
 
     #[inline]
-    fn read_array<const N: usize>(&mut self) -> [u8; N] {
+    pub fn read_array<const N: usize>(&mut self) -> [u8; N] {
         self.read_raw_bytes(N).try_into().unwrap()
     }
 
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index f00472f181d..ba82ee95caa 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -381,6 +381,24 @@ pub enum DebugInfo {
     Full,
 }
 
+#[derive(Clone, Copy, Debug, PartialEq, Hash)]
+pub enum DebugInfoCompression {
+    None,
+    Zlib,
+    Zstd,
+}
+
+impl ToString for DebugInfoCompression {
+    fn to_string(&self) -> String {
+        match self {
+            DebugInfoCompression::None => "none",
+            DebugInfoCompression::Zlib => "zlib",
+            DebugInfoCompression::Zstd => "zstd",
+        }
+        .to_owned()
+    }
+}
+
 /// Split debug-information is enabled by `-C split-debuginfo`, this enum is only used if split
 /// debug-information is enabled (in either `Packed` or `Unpacked` modes), and the platform
 /// uses DWARF for debug-information.
@@ -1015,6 +1033,7 @@ impl Default for Options {
             crate_types: Vec::new(),
             optimize: OptLevel::No,
             debuginfo: DebugInfo::None,
+            debuginfo_compression: DebugInfoCompression::None,
             lint_opts: Vec::new(),
             lint_cap: None,
             describe_lints: false,
@@ -1067,7 +1086,7 @@ impl Options {
     /// Returns `true` if there will be an output file generated.
     pub fn will_create_output_file(&self) -> bool {
         !self.unstable_opts.parse_only && // The file is just being parsed
-            !self.unstable_opts.ls // The file is just being queried
+            self.unstable_opts.ls.is_empty() // The file is just being queried
     }
 
     #[inline]
@@ -1084,12 +1103,6 @@ impl Options {
     pub fn get_symbol_mangling_version(&self) -> SymbolManglingVersion {
         self.cg.symbol_mangling_version.unwrap_or(SymbolManglingVersion::Legacy)
     }
-
-    #[allow(rustc::bad_opt_access)]
-    pub fn incremental_relative_spans(&self) -> bool {
-        self.unstable_opts.incremental_relative_spans
-            || (self.unstable_features.is_nightly_build() && self.incremental.is_some())
-    }
 }
 
 impl UnstableOptions {
@@ -2160,12 +2173,6 @@ fn collect_print_requests(
     prints.extend(matches.opt_strs("print").into_iter().map(|req| {
         let (req, out) = split_out_file_name(&req);
 
-        if out.is_some() && !unstable_opts.unstable_options {
-            handler.early_error(
-                "the `-Z unstable-options` flag must also be passed to \
-                 enable the path print option",
-            );
-        }
         let kind = match PRINT_KINDS.iter().find(|&&(name, _)| name == req) {
             Some((_, PrintKind::TargetSpec)) => {
                 if unstable_opts.unstable_options {
@@ -2283,6 +2290,13 @@ fn select_debuginfo(matches: &getopts::Matches, cg: &CodegenOptions) -> DebugInf
     if max_g > max_c { DebugInfo::Full } else { cg.debuginfo }
 }
 
+fn select_debuginfo_compression(
+    _handler: &EarlyErrorHandler,
+    unstable_opts: &UnstableOptions,
+) -> DebugInfoCompression {
+    unstable_opts.debuginfo_compression
+}
+
 pub(crate) fn parse_assert_incr_state(
     handler: &EarlyErrorHandler,
     opt_assertion: &Option<String>,
@@ -2758,6 +2772,8 @@ pub fn build_session_options(
     // for more details.
     let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No);
     let debuginfo = select_debuginfo(matches, &cg);
+    let debuginfo_compression: DebugInfoCompression =
+        select_debuginfo_compression(handler, &unstable_opts);
 
     let mut search_paths = vec![];
     for s in &matches.opt_strs("L") {
@@ -2834,6 +2850,7 @@ pub fn build_session_options(
         crate_types,
         optimize: opt_level,
         debuginfo,
+        debuginfo_compression,
         lint_opts,
         lint_cap,
         describe_lints,
@@ -2959,6 +2976,7 @@ pub mod nightly_options {
     ) {
         let has_z_unstable_option = matches.opt_strs("Z").iter().any(|x| *x == "unstable-options");
         let really_allows_unstable_options = match_is_nightly_build(matches);
+        let mut nightly_options_on_stable = 0;
 
         for opt in flags.iter() {
             if opt.stability == OptionStability::Stable {
@@ -2979,20 +2997,27 @@ pub mod nightly_options {
             }
             match opt.stability {
                 OptionStability::Unstable => {
+                    nightly_options_on_stable += 1;
                     let msg = format!(
                         "the option `{}` is only accepted on the nightly compiler",
                         opt.name
                     );
                     let _ = handler.early_error_no_abort(msg);
-                    handler.early_note("selecting a toolchain with `+toolchain` arguments require a rustup proxy; see <https://rust-lang.github.io/rustup/concepts/index.html>");
-                    handler.early_help(
-                        "consider switching to a nightly toolchain: `rustup default nightly`",
-                    );
-                    handler.early_note("for more information about Rust's stability policy, see <https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#unstable-features>");
                 }
                 OptionStability::Stable => {}
             }
         }
+        if nightly_options_on_stable > 0 {
+            handler
+                .early_help("consider switching to a nightly toolchain: `rustup default nightly`");
+            handler.early_note("selecting a toolchain with `+toolchain` arguments require a rustup proxy; see <https://rust-lang.github.io/rustup/concepts/index.html>");
+            handler.early_note("for more information about Rust's stability policy, see <https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#unstable-features>");
+            handler.early_error(format!(
+                "{} nightly option{} were parsed",
+                nightly_options_on_stable,
+                if nightly_options_on_stable > 1 { "s" } else { "" }
+            ));
+        }
     }
 }
 
@@ -3119,11 +3144,11 @@ impl PpMode {
 /// how the hash should be calculated when adding a new command-line argument.
 pub(crate) mod dep_tracking {
     use super::{
-        BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, ErrorOutputType,
-        InstrumentCoverage, InstrumentXRay, LdImpl, LinkerPluginLto, LocationDetail, LtoCli,
-        OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes, Passes, ResolveDocLinks,
-        SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion,
-        TraitSolver, TrimmedDefPaths,
+        BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, DebugInfoCompression,
+        ErrorOutputType, InstrumentCoverage, InstrumentXRay, LdImpl, LinkerPluginLto,
+        LocationDetail, LtoCli, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes,
+        Passes, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath,
+        SymbolManglingVersion, TraitSolver, TrimmedDefPaths,
     };
     use crate::lint;
     use crate::options::WasiExecModel;
@@ -3201,6 +3226,7 @@ pub(crate) mod dep_tracking {
         OptLevel,
         LtoCli,
         DebugInfo,
+        DebugInfoCompression,
         UnstableFeatures,
         NativeLib,
         NativeLibKind,
diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs
index 90f57be9324..d816842b02b 100644
--- a/compiler/rustc_session/src/cstore.rs
+++ b/compiler/rustc_session/src/cstore.rs
@@ -7,7 +7,7 @@ use crate::utils::NativeLibKind;
 use crate::Session;
 use rustc_ast as ast;
 use rustc_data_structures::owned_slice::OwnedSlice;
-use rustc_data_structures::sync::{self, AppendOnlyIndexVec, FreezeLock, RwLock};
+use rustc_data_structures::sync::{self, AppendOnlyIndexVec, FreezeLock};
 use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, StableCrateId, LOCAL_CRATE};
 use rustc_hir::definitions::{DefKey, DefPath, DefPathHash, Definitions};
 use rustc_span::hygiene::{ExpnHash, ExpnId};
@@ -258,7 +258,7 @@ pub trait CrateStore: std::fmt::Debug {
 pub type CrateStoreDyn = dyn CrateStore + sync::DynSync + sync::DynSend;
 
 pub struct Untracked {
-    pub cstore: RwLock<Box<CrateStoreDyn>>,
+    pub cstore: FreezeLock<Box<CrateStoreDyn>>,
     /// Reference span for definitions.
     pub source_span: AppendOnlyIndexVec<LocalDefId, Span>,
     pub definitions: FreezeLock<Definitions>,
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 40099de707b..73fd7965895 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -139,6 +139,7 @@ top_level_options!(
         /// can influence whether overflow checks are done or not.
         debug_assertions: bool [TRACKED],
         debuginfo: DebugInfo [TRACKED],
+        debuginfo_compression: DebugInfoCompression [TRACKED],
         lint_opts: Vec<(String, lint::Level)> [TRACKED_NO_CRATE_HASH],
         lint_cap: Option<lint::Level> [TRACKED_NO_CRATE_HASH],
         describe_lints: bool [UNTRACKED],
@@ -376,6 +377,7 @@ mod desc {
         "either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
     pub const parse_cfprotection: &str = "`none`|`no`|`n` (default), `branch`, `return`, or `full`|`yes`|`y` (equivalent to `branch` and `return`)";
     pub const parse_debuginfo: &str = "either an integer (0, 1, 2), `none`, `line-directives-only`, `line-tables-only`, `limited`, or `full`";
+    pub const parse_debuginfo_compression: &str = "one of `none`, `zlib`, or `zstd`";
     pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`";
     pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavorCli::one_of();
     pub const parse_optimization_fuel: &str = "crate=integer";
@@ -782,6 +784,19 @@ mod parse {
         true
     }
 
+    pub(crate) fn parse_debuginfo_compression(
+        slot: &mut DebugInfoCompression,
+        v: Option<&str>,
+    ) -> bool {
+        match v {
+            Some("none") => *slot = DebugInfoCompression::None,
+            Some("zlib") => *slot = DebugInfoCompression::Zlib,
+            Some("zstd") => *slot = DebugInfoCompression::Zstd,
+            _ => return false,
+        };
+        true
+    }
+
     pub(crate) fn parse_linker_flavor(slot: &mut Option<LinkerFlavorCli>, v: Option<&str>) -> bool {
         match v.and_then(LinkerFlavorCli::from_str) {
             Some(lf) => *slot = Some(lf),
@@ -1424,6 +1439,8 @@ options! {
         "emit discriminators and other data necessary for AutoFDO"),
     debug_macros: bool = (false, parse_bool, [TRACKED],
         "emit line numbers debug info inside macros (default: no)"),
+    debuginfo_compression: DebugInfoCompression = (DebugInfoCompression::None, parse_debuginfo_compression, [TRACKED],
+        "compress debug info sections (none, zlib, zstd, default: none)"),
     deduplicate_diagnostics: bool = (true, parse_bool, [UNTRACKED],
         "deduplicate identical diagnostics (default: yes)"),
     dep_info_omit_d_target: bool = (false, parse_bool, [TRACKED],
@@ -1524,9 +1541,6 @@ options! {
     incremental_info: bool = (false, parse_bool, [UNTRACKED],
         "print high-level information about incremental reuse (or the lack thereof) \
         (default: no)"),
-    #[rustc_lint_opt_deny_field_access("use `Session::incremental_relative_spans` instead of this field")]
-    incremental_relative_spans: bool = (false, parse_bool, [TRACKED],
-        "hash spans relative to their parent item for incr. comp. (default: no)"),
     incremental_verify_ich: bool = (false, parse_bool, [UNTRACKED],
         "verify incr. comp. hashes of green query instances (default: no)"),
     inline_in_all_cgus: Option<bool> = (None, parse_opt_bool, [TRACKED],
@@ -1580,8 +1594,9 @@ options! {
         "what location details should be tracked when using caller_location, either \
         `none`, or a comma separated list of location details, for which \
         valid options are `file`, `line`, and `column` (default: `file,line,column`)"),
-    ls: bool = (false, parse_bool, [UNTRACKED],
-        "list the symbols defined by a library crate (default: no)"),
+    ls: Vec<String> = (Vec::new(), parse_list, [UNTRACKED],
+        "decode and print various parts of the crate metadata for a library crate \
+        (space separated)"),
     macro_backtrace: bool = (false, parse_bool, [UNTRACKED],
         "show macro backtraces (default: no)"),
     maximal_hir_to_mir_coverage: bool = (false, parse_bool, [TRACKED],
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 86db2edab7b..9bff9017881 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -204,6 +204,12 @@ pub struct Session {
 
     /// The version of the rustc process, possibly including a commit hash and description.
     pub cfg_version: &'static str,
+
+    /// All commandline args used to invoke the compiler, with @file args fully expanded.
+    /// This will only be used within debug info, e.g. in the pdb file on windows
+    /// This is mainly useful for other tools that reads that debuginfo to figure out
+    /// how to call the compiler with the same arguments.
+    pub expanded_args: Vec<String>,
 }
 
 pub struct PerfStats {
@@ -1325,6 +1331,7 @@ pub fn build_session(
     target_override: Option<Target>,
     cfg_version: &'static str,
     ice_file: Option<PathBuf>,
+    expanded_args: Vec<String>,
 ) -> Session {
     // FIXME: This is not general enough to make the warning lint completely override
     // normal diagnostic warnings, since the warning lint can also be denied and changed
@@ -1467,6 +1474,7 @@ pub fn build_session(
         target_features: Default::default(),
         unstable_target_features: Default::default(),
         cfg_version,
+        expanded_args,
     };
 
     validate_commandline_args_with_session_available(&sess);
diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs
index 73b8e060dd8..10ee5af86c6 100644
--- a/compiler/rustc_smir/src/rustc_internal/mod.rs
+++ b/compiler/rustc_smir/src/rustc_internal/mod.rs
@@ -16,8 +16,8 @@ use rustc_driver::{Callbacks, Compilation, RunCompiler};
 use rustc_interface::{interface, Queries};
 use rustc_middle::mir::interpret::AllocId;
 use rustc_middle::ty::TyCtxt;
-use rustc_session::EarlyErrorHandler;
 pub use rustc_span::def_id::{CrateNum, DefId};
+use rustc_span::Span;
 
 fn with_tables<R>(mut f: impl FnMut(&mut Tables<'_>) -> R) -> R {
     let mut ret = None;
@@ -160,6 +160,17 @@ impl<'tcx> Tables<'tcx> {
         self.alloc_ids.push(aid);
         stable_mir::AllocId(id)
     }
+
+    pub(crate) fn create_span(&mut self, span: Span) -> stable_mir::ty::Span {
+        for (i, &sp) in self.spans.iter().enumerate() {
+            if sp == span {
+                return stable_mir::ty::Span(i);
+            }
+        }
+        let id = self.spans.len();
+        self.spans.push(span);
+        stable_mir::ty::Span(id)
+    }
 }
 
 pub fn crate_num(item: &stable_mir::Crate) -> CrateNum {
@@ -167,7 +178,10 @@ pub fn crate_num(item: &stable_mir::Crate) -> CrateNum {
 }
 
 pub fn run(tcx: TyCtxt<'_>, f: impl FnOnce()) {
-    crate::stable_mir::run(Tables { tcx, def_ids: vec![], alloc_ids: vec![], types: vec![] }, f);
+    crate::stable_mir::run(
+        Tables { tcx, def_ids: vec![], alloc_ids: vec![], spans: vec![], types: vec![] },
+        f,
+    );
 }
 
 /// A type that provides internal information but that can still be used for debug purpose.
@@ -233,7 +247,6 @@ where
     /// continue the compilation afterwards (defaults to `Compilation::Continue`)
     fn after_analysis<'tcx>(
         &mut self,
-        _handler: &EarlyErrorHandler,
         _compiler: &interface::Compiler,
         queries: &'tcx Queries<'tcx>,
     ) -> Compilation {
diff --git a/compiler/rustc_smir/src/rustc_smir/alloc.rs b/compiler/rustc_smir/src/rustc_smir/alloc.rs
index 166c8bda9e1..35e65c19be0 100644
--- a/compiler/rustc_smir/src/rustc_smir/alloc.rs
+++ b/compiler/rustc_smir/src/rustc_smir/alloc.rs
@@ -45,7 +45,7 @@ pub fn new_allocation<'tcx>(
             new_empty_allocation(align.abi)
         }
         ConstValue::Slice { data, start, end } => {
-            let alloc_id = tables.tcx.create_memory_alloc(data);
+            let alloc_id = tables.tcx.reserve_and_set_memory_alloc(data);
             let ptr = Pointer::new(alloc_id, rustc_target::abi::Size::from_bytes(start));
             let scalar_ptr = rustc_middle::mir::interpret::Scalar::from_pointer(ptr, &tables.tcx);
             let scalar_len = rustc_middle::mir::interpret::Scalar::from_target_usize(
@@ -72,7 +72,8 @@ pub fn new_allocation<'tcx>(
                 .unwrap();
             allocation.stable(tables)
         }
-        ConstValue::ByRef { alloc, offset } => {
+        ConstValue::Indirect { alloc_id, offset } => {
+            let alloc = tables.tcx.global_alloc(alloc_id).unwrap_memory();
             let ty_size = tables
                 .tcx
                 .layout_of(rustc_middle::ty::ParamEnv::reveal_all().and(ty))
diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs
index a7640736481..93b5b9654d3 100644
--- a/compiler/rustc_smir/src/rustc_smir/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/mod.rs
@@ -9,7 +9,9 @@
 
 use crate::rustc_internal::{self, opaque};
 use crate::stable_mir::mir::{CopyNonOverlapping, UserTypeProjection, VariantIdx};
-use crate::stable_mir::ty::{FloatTy, GenericParamDef, IntTy, Movability, RigidTy, TyKind, UintTy};
+use crate::stable_mir::ty::{
+    FloatTy, GenericParamDef, IntTy, Movability, RigidTy, Span, TyKind, UintTy,
+};
 use crate::stable_mir::{self, CompilerError, Context};
 use rustc_hir as hir;
 use rustc_middle::mir::interpret::{alloc_range, AllocId};
@@ -42,6 +44,10 @@ impl<'tcx> Context for Tables<'tcx> {
         self.tcx.def_path_str(self[def_id])
     }
 
+    fn span_of_an_item(&mut self, def_id: stable_mir::DefId) -> Span {
+        self.tcx.def_span(self[def_id]).stable(self)
+    }
+
     fn all_local_items(&mut self) -> stable_mir::CrateItems {
         self.tcx.mir_keys(()).iter().map(|item| self.crate_item(item.to_def_id())).collect()
     }
@@ -80,7 +86,7 @@ impl<'tcx> Context for Tables<'tcx> {
 
     fn mir_body(&mut self, item: stable_mir::DefId) -> stable_mir::mir::Body {
         let def_id = self[item];
-        let mir = self.tcx.optimized_mir(def_id);
+        let mir = self.tcx.instance_mir(ty::InstanceDef::Item(def_id));
         stable_mir::mir::Body {
             blocks: mir
                 .basic_blocks
@@ -131,6 +137,23 @@ impl<'tcx> Context for Tables<'tcx> {
                 .collect(),
         }
     }
+
+    fn explicit_predicates_of(
+        &mut self,
+        def_id: stable_mir::DefId,
+    ) -> stable_mir::ty::GenericPredicates {
+        let def_id = self[def_id];
+        let ty::GenericPredicates { parent, predicates } = self.tcx.explicit_predicates_of(def_id);
+        stable_mir::ty::GenericPredicates {
+            parent: parent.map(|did| self.trait_def(did)),
+            predicates: predicates
+                .iter()
+                .map(|(clause, span)| {
+                    (clause.as_predicate().kind().skip_binder().stable(self), span.stable(self))
+                })
+                .collect(),
+        }
+    }
 }
 
 #[derive(Clone)]
@@ -164,6 +187,7 @@ pub struct Tables<'tcx> {
     pub tcx: TyCtxt<'tcx>,
     pub def_ids: Vec<DefId>,
     pub alloc_ids: Vec<AllocId>,
+    pub spans: Vec<rustc_span::Span>,
     pub types: Vec<MaybeStable<stable_mir::ty::TyKind, Ty<'tcx>>>,
 }
 
@@ -1293,7 +1317,7 @@ impl<'tcx> Stable<'tcx> for rustc_middle::ty::GenericParamDefKind {
             ty::GenericParamDefKind::Type { has_default, synthetic } => {
                 GenericParamDefKind::Type { has_default: *has_default, synthetic: *synthetic }
             }
-            ty::GenericParamDefKind::Const { has_default } => {
+            ty::GenericParamDefKind::Const { has_default, is_host_effect: _ } => {
                 GenericParamDefKind::Const { has_default: *has_default }
             }
         }
@@ -1493,9 +1517,8 @@ impl<'tcx> Stable<'tcx> for ty::Region<'tcx> {
 impl<'tcx> Stable<'tcx> for rustc_span::Span {
     type T = stable_mir::ty::Span;
 
-    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
-        // FIXME: add a real implementation of stable spans
-        opaque(self)
+    fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
+        tables.create_span(*self)
     }
 }
 
diff --git a/compiler/rustc_smir/src/stable_mir/mod.rs b/compiler/rustc_smir/src/stable_mir/mod.rs
index 9cab7230b97..3c86cb4038a 100644
--- a/compiler/rustc_smir/src/stable_mir/mod.rs
+++ b/compiler/rustc_smir/src/stable_mir/mod.rs
@@ -89,6 +89,10 @@ impl CrateItem {
     pub fn body(&self) -> mir::Body {
         with(|cx| cx.mir_body(self.0))
     }
+
+    pub fn span(&self) -> Span {
+        with(|cx| cx.span_of_an_item(self.0))
+    }
 }
 
 /// Return the function where execution starts if the current
@@ -145,6 +149,7 @@ pub trait Context {
     fn trait_impl(&mut self, trait_impl: &ImplDef) -> ImplTrait;
     fn generics_of(&mut self, def_id: DefId) -> Generics;
     fn predicates_of(&mut self, def_id: DefId) -> GenericPredicates;
+    fn explicit_predicates_of(&mut self, def_id: DefId) -> GenericPredicates;
     /// Get information about the local crate.
     fn local_crate(&self) -> Crate;
     /// Retrieve a list of all external crates.
@@ -156,6 +161,9 @@ pub trait Context {
     /// Prints the name of given `DefId`
     fn name_of_def_id(&self, def_id: DefId) -> String;
 
+    /// `Span` of an item
+    fn span_of_an_item(&mut self, def_id: DefId) -> Span;
+
     /// Obtain the representation of a type.
     fn ty_kind(&mut self, ty: Ty) -> TyKind;
 
diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs
index 7e344dc516b..3a8fc1a502e 100644
--- a/compiler/rustc_smir/src/stable_mir/ty.rs
+++ b/compiler/rustc_smir/src/stable_mir/ty.rs
@@ -4,10 +4,17 @@ use super::{
     with, AllocId, DefId,
 };
 use crate::rustc_internal::Opaque;
+use std::fmt::{self, Debug, Formatter};
 
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone)]
 pub struct Ty(pub usize);
 
+impl Debug for Ty {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        f.debug_struct("Ty").field("id", &self.0).field("kind", &self.kind()).finish()
+    }
+}
+
 impl Ty {
     pub fn kind(&self) -> TyKind {
         with(|context| context.ty_kind(*self))
@@ -28,7 +35,16 @@ pub struct Const {
 
 type Ident = Opaque;
 pub(crate) type Region = Opaque;
-pub(crate) type Span = Opaque;
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub struct Span(pub(crate) usize);
+
+impl Debug for Span {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        let mut span = None;
+        with(|context| context.rustc_tables(&mut |tables| span = Some(tables.spans[self.0])));
+        f.write_fmt(format_args!("{:?}", &span.unwrap()))
+    }
+}
 
 #[derive(Clone, Debug)]
 pub enum TyKind {
@@ -388,6 +404,10 @@ impl TraitDecl {
     pub fn predicates_of(&self) -> GenericPredicates {
         with(|cx| cx.predicates_of(self.def_id.0))
     }
+
+    pub fn explicit_predicates_of(&self) -> GenericPredicates {
+        with(|cx| cx.explicit_predicates_of(self.def_id.0))
+    }
 }
 
 pub type ImplTrait = EarlyBinder<TraitRef>;
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index f1a6e9059d7..68724c48037 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -33,7 +33,7 @@ extern crate rustc_macros;
 #[macro_use]
 extern crate tracing;
 
-use rustc_data_structures::AtomicRef;
+use rustc_data_structures::{cold_path, AtomicRef};
 use rustc_macros::HashStable_Generic;
 use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 
@@ -64,7 +64,7 @@ pub mod fatal_error;
 pub mod profiling;
 
 use rustc_data_structures::stable_hasher::{Hash128, Hash64, HashStable, StableHasher};
-use rustc_data_structures::sync::{Lock, Lrc};
+use rustc_data_structures::sync::{FreezeLock, FreezeWriteGuard, Lock, Lrc};
 
 use std::borrow::Cow;
 use std::cmp::{self, Ordering};
@@ -510,10 +510,6 @@ impl SpanData {
     pub fn is_dummy(self) -> bool {
         self.lo.0 == 0 && self.hi.0 == 0
     }
-    #[inline]
-    pub fn is_visible(self, sm: &SourceMap) -> bool {
-        !self.is_dummy() && sm.is_span_accessible(self.span())
-    }
     /// Returns `true` if `self` fully encloses `other`.
     pub fn contains(self, other: Self) -> bool {
         self.lo <= other.lo && other.hi <= self.hi
@@ -573,15 +569,9 @@ impl Span {
         self.data().with_parent(ctxt)
     }
 
-    /// Returns `true` if this is a dummy span with any hygienic context.
-    #[inline]
-    pub fn is_dummy(self) -> bool {
-        self.data_untracked().is_dummy()
-    }
-
     #[inline]
     pub fn is_visible(self, sm: &SourceMap) -> bool {
-        self.data_untracked().is_visible(sm)
+        !self.is_dummy() && sm.is_span_accessible(self)
     }
 
     /// Returns `true` if this span comes from any kind of macro, desugaring or inlining.
@@ -1206,7 +1196,6 @@ pub enum ExternalSourceKind {
     AbsentOk,
     /// A failed attempt has been made to load the external source.
     AbsentErr,
-    Unneeded,
 }
 
 impl ExternalSource {
@@ -1343,13 +1332,13 @@ pub struct SourceFile {
     pub src_hash: SourceFileHash,
     /// The external source code (used for external crates, which will have a `None`
     /// value as `self.src`.
-    pub external_src: Lock<ExternalSource>,
+    pub external_src: FreezeLock<ExternalSource>,
     /// The start position of this source in the `SourceMap`.
     pub start_pos: BytePos,
     /// The byte length of this source.
     pub source_len: RelativeBytePos,
     /// Locations of lines beginnings in the source code.
-    pub lines: Lock<SourceFileLines>,
+    pub lines: FreezeLock<SourceFileLines>,
     /// Locations of multi-byte characters in the source code.
     pub multibyte_chars: Vec<MultiByteChar>,
     /// Width of characters that are not narrow in the source code.
@@ -1368,10 +1357,10 @@ impl Clone for SourceFile {
             name: self.name.clone(),
             src: self.src.clone(),
             src_hash: self.src_hash,
-            external_src: Lock::new(self.external_src.borrow().clone()),
+            external_src: self.external_src.clone(),
             start_pos: self.start_pos,
             source_len: self.source_len,
-            lines: Lock::new(self.lines.borrow().clone()),
+            lines: self.lines.clone(),
             multibyte_chars: self.multibyte_chars.clone(),
             non_narrow_chars: self.non_narrow_chars.clone(),
             normalized_pos: self.normalized_pos.clone(),
@@ -1389,64 +1378,63 @@ impl<S: Encoder> Encodable<S> for SourceFile {
         self.source_len.encode(s);
 
         // We are always in `Lines` form by the time we reach here.
-        assert!(self.lines.borrow().is_lines());
-        self.lines(|lines| {
-            // Store the length.
-            s.emit_u32(lines.len() as u32);
-
-            // Compute and store the difference list.
-            if lines.len() != 0 {
-                let max_line_length = if lines.len() == 1 {
-                    0
-                } else {
-                    lines
-                        .array_windows()
-                        .map(|&[fst, snd]| snd - fst)
-                        .map(|bp| bp.to_usize())
-                        .max()
-                        .unwrap()
-                };
-
-                let bytes_per_diff: usize = match max_line_length {
-                    0..=0xFF => 1,
-                    0x100..=0xFFFF => 2,
-                    _ => 4,
-                };
-
-                // Encode the number of bytes used per diff.
-                s.emit_u8(bytes_per_diff as u8);
-
-                // Encode the first element.
-                assert_eq!(lines[0], RelativeBytePos(0));
-
-                // Encode the difference list.
-                let diff_iter = lines.array_windows().map(|&[fst, snd]| snd - fst);
-                let num_diffs = lines.len() - 1;
-                let mut raw_diffs;
-                match bytes_per_diff {
-                    1 => {
-                        raw_diffs = Vec::with_capacity(num_diffs);
-                        for diff in diff_iter {
-                            raw_diffs.push(diff.0 as u8);
-                        }
+        assert!(self.lines.read().is_lines());
+        let lines = self.lines();
+        // Store the length.
+        s.emit_u32(lines.len() as u32);
+
+        // Compute and store the difference list.
+        if lines.len() != 0 {
+            let max_line_length = if lines.len() == 1 {
+                0
+            } else {
+                lines
+                    .array_windows()
+                    .map(|&[fst, snd]| snd - fst)
+                    .map(|bp| bp.to_usize())
+                    .max()
+                    .unwrap()
+            };
+
+            let bytes_per_diff: usize = match max_line_length {
+                0..=0xFF => 1,
+                0x100..=0xFFFF => 2,
+                _ => 4,
+            };
+
+            // Encode the number of bytes used per diff.
+            s.emit_u8(bytes_per_diff as u8);
+
+            // Encode the first element.
+            assert_eq!(lines[0], RelativeBytePos(0));
+
+            // Encode the difference list.
+            let diff_iter = lines.array_windows().map(|&[fst, snd]| snd - fst);
+            let num_diffs = lines.len() - 1;
+            let mut raw_diffs;
+            match bytes_per_diff {
+                1 => {
+                    raw_diffs = Vec::with_capacity(num_diffs);
+                    for diff in diff_iter {
+                        raw_diffs.push(diff.0 as u8);
                     }
-                    2 => {
-                        raw_diffs = Vec::with_capacity(bytes_per_diff * num_diffs);
-                        for diff in diff_iter {
-                            raw_diffs.extend_from_slice(&(diff.0 as u16).to_le_bytes());
-                        }
+                }
+                2 => {
+                    raw_diffs = Vec::with_capacity(bytes_per_diff * num_diffs);
+                    for diff in diff_iter {
+                        raw_diffs.extend_from_slice(&(diff.0 as u16).to_le_bytes());
                     }
-                    4 => {
-                        raw_diffs = Vec::with_capacity(bytes_per_diff * num_diffs);
-                        for diff in diff_iter {
-                            raw_diffs.extend_from_slice(&(diff.0).to_le_bytes());
-                        }
+                }
+                4 => {
+                    raw_diffs = Vec::with_capacity(bytes_per_diff * num_diffs);
+                    for diff in diff_iter {
+                        raw_diffs.extend_from_slice(&(diff.0).to_le_bytes());
                     }
-                    _ => unreachable!(),
                 }
-                s.emit_raw_bytes(&raw_diffs);
+                _ => unreachable!(),
             }
-        });
+            s.emit_raw_bytes(&raw_diffs);
+        }
 
         self.multibyte_chars.encode(s);
         self.non_narrow_chars.encode(s);
@@ -1488,8 +1476,8 @@ impl<D: Decoder> Decodable<D> for SourceFile {
             src_hash,
             // Unused - the metadata decoder will construct
             // a new SourceFile, filling in `external_src` properly
-            external_src: Lock::new(ExternalSource::Unneeded),
-            lines: Lock::new(lines),
+            external_src: FreezeLock::frozen(ExternalSource::Unneeded),
+            lines: FreezeLock::new(lines),
             multibyte_chars,
             non_narrow_chars,
             normalized_pos,
@@ -1530,10 +1518,10 @@ impl SourceFile {
             name,
             src: Some(Lrc::new(src)),
             src_hash,
-            external_src: Lock::new(ExternalSource::Unneeded),
+            external_src: FreezeLock::frozen(ExternalSource::Unneeded),
             start_pos: BytePos::from_u32(0),
             source_len: RelativeBytePos::from_u32(source_len),
-            lines: Lock::new(SourceFileLines::Lines(lines)),
+            lines: FreezeLock::frozen(SourceFileLines::Lines(lines)),
             multibyte_chars,
             non_narrow_chars,
             normalized_pos,
@@ -1542,65 +1530,82 @@ impl SourceFile {
         })
     }
 
-    pub fn lines<F, R>(&self, f: F) -> R
-    where
-        F: FnOnce(&[RelativeBytePos]) -> R,
-    {
-        let mut guard = self.lines.borrow_mut();
-        match &*guard {
-            SourceFileLines::Lines(lines) => f(lines),
-            SourceFileLines::Diffs(SourceFileDiffs { bytes_per_diff, num_diffs, raw_diffs }) => {
-                // Convert from "diffs" form to "lines" form.
-                let num_lines = num_diffs + 1;
-                let mut lines = Vec::with_capacity(num_lines);
-                let mut line_start = RelativeBytePos(0);
-                lines.push(line_start);
-
-                assert_eq!(*num_diffs, raw_diffs.len() / bytes_per_diff);
-                match bytes_per_diff {
-                    1 => {
-                        lines.extend(raw_diffs.into_iter().map(|&diff| {
-                            line_start = line_start + RelativeBytePos(diff as u32);
-                            line_start
-                        }));
-                    }
-                    2 => {
-                        lines.extend((0..*num_diffs).map(|i| {
-                            let pos = bytes_per_diff * i;
-                            let bytes = [raw_diffs[pos], raw_diffs[pos + 1]];
-                            let diff = u16::from_le_bytes(bytes);
-                            line_start = line_start + RelativeBytePos(diff as u32);
-                            line_start
-                        }));
-                    }
-                    4 => {
-                        lines.extend((0..*num_diffs).map(|i| {
-                            let pos = bytes_per_diff * i;
-                            let bytes = [
-                                raw_diffs[pos],
-                                raw_diffs[pos + 1],
-                                raw_diffs[pos + 2],
-                                raw_diffs[pos + 3],
-                            ];
-                            let diff = u32::from_le_bytes(bytes);
-                            line_start = line_start + RelativeBytePos(diff);
-                            line_start
-                        }));
-                    }
-                    _ => unreachable!(),
-                }
-                let res = f(&lines);
-                *guard = SourceFileLines::Lines(lines);
-                res
+    /// This converts the `lines` field to contain `SourceFileLines::Lines` if needed and freezes it.
+    fn convert_diffs_to_lines_frozen(&self) {
+        let mut guard = if let Some(guard) = self.lines.try_write() { guard } else { return };
+
+        let SourceFileDiffs { bytes_per_diff, num_diffs, raw_diffs } = match &*guard {
+            SourceFileLines::Diffs(diffs) => diffs,
+            SourceFileLines::Lines(..) => {
+                FreezeWriteGuard::freeze(guard);
+                return;
             }
+        };
+
+        // Convert from "diffs" form to "lines" form.
+        let num_lines = num_diffs + 1;
+        let mut lines = Vec::with_capacity(num_lines);
+        let mut line_start = RelativeBytePos(0);
+        lines.push(line_start);
+
+        assert_eq!(*num_diffs, raw_diffs.len() / bytes_per_diff);
+        match bytes_per_diff {
+            1 => {
+                lines.extend(raw_diffs.into_iter().map(|&diff| {
+                    line_start = line_start + RelativeBytePos(diff as u32);
+                    line_start
+                }));
+            }
+            2 => {
+                lines.extend((0..*num_diffs).map(|i| {
+                    let pos = bytes_per_diff * i;
+                    let bytes = [raw_diffs[pos], raw_diffs[pos + 1]];
+                    let diff = u16::from_le_bytes(bytes);
+                    line_start = line_start + RelativeBytePos(diff as u32);
+                    line_start
+                }));
+            }
+            4 => {
+                lines.extend((0..*num_diffs).map(|i| {
+                    let pos = bytes_per_diff * i;
+                    let bytes = [
+                        raw_diffs[pos],
+                        raw_diffs[pos + 1],
+                        raw_diffs[pos + 2],
+                        raw_diffs[pos + 3],
+                    ];
+                    let diff = u32::from_le_bytes(bytes);
+                    line_start = line_start + RelativeBytePos(diff);
+                    line_start
+                }));
+            }
+            _ => unreachable!(),
         }
+
+        *guard = SourceFileLines::Lines(lines);
+
+        FreezeWriteGuard::freeze(guard);
+    }
+
+    pub fn lines(&self) -> &[RelativeBytePos] {
+        if let Some(SourceFileLines::Lines(lines)) = self.lines.get() {
+            return &lines[..];
+        }
+
+        cold_path(|| {
+            self.convert_diffs_to_lines_frozen();
+            if let Some(SourceFileLines::Lines(lines)) = self.lines.get() {
+                return &lines[..];
+            }
+            unreachable!()
+        })
     }
 
     /// Returns the `BytePos` of the beginning of the current line.
     pub fn line_begin_pos(&self, pos: BytePos) -> BytePos {
         let pos = self.relative_position(pos);
         let line_index = self.lookup_line(pos).unwrap();
-        let line_start_pos = self.lines(|lines| lines[line_index]);
+        let line_start_pos = self.lines()[line_index];
         self.absolute_position(line_start_pos)
     }
 
@@ -1612,35 +1617,37 @@ impl SourceFile {
     where
         F: FnOnce() -> Option<String>,
     {
-        if matches!(
-            *self.external_src.borrow(),
-            ExternalSource::Foreign { kind: ExternalSourceKind::AbsentOk, .. }
-        ) {
+        if !self.external_src.is_frozen() {
             let src = get_src();
-            let mut external_src = self.external_src.borrow_mut();
-            // Check that no-one else have provided the source while we were getting it
-            if let ExternalSource::Foreign {
-                kind: src_kind @ ExternalSourceKind::AbsentOk, ..
-            } = &mut *external_src
-            {
-                if let Some(mut src) = src {
-                    // The src_hash needs to be computed on the pre-normalized src.
-                    if self.src_hash.matches(&src) {
-                        normalize_src(&mut src);
-                        *src_kind = ExternalSourceKind::Present(Lrc::new(src));
-                        return true;
-                    }
+            let src = src.and_then(|mut src| {
+                // The src_hash needs to be computed on the pre-normalized src.
+                self.src_hash.matches(&src).then(|| {
+                    normalize_src(&mut src);
+                    src
+                })
+            });
+
+            self.external_src.try_write().map(|mut external_src| {
+                if let ExternalSource::Foreign {
+                    kind: src_kind @ ExternalSourceKind::AbsentOk,
+                    ..
+                } = &mut *external_src
+                {
+                    *src_kind = if let Some(src) = src {
+                        ExternalSourceKind::Present(Lrc::new(src))
+                    } else {
+                        ExternalSourceKind::AbsentErr
+                    };
                 } else {
-                    *src_kind = ExternalSourceKind::AbsentErr;
+                    panic!("unexpected state {:?}", *external_src)
                 }
 
-                false
-            } else {
-                self.src.is_some() || external_src.get_source().is_some()
-            }
-        } else {
-            self.src.is_some() || self.external_src.borrow().get_source().is_some()
+                // Freeze this so we don't try to load the source again.
+                FreezeWriteGuard::freeze(external_src)
+            });
         }
+
+        self.src.is_some() || self.external_src.read().get_source().is_some()
     }
 
     /// Gets a line from the list of pre-computed line-beginnings.
@@ -1658,7 +1665,7 @@ impl SourceFile {
         }
 
         let begin = {
-            let line = self.lines(|lines| lines.get(line_number).copied())?;
+            let line = self.lines().get(line_number).copied()?;
             line.to_usize()
         };
 
@@ -1682,7 +1689,7 @@ impl SourceFile {
     }
 
     pub fn count_lines(&self) -> usize {
-        self.lines(|lines| lines.len())
+        self.lines().len()
     }
 
     #[inline]
@@ -1705,7 +1712,7 @@ impl SourceFile {
     /// number. If the source_file is empty or the position is located before the
     /// first line, `None` is returned.
     pub fn lookup_line(&self, pos: RelativeBytePos) -> Option<usize> {
-        self.lines(|lines| lines.partition_point(|x| x <= &pos).checked_sub(1))
+        self.lines().partition_point(|x| x <= &pos).checked_sub(1)
     }
 
     pub fn line_bounds(&self, line_index: usize) -> Range<BytePos> {
@@ -1713,15 +1720,13 @@ impl SourceFile {
             return self.start_pos..self.start_pos;
         }
 
-        self.lines(|lines| {
-            assert!(line_index < lines.len());
-            if line_index == (lines.len() - 1) {
-                self.absolute_position(lines[line_index])..self.end_position()
-            } else {
-                self.absolute_position(lines[line_index])
-                    ..self.absolute_position(lines[line_index + 1])
-            }
-        })
+        let lines = self.lines();
+        assert!(line_index < lines.len());
+        if line_index == (lines.len() - 1) {
+            self.absolute_position(lines[line_index])..self.end_position()
+        } else {
+            self.absolute_position(lines[line_index])..self.absolute_position(lines[line_index + 1])
+        }
     }
 
     /// Returns whether or not the file contains the given `SourceMap` byte
@@ -1807,7 +1812,7 @@ impl SourceFile {
         match self.lookup_line(pos) {
             Some(a) => {
                 let line = a + 1; // Line numbers start at 1
-                let linebpos = self.lines(|lines| lines[a]);
+                let linebpos = self.lines()[a];
                 let linechpos = self.bytepos_to_file_charpos(linebpos);
                 let col = chpos - linechpos;
                 debug!("byte pos {:?} is on the line at byte pos {:?}", pos, linebpos);
@@ -1827,7 +1832,7 @@ impl SourceFile {
         let (line, col_or_chpos) = self.lookup_file_pos(pos);
         if line > 0 {
             let col = col_or_chpos;
-            let linebpos = self.lines(|lines| lines[line - 1]);
+            let linebpos = self.lines()[line - 1];
             let col_display = {
                 let start_width_idx = self
                     .non_narrow_chars
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index 81730f2f608..68727a6c40e 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -328,7 +328,7 @@ impl SourceMap {
         name_hash: Hash128,
         source_len: u32,
         cnum: CrateNum,
-        file_local_lines: Lock<SourceFileLines>,
+        file_local_lines: FreezeLock<SourceFileLines>,
         multibyte_chars: Vec<MultiByteChar>,
         non_narrow_chars: Vec<NonNarrowChar>,
         normalized_pos: Vec<NormalizedPos>,
@@ -340,7 +340,7 @@ impl SourceMap {
             name: filename,
             src: None,
             src_hash,
-            external_src: Lock::new(ExternalSource::Foreign {
+            external_src: FreezeLock::new(ExternalSource::Foreign {
                 kind: ExternalSourceKind::AbsentOk,
                 metadata_index,
             }),
@@ -564,7 +564,7 @@ impl SourceMap {
                 end: (local_end.sf.name.clone(), local_end.sf.start_pos),
             })))
         } else {
-            self.ensure_source_file_source_present(local_begin.sf.clone());
+            self.ensure_source_file_source_present(&local_begin.sf);
 
             let start_index = local_begin.pos.to_usize();
             let end_index = local_end.pos.to_usize();
@@ -581,7 +581,7 @@ impl SourceMap {
 
             if let Some(ref src) = local_begin.sf.src {
                 extract_source(src, start_index, end_index)
-            } else if let Some(src) = local_begin.sf.external_src.borrow().get_source() {
+            } else if let Some(src) = local_begin.sf.external_src.read().get_source() {
                 extract_source(src, start_index, end_index)
             } else {
                 Err(SpanSnippetError::SourceNotAvailable { filename: local_begin.sf.name.clone() })
@@ -873,7 +873,7 @@ impl SourceMap {
             let sp = sp.data();
             let local_begin = self.lookup_byte_offset(sp.lo);
             let start_index = local_begin.pos.to_usize();
-            let src = local_begin.sf.external_src.borrow();
+            let src = local_begin.sf.external_src.read();
 
             let snippet = if let Some(ref src) = local_begin.sf.src {
                 Some(&src[start_index..])
@@ -983,7 +983,7 @@ impl SourceMap {
             return 1;
         }
 
-        let src = local_begin.sf.external_src.borrow();
+        let src = local_begin.sf.external_src.read();
 
         let snippet = if let Some(src) = &local_begin.sf.src {
             src
@@ -1030,7 +1030,7 @@ impl SourceMap {
         self.files().iter().fold(0, |a, f| a + f.count_lines())
     }
 
-    pub fn ensure_source_file_source_present(&self, source_file: Lrc<SourceFile>) -> bool {
+    pub fn ensure_source_file_source_present(&self, source_file: &SourceFile) -> bool {
         source_file.add_external_src(|| {
             let FileName::Real(ref name) = source_file.name else {
                 return None;
diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs
index 7689e6afac5..e393db02064 100644
--- a/compiler/rustc_span/src/source_map/tests.rs
+++ b/compiler/rustc_span/src/source_map/tests.rs
@@ -1,6 +1,6 @@
 use super::*;
 
-use rustc_data_structures::sync::Lrc;
+use rustc_data_structures::sync::{FreezeLock, Lrc};
 
 fn init_source_map() -> SourceMap {
     let sm = SourceMap::new(FilePathMapping::empty());
@@ -246,7 +246,7 @@ fn t10() {
         name_hash,
         source_len.to_u32(),
         CrateNum::new(0),
-        lines,
+        FreezeLock::new(lines.read().clone()),
         multibyte_chars,
         non_narrow_chars,
         normalized_pos,
diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs
index 1eea0f63ca0..93ab154601f 100644
--- a/compiler/rustc_span/src/span_encoding.rs
+++ b/compiler/rustc_span/src/span_encoding.rs
@@ -1,9 +1,3 @@
-// Spans are encoded using 1-bit tag and 2 different encoding formats (one for each tag value).
-// One format is used for keeping span data inline,
-// another contains index into an out-of-line span interner.
-// The encoding format for inline spans were obtained by optimizing over crates in rustc/libstd.
-// See https://internals.rust-lang.org/t/rfc-compiler-refactoring-spans/1357/28
-
 use crate::def_id::{DefIndex, LocalDefId};
 use crate::hygiene::SyntaxContext;
 use crate::SPAN_TRACK;
@@ -13,59 +7,69 @@ use rustc_data_structures::fx::FxIndexSet;
 
 /// A compressed span.
 ///
-/// Whereas [`SpanData`] is 16 bytes, which is a bit too big to stick everywhere, `Span`
-/// is a form that only takes up 8 bytes, with less space for the length, parent and
-/// context. The vast majority (99.9%+) of `SpanData` instances will fit within
-/// those 8 bytes; any `SpanData` whose fields don't fit into a `Span` are
+/// [`SpanData`] is 16 bytes, which is too big to stick everywhere. `Span` only
+/// takes up 8 bytes, with less space for the length, parent and context. The
+/// vast majority (99.9%+) of `SpanData` instances can be made to fit within
+/// those 8 bytes. Any `SpanData` whose fields don't fit into a `Span` are
 /// stored in a separate interner table, and the `Span` will index into that
 /// table. Interning is rare enough that the cost is low, but common enough
 /// that the code is exercised regularly.
 ///
 /// An earlier version of this code used only 4 bytes for `Span`, but that was
 /// slower because only 80--90% of spans could be stored inline (even less in
-/// very large crates) and so the interner was used a lot more.
+/// very large crates) and so the interner was used a lot more. That version of
+/// the code also predated the storage of parents.
+///
+/// There are four different span forms.
 ///
-/// Inline (compressed) format with no parent:
-/// - `span.base_or_index == span_data.lo`
-/// - `span.len_or_tag == len == span_data.hi - span_data.lo` (must be `<= MAX_LEN`)
-/// - `span.ctxt_or_tag == span_data.ctxt` (must be `<= MAX_CTXT`)
+/// Inline-context format (requires non-huge length, non-huge context, and no parent):
+/// - `span.lo_or_index == span_data.lo`
+/// - `span.len_with_tag_or_marker == len == span_data.hi - span_data.lo` (must be `<= MAX_LEN`)
+/// - `span.ctxt_or_parent_or_marker == span_data.ctxt` (must be `<= MAX_CTXT`)
 ///
-/// Interned format with inline `SyntaxContext`:
-/// - `span.base_or_index == index` (indexes into the interner table)
-/// - `span.len_or_tag == LEN_TAG` (high bit set, all other bits are zero)
-/// - `span.ctxt_or_tag == span_data.ctxt` (must be `<= MAX_CTXT`)
+/// Inline-parent format (requires non-huge length, root context, and non-huge parent):
+/// - `span.lo_or_index == span_data.lo`
+/// - `span.len_with_tag_or_marker & !PARENT_TAG == len == span_data.hi - span_data.lo`
+///   (must be `<= MAX_LEN`)
+/// - `span.len_with_tag_or_marker` has top bit (`PARENT_TAG`) set
+/// - `span.ctxt_or_parent_or_marker == span_data.parent` (must be `<= MAX_CTXT`)
 ///
-/// Inline (compressed) format with root context:
-/// - `span.base_or_index == span_data.lo`
-/// - `span.len_or_tag == len == span_data.hi - span_data.lo` (must be `<= MAX_LEN`)
-/// - `span.len_or_tag` has top bit (`PARENT_MASK`) set
-/// - `span.ctxt == span_data.parent` (must be `<= MAX_CTXT`)
+/// Partially-interned format (requires non-huge context):
+/// - `span.lo_or_index == index` (indexes into the interner table)
+/// - `span.len_with_tag_or_marker == BASE_LEN_INTERNED_MARKER`
+/// - `span.ctxt_or_parent_or_marker == span_data.ctxt` (must be `<= MAX_CTXT`)
 ///
-/// Interned format:
-/// - `span.base_or_index == index` (indexes into the interner table)
-/// - `span.len_or_tag == LEN_TAG` (high bit set, all other bits are zero)
-/// - `span.ctxt_or_tag == CTXT_TAG`
+/// Fully-interned format (all cases not covered above):
+/// - `span.lo_or_index == index` (indexes into the interner table)
+/// - `span.len_with_tag_or_marker == BASE_LEN_INTERNED_MARKER`
+/// - `span.ctxt_or_parent_or_marker == CTXT_INTERNED_MARKER`
 ///
-/// The inline form uses 0 for the tag value (rather than 1) so that we don't
-/// need to mask out the tag bit when getting the length, and so that the
-/// dummy span can be all zeroes.
+/// The partially-interned form requires looking in the interning table for
+/// lo and length, but the context is stored inline as well as interned.
+/// This is useful because context lookups are often done in isolation, and
+/// inline lookups are quicker.
 ///
 /// Notes about the choice of field sizes:
-/// - `base` is 32 bits in both `Span` and `SpanData`, which means that `base`
-///   values never cause interning. The number of bits needed for `base`
+/// - `lo` is 32 bits in both `Span` and `SpanData`, which means that `lo`
+///   values never cause interning. The number of bits needed for `lo`
 ///   depends on the crate size. 32 bits allows up to 4 GiB of code in a crate.
-/// - `len` is 15 bits in `Span` (a u16, minus 1 bit for the tag) and 32 bits
-///   in `SpanData`, which means that large `len` values will cause interning.
-///   The number of bits needed for `len` does not depend on the crate size.
-///   The most common numbers of bits for `len` are from 0 to 7, with a peak usually
-///   at 3 or 4, and then it drops off quickly from 8 onwards. 15 bits is enough
-///   for 99.99%+ of cases, but larger values (sometimes 20+ bits) might occur
-///   dozens of times in a typical crate.
-/// - `ctxt_or_tag` is 16 bits in `Span` and 32 bits in `SpanData`, which means that
-///   large `ctxt` values will cause interning. The number of bits needed for
-///   `ctxt` values depend partly on the crate size and partly on the form of
-///   the code. No crates in `rustc-perf` need more than 15 bits for `ctxt_or_tag`,
-///   but larger crates might need more than 16 bits.
+///   Having no compression on this field means there is no performance cliff
+///   if a crate exceeds a particular size.
+/// - `len` is ~15 bits in `Span` (a u16, minus 1 bit for PARENT_TAG) and 32
+///   bits in `SpanData`, which means that large `len` values will cause
+///   interning. The number of bits needed for `len` does not depend on the
+///   crate size. The most common numbers of bits for `len` are from 0 to 7,
+///   with a peak usually at 3 or 4, and then it drops off quickly from 8
+///   onwards. 15 bits is enough for 99.99%+ of cases, but larger values
+///   (sometimes 20+ bits) might occur dozens of times in a typical crate.
+/// - `ctxt_or_parent_or_marker` is 16 bits in `Span` and two 32 bit fields in
+///   `SpanData`, which means intering will happen if `ctxt` is large, if
+///   `parent` is large, or if both values are non-zero. The number of bits
+///   needed for `ctxt` values depend partly on the crate size and partly on
+///   the form of the code. No crates in `rustc-perf` need more than 15 bits
+///   for `ctxt_or_parent_or_marker`, but larger crates might need more than 16
+///   bits. The number of bits needed for `parent` hasn't been measured,
+///   because `parent` isn't currently used by default.
 ///
 /// In order to reliably use parented spans in incremental compilation,
 /// the dependency to the parent definition's span. This is performed
@@ -74,19 +78,22 @@ use rustc_data_structures::fx::FxIndexSet;
 #[derive(Clone, Copy, Eq, PartialEq, Hash)]
 #[rustc_pass_by_value]
 pub struct Span {
-    base_or_index: u32,
-    len_or_tag: u16,
-    ctxt_or_tag: u16,
+    lo_or_index: u32,
+    len_with_tag_or_marker: u16,
+    ctxt_or_parent_or_marker: u16,
 }
 
-const LEN_TAG: u16 = 0b1111_1111_1111_1111;
-const PARENT_MASK: u16 = 0b1000_0000_0000_0000;
-const MAX_LEN: u32 = 0b0111_1111_1111_1111;
-const CTXT_TAG: u32 = 0b1111_1111_1111_1111;
-const MAX_CTXT: u32 = CTXT_TAG - 1;
+// `MAX_LEN` is chosen so that `PARENT_TAG | MAX_LEN` is distinct from
+// `BASE_LEN_INTERNED_MARKER`. (If `MAX_LEN` was 1 higher, this wouldn't be true.)
+const MAX_LEN: u32 = 0b0111_1111_1111_1110;
+const MAX_CTXT: u32 = 0b0111_1111_1111_1110;
+const PARENT_TAG: u16 = 0b1000_0000_0000_0000;
+const BASE_LEN_INTERNED_MARKER: u16 = 0b1111_1111_1111_1111;
+const CTXT_INTERNED_MARKER: u16 = 0b1111_1111_1111_1111;
 
-/// Dummy span, both position and length are zero, syntax context is zero as well.
-pub const DUMMY_SP: Span = Span { base_or_index: 0, len_or_tag: 0, ctxt_or_tag: 0 };
+/// The dummy span has zero position, length, and context, and no parent.
+pub const DUMMY_SP: Span =
+    Span { lo_or_index: 0, len_with_tag_or_marker: 0, ctxt_or_parent_or_marker: 0 };
 
 impl Span {
     #[inline]
@@ -100,39 +107,43 @@ impl Span {
             std::mem::swap(&mut lo, &mut hi);
         }
 
-        let (base, len, ctxt2) = (lo.0, hi.0 - lo.0, ctxt.as_u32());
-
-        if len <= MAX_LEN && ctxt2 <= MAX_CTXT {
-            let len_or_tag = len as u16;
-            debug_assert_eq!(len_or_tag & PARENT_MASK, 0);
+        let (lo2, len, ctxt2) = (lo.0, hi.0 - lo.0, ctxt.as_u32());
 
-            if let Some(parent) = parent {
-                // Inline format with parent.
-                let len_or_tag = len_or_tag | PARENT_MASK;
-                let parent2 = parent.local_def_index.as_u32();
-                if ctxt2 == SyntaxContext::root().as_u32()
-                    && parent2 <= MAX_CTXT
-                    && len_or_tag < LEN_TAG
-                {
-                    debug_assert_ne!(len_or_tag, LEN_TAG);
-                    return Span { base_or_index: base, len_or_tag, ctxt_or_tag: parent2 as u16 };
-                }
-            } else {
-                // Inline format with ctxt.
-                debug_assert_ne!(len_or_tag, LEN_TAG);
+        if len <= MAX_LEN {
+            if ctxt2 <= MAX_CTXT && parent.is_none() {
+                // Inline-context format.
                 return Span {
-                    base_or_index: base,
-                    len_or_tag: len as u16,
-                    ctxt_or_tag: ctxt2 as u16,
+                    lo_or_index: lo2,
+                    len_with_tag_or_marker: len as u16,
+                    ctxt_or_parent_or_marker: ctxt2 as u16,
+                };
+            } else if ctxt2 == SyntaxContext::root().as_u32()
+                && let Some(parent) = parent
+                && let parent2 = parent.local_def_index.as_u32()
+                && parent2 <= MAX_CTXT
+            {
+                // Inline-parent format.
+                return Span {
+                    lo_or_index: lo2,
+                    len_with_tag_or_marker: PARENT_TAG | len as u16,
+                    ctxt_or_parent_or_marker: parent2 as u16
                 };
             }
         }
 
-        // Interned format.
+        // Partially-interned or fully-interned format.
         let index =
             with_span_interner(|interner| interner.intern(&SpanData { lo, hi, ctxt, parent }));
-        let ctxt_or_tag = if ctxt2 <= MAX_CTXT { ctxt2 } else { CTXT_TAG } as u16;
-        Span { base_or_index: index, len_or_tag: LEN_TAG, ctxt_or_tag }
+        let ctxt_or_parent_or_marker = if ctxt2 <= MAX_CTXT {
+            ctxt2 as u16 // partially-interned
+        } else {
+            CTXT_INTERNED_MARKER // fully-interned
+        };
+        Span {
+            lo_or_index: index,
+            len_with_tag_or_marker: BASE_LEN_INTERNED_MARKER,
+            ctxt_or_parent_or_marker,
+        }
     }
 
     #[inline]
@@ -148,56 +159,80 @@ impl Span {
     /// This function must not be used outside the incremental engine.
     #[inline]
     pub fn data_untracked(self) -> SpanData {
-        if self.len_or_tag != LEN_TAG {
-            // Inline format.
-            if self.len_or_tag & PARENT_MASK == 0 {
-                debug_assert!(self.len_or_tag as u32 <= MAX_LEN);
+        if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
+            if self.len_with_tag_or_marker & PARENT_TAG == 0 {
+                // Inline-context format.
+                let len = self.len_with_tag_or_marker as u32;
+                debug_assert!(len <= MAX_LEN);
                 SpanData {
-                    lo: BytePos(self.base_or_index),
-                    hi: BytePos(self.base_or_index + self.len_or_tag as u32),
-                    ctxt: SyntaxContext::from_u32(self.ctxt_or_tag as u32),
+                    lo: BytePos(self.lo_or_index),
+                    hi: BytePos(self.lo_or_index + len),
+                    ctxt: SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32),
                     parent: None,
                 }
             } else {
-                let len = self.len_or_tag & !PARENT_MASK;
-                debug_assert!(len as u32 <= MAX_LEN);
-                let parent =
-                    LocalDefId { local_def_index: DefIndex::from_u32(self.ctxt_or_tag as u32) };
+                // Inline-parent format.
+                let len = (self.len_with_tag_or_marker & !PARENT_TAG) as u32;
+                debug_assert!(len <= MAX_LEN);
+                let parent = LocalDefId {
+                    local_def_index: DefIndex::from_u32(self.ctxt_or_parent_or_marker as u32),
+                };
                 SpanData {
-                    lo: BytePos(self.base_or_index),
-                    hi: BytePos(self.base_or_index + len as u32),
+                    lo: BytePos(self.lo_or_index),
+                    hi: BytePos(self.lo_or_index + len),
                     ctxt: SyntaxContext::root(),
                     parent: Some(parent),
                 }
             }
         } else {
-            // Interned format.
-            let index = self.base_or_index;
+            // Fully-interned or partially-interned format. In either case,
+            // the interned value contains all the data, so we don't need to
+            // distinguish them.
+            let index = self.lo_or_index;
             with_span_interner(|interner| interner.spans[index as usize])
         }
     }
 
+    /// Returns `true` if this is a dummy span with any hygienic context.
+    #[inline]
+    pub fn is_dummy(self) -> bool {
+        if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
+            // Inline-context or inline-parent format.
+            let lo = self.lo_or_index;
+            let len = (self.len_with_tag_or_marker & !PARENT_TAG) as u32;
+            debug_assert!(len <= MAX_LEN);
+            lo == 0 && len == 0
+        } else {
+            // Fully-interned or partially-interned format.
+            let index = self.lo_or_index;
+            let data = with_span_interner(|interner| interner.spans[index as usize]);
+            data.lo == BytePos(0) && data.hi == BytePos(0)
+        }
+    }
+
     /// This function is used as a fast path when decoding the full `SpanData` is not necessary.
+    /// It's a cut-down version of `data_untracked`.
     #[inline]
     pub fn ctxt(self) -> SyntaxContext {
-        let ctxt_or_tag = self.ctxt_or_tag as u32;
-        // Check for interned format.
-        if self.len_or_tag == LEN_TAG {
-            if ctxt_or_tag == CTXT_TAG {
-                // Fully interned format.
-                let index = self.base_or_index;
-                with_span_interner(|interner| interner.spans[index as usize].ctxt)
+        if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
+            if self.len_with_tag_or_marker & PARENT_TAG == 0 {
+                // Inline-context format.
+                SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32)
             } else {
-                // Interned format with inline ctxt.
-                SyntaxContext::from_u32(ctxt_or_tag)
+                // Inline-parent format. We know that the SyntaxContext is root.
+                SyntaxContext::root()
             }
-        } else if self.len_or_tag & PARENT_MASK == 0 {
-            // Inline format with inline ctxt.
-            SyntaxContext::from_u32(ctxt_or_tag)
         } else {
-            // Inline format with inline parent.
-            // We know that the SyntaxContext is root.
-            SyntaxContext::root()
+            if self.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER {
+                // Partially-interned format. This path avoids looking up the
+                // interned value, and is the whole point of the
+                // partially-interned format.
+                SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32)
+            } else {
+                // Fully-interned format.
+                let index = self.lo_or_index;
+                with_span_interner(|interner| interner.spans[index as usize].ctxt)
+            }
         }
     }
 }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 656deebb5d0..382754be2ca 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -387,6 +387,7 @@ symbols! {
         asm_sym,
         asm_unwind,
         assert,
+        assert_eq,
         assert_eq_macro,
         assert_inhabited,
         assert_macro,
@@ -573,6 +574,8 @@ symbols! {
         cosf32,
         cosf64,
         count,
+        coverage,
+        coverage_attribute,
         cr,
         crate_id,
         crate_in_paths,
@@ -589,6 +592,7 @@ symbols! {
         cttz,
         cttz_nonzero,
         custom_attribute,
+        custom_code_classes_in_docs,
         custom_derive,
         custom_inner_attributes,
         custom_mir,
@@ -1068,6 +1072,7 @@ symbols! {
         note,
         object_safe_for_dispatch,
         of,
+        off,
         offset,
         offset_of,
         omit_gdb_pretty_printer_section,
@@ -1174,7 +1179,6 @@ symbols! {
         ptr_cast_const,
         ptr_cast_mut,
         ptr_const_is_null,
-        ptr_from_mut,
         ptr_from_ref,
         ptr_guaranteed_cmp,
         ptr_is_null,
@@ -1333,6 +1337,7 @@ symbols! {
         rustc_main,
         rustc_mir,
         rustc_must_implement_one_of,
+        rustc_never_returns_null_ptr,
         rustc_nonnull_optimization_guaranteed,
         rustc_nounwind,
         rustc_object_lifetime_default,
@@ -1639,6 +1644,7 @@ symbols! {
         unsafe_block_in_unsafe_fn,
         unsafe_cell,
         unsafe_cell_from_mut,
+        unsafe_cell_raw_get,
         unsafe_no_drop_flag,
         unsafe_pin_internals,
         unsize,
diff --git a/compiler/rustc_span/src/tests.rs b/compiler/rustc_span/src/tests.rs
index a980ee8d9e0..cb88fa89058 100644
--- a/compiler/rustc_span/src/tests.rs
+++ b/compiler/rustc_span/src/tests.rs
@@ -7,9 +7,7 @@ fn test_lookup_line() {
         SourceFile::new(FileName::Anon(Hash64::ZERO), source, SourceFileHashAlgorithm::Sha256)
             .unwrap();
     sf.start_pos = BytePos(3);
-    sf.lines(|lines| {
-        assert_eq!(lines, &[RelativeBytePos(0), RelativeBytePos(14), RelativeBytePos(25)])
-    });
+    assert_eq!(sf.lines(), &[RelativeBytePos(0), RelativeBytePos(14), RelativeBytePos(25)]);
 
     assert_eq!(sf.lookup_line(RelativeBytePos(0)), Some(0));
     assert_eq!(sf.lookup_line(RelativeBytePos(1)), Some(0));
diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs
index 3a33568084c..2fc102bda13 100644
--- a/compiler/rustc_symbol_mangling/src/legacy.rs
+++ b/compiler/rustc_symbol_mangling/src/legacy.rs
@@ -230,7 +230,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> {
                 self.write_str("[")?;
                 self = self.print_type(ty)?;
                 self.write_str("; ")?;
-                if let Some(size) = size.try_to_bits(self.tcx().data_layout.pointer_size) {
+                if let Some(size) = size.try_to_target_usize(self.tcx()) {
                     write!(self, "{size}")?
                 } else if let ty::ConstKind::Param(param) = size.kind() {
                     self = param.print(self)?
diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs
index 74538e9f5a3..535a3ea2d7e 100644
--- a/compiler/rustc_symbol_mangling/src/lib.rs
+++ b/compiler/rustc_symbol_mangling/src/lib.rs
@@ -108,7 +108,6 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
 use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
 use rustc_middle::query::Providers;
-use rustc_middle::ty::GenericArgsRef;
 use rustc_middle::ty::{self, Instance, TyCtxt};
 use rustc_session::config::SymbolManglingVersion;
 
@@ -144,7 +143,7 @@ fn symbol_name_provider<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty
         // This closure determines the instantiating crate for instances that
         // need an instantiating-crate-suffix for their symbol name, in order
         // to differentiate between local copies.
-        if is_generic(instance.args) {
+        if is_generic(instance, tcx) {
             // For generics we might find re-usable upstream instances. If there
             // is one, we rely on the symbol being instantiated locally.
             instance.upstream_monomorphization(tcx).unwrap_or(LOCAL_CRATE)
@@ -246,7 +245,7 @@ fn compute_symbol_name<'tcx>(
     // the ID of the instantiating crate. This avoids symbol conflicts
     // in case the same instances is emitted in two crates of the same
     // project.
-    let avoid_cross_crate_conflicts = is_generic(args) || is_globally_shared_function;
+    let avoid_cross_crate_conflicts = is_generic(instance, tcx) || is_globally_shared_function;
 
     let instantiating_crate = avoid_cross_crate_conflicts.then(compute_instantiating_crate);
 
@@ -278,6 +277,6 @@ fn compute_symbol_name<'tcx>(
     symbol
 }
 
-fn is_generic(args: GenericArgsRef<'_>) -> bool {
-    args.non_erasable_generics().next().is_some()
+fn is_generic<'tcx>(instance: Instance<'tcx>, tcx: TyCtxt<'tcx>) -> bool {
+    instance.args.non_erasable_generics(tcx, instance.def_id()).next().is_some()
 }
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index da19a3ba4fd..2550570af65 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -562,7 +562,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
 
     fn print_const(mut self, ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
         // We only mangle a typed value if the const can be evaluated.
-        let ct = ct.eval(self.tcx, ty::ParamEnv::reveal_all());
+        let ct = ct.normalize(self.tcx, ty::ParamEnv::reveal_all());
         match ct.kind() {
             ty::ConstKind::Value(_) => {}
 
diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs
index 8d573def9bb..56547bfb426 100644
--- a/compiler/rustc_target/src/abi/call/mod.rs
+++ b/compiler/rustc_target/src/abi/call/mod.rs
@@ -2,6 +2,7 @@ use crate::abi::{self, Abi, Align, FieldsShape, Size};
 use crate::abi::{HasDataLayout, TyAbiInterface, TyAndLayout};
 use crate::spec::{self, HasTargetSpec};
 use rustc_span::Symbol;
+use std::fmt;
 use std::str::FromStr;
 
 mod aarch64;
@@ -36,7 +37,10 @@ pub enum PassMode {
     Ignore,
     /// Pass the argument directly.
     ///
-    /// The argument has a layout abi of `Scalar`, `Vector` or in rare cases `Aggregate`.
+    /// The argument has a layout abi of `Scalar` or `Vector`.
+    /// Unfortunately due to past mistakes, in rare cases on wasm, it can also be `Aggregate`.
+    /// This is bad since it leaks LLVM implementation details into the ABI.
+    /// (Also see <https://github.com/rust-lang/rust/issues/115666>.)
     Direct(ArgAttributes),
     /// Pass a pair's elements directly in two arguments.
     ///
@@ -55,6 +59,29 @@ pub enum PassMode {
     Indirect { attrs: ArgAttributes, extra_attrs: Option<ArgAttributes>, on_stack: bool },
 }
 
+impl PassMode {
+    /// Checks if these two `PassMode` are equal enough to be considered "the same for all
+    /// function call ABIs". However, the `Layout` can also impact ABI decisions,
+    /// so that needs to be compared as well!
+    pub fn eq_abi(&self, other: &Self) -> bool {
+        match (self, other) {
+            (PassMode::Ignore, PassMode::Ignore) => true, // can still be reached for the return type
+            (PassMode::Direct(a1), PassMode::Direct(a2)) => a1.eq_abi(a2),
+            (PassMode::Pair(a1, b1), PassMode::Pair(a2, b2)) => a1.eq_abi(a2) && b1.eq_abi(b2),
+            (PassMode::Cast(c1, pad1), PassMode::Cast(c2, pad2)) => c1.eq_abi(c2) && pad1 == pad2,
+            (
+                PassMode::Indirect { attrs: a1, extra_attrs: None, on_stack: s1 },
+                PassMode::Indirect { attrs: a2, extra_attrs: None, on_stack: s2 },
+            ) => a1.eq_abi(a2) && s1 == s2,
+            (
+                PassMode::Indirect { attrs: a1, extra_attrs: Some(e1), on_stack: s1 },
+                PassMode::Indirect { attrs: a2, extra_attrs: Some(e2), on_stack: s2 },
+            ) => a1.eq_abi(a2) && e1.eq_abi(e2) && s1 == s2,
+            _ => false,
+        }
+    }
+}
+
 // Hack to disable non_upper_case_globals only for the bitflags! and not for the rest
 // of this module
 pub use attr_impl::ArgAttribute;
@@ -127,6 +154,24 @@ impl ArgAttributes {
     pub fn contains(&self, attr: ArgAttribute) -> bool {
         self.regular.contains(attr)
     }
+
+    /// Checks if these two `ArgAttributes` are equal enough to be considered "the same for all
+    /// function call ABIs".
+    pub fn eq_abi(&self, other: &Self) -> bool {
+        // There's only one regular attribute that matters for the call ABI: InReg.
+        // Everything else is things like noalias, dereferenceable, nonnull, ...
+        // (This also applies to pointee_size, pointee_align.)
+        if self.regular.contains(ArgAttribute::InReg) != other.regular.contains(ArgAttribute::InReg)
+        {
+            return false;
+        }
+        // We also compare the sign extension mode -- this could let the callee make assumptions
+        // about bits that conceptually were not even passed.
+        if self.arg_ext != other.arg_ext {
+            return false;
+        }
+        return true;
+    }
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
@@ -272,6 +317,14 @@ impl CastTarget {
                 acc.max(align)
             })
     }
+
+    /// Checks if these two `CastTarget` are equal enough to be considered "the same for all
+    /// function call ABIs".
+    pub fn eq_abi(&self, other: &Self) -> bool {
+        let CastTarget { prefix: prefix_l, rest: rest_l, attrs: attrs_l } = self;
+        let CastTarget { prefix: prefix_r, rest: rest_r, attrs: attrs_r } = other;
+        prefix_l == prefix_r && rest_l == rest_r && attrs_l.eq_abi(attrs_r)
+    }
 }
 
 /// Return value from the `homogeneous_aggregate` test function.
@@ -330,8 +383,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
     /// only a single type (e.g., `(u32, u32)`). Such aggregates are often
     /// special-cased in ABIs.
     ///
-    /// Note: We generally ignore fields of zero-sized type when computing
-    /// this value (see #56877).
+    /// 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
@@ -389,12 +441,18 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
                         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);
                             }
 
-                            let field = layout.field(cx, i);
-
                             result = result.merge(field.homogeneous_aggregate(cx)?)?;
 
                             // Keep track of the offset (without padding).
@@ -458,13 +516,22 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
 
 /// Information about how to pass an argument to,
 /// or return a value from, a function, under some ABI.
-#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+#[derive(Clone, PartialEq, Eq, Hash, HashStable_Generic)]
 pub struct ArgAbi<'a, Ty> {
     pub layout: TyAndLayout<'a, Ty>,
     pub mode: PassMode,
 }
 
+// Needs to be a custom impl because of the bounds on the `TyAndLayout` debug impl.
+impl<'a, Ty: fmt::Display> fmt::Debug for ArgAbi<'a, Ty> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let ArgAbi { layout, mode } = self;
+        f.debug_struct("ArgAbi").field("layout", layout).field("mode", mode).finish()
+    }
+}
+
 impl<'a, Ty> ArgAbi<'a, Ty> {
+    /// This defines the "default ABI" for that type, that is then later adjusted in `fn_abi_adjust_for_abi`.
     pub fn new(
         cx: &impl HasDataLayout,
         layout: TyAndLayout<'a, Ty>,
@@ -478,6 +545,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
                 scalar_attrs(&layout, b, a.size(cx).align_to(b.align(cx).abi)),
             ),
             Abi::Vector { .. } => PassMode::Direct(ArgAttributes::new()),
+            // The `Aggregate` ABI should always be adjusted later.
             Abi::Aggregate { .. } => PassMode::Direct(ArgAttributes::new()),
         };
         ArgAbi { layout, mode }
@@ -570,6 +638,14 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
     pub fn is_ignore(&self) -> bool {
         matches!(self.mode, PassMode::Ignore)
     }
+
+    /// Checks if these two `ArgAbi` are equal enough to be considered "the same for all
+    /// function call ABIs".
+    pub fn eq_abi(&self, other: &Self) -> bool {
+        // Ideally we'd just compare the `mode`, but that is not enough -- for some modes LLVM will look
+        // at the type.
+        self.layout.eq_abi(&other.layout) && self.mode.eq_abi(&other.mode)
+    }
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
@@ -627,7 +703,7 @@ impl RiscvInterruptKind {
 ///
 /// I will do my best to describe this structure, but these
 /// comments are reverse-engineered and may be inaccurate. -NDM
-#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+#[derive(Clone, PartialEq, Eq, Hash, HashStable_Generic)]
 pub struct FnAbi<'a, Ty> {
     /// The LLVM types of each argument.
     pub args: Box<[ArgAbi<'a, Ty>]>,
@@ -648,6 +724,21 @@ pub struct FnAbi<'a, Ty> {
     pub can_unwind: bool,
 }
 
+// Needs to be a custom impl because of the bounds on the `TyAndLayout` debug impl.
+impl<'a, Ty: fmt::Display> fmt::Debug for FnAbi<'a, Ty> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let FnAbi { args, ret, c_variadic, fixed_count, conv, can_unwind } = self;
+        f.debug_struct("FnAbi")
+            .field("args", args)
+            .field("ret", ret)
+            .field("c_variadic", c_variadic)
+            .field("fixed_count", fixed_count)
+            .field("conv", conv)
+            .field("can_unwind", can_unwind)
+            .finish()
+    }
+}
+
 /// Error produced by attempting to adjust a `FnAbi`, for a "foreign" ABI.
 #[derive(Copy, Clone, Debug, HashStable_Generic)]
 pub enum AdjustForForeignAbiError {
diff --git a/compiler/rustc_target/src/abi/call/wasm.rs b/compiler/rustc_target/src/abi/call/wasm.rs
index 0eb2309ecb2..796b752ff9d 100644
--- a/compiler/rustc_target/src/abi/call/wasm.rs
+++ b/compiler/rustc_target/src/abi/call/wasm.rs
@@ -61,6 +61,10 @@ where
 /// The purpose of this ABI is for matching the WebAssembly standard. This
 /// intentionally diverges from the C ABI and is specifically crafted to take
 /// advantage of LLVM's support of multiple returns in WebAssembly.
+///
+/// This ABI is *bad*! It uses `PassMode::Direct` for `abi::Aggregate` types, which leaks LLVM
+/// implementation details into the ABI. It's just hard to fix because ABIs are hard to change.
+/// Also see <https://github.com/rust-lang/rust/issues/115666>.
 pub fn compute_wasm_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
     if !fn_abi.ret.is_ignore() {
         classify_ret(&mut fn_abi.ret);
diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs
index dd435dbb0a3..636adcf6b17 100644
--- a/compiler/rustc_target/src/abi/mod.rs
+++ b/compiler/rustc_target/src/abi/mod.rs
@@ -3,6 +3,7 @@ pub use Primitive::*;
 
 use crate::json::{Json, ToJson};
 
+use std::fmt;
 use std::ops::Deref;
 
 use rustc_macros::HashStable_Generic;
@@ -24,12 +25,22 @@ impl ToJson for Endian {
 /// to that obtained from `layout_of(ty)`, as we need to produce
 /// layouts for which Rust types do not exist, such as enum variants
 /// or synthetic fields of enums (i.e., discriminants) and fat pointers.
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable_Generic)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)]
 pub struct TyAndLayout<'a, Ty> {
     pub ty: Ty,
     pub layout: Layout<'a>,
 }
 
+impl<'a, Ty: fmt::Display> fmt::Debug for TyAndLayout<'a, Ty> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        // Print the type in a readable way, not its debug representation.
+        f.debug_struct("TyAndLayout")
+            .field("ty", &format_args!("{}", self.ty))
+            .field("layout", &self.layout)
+            .finish()
+    }
+}
+
 impl<'a, Ty> Deref for TyAndLayout<'a, Ty> {
     type Target = &'a LayoutS;
     fn deref(&self) -> &&'a LayoutS {
@@ -144,4 +155,25 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
 
         offset
     }
+
+    /// Finds the one field that is not a 1-ZST.
+    /// Returns `None` if there are multiple non-1-ZST fields or only 1-ZST-fields.
+    pub fn non_1zst_field<C>(&self, cx: &C) -> Option<(usize, Self)>
+    where
+        Ty: TyAbiInterface<'a, C> + Copy,
+    {
+        let mut found = None;
+        for field_idx in 0..self.fields.count() {
+            let field = self.field(cx, field_idx);
+            if field.is_1zst() {
+                continue;
+            }
+            if found.is_some() {
+                // More than one non-1-ZST field.
+                return None;
+            }
+            found = Some((field_idx, field));
+        }
+        found
+    }
 }
diff --git a/compiler/rustc_target/src/spec/abi.rs b/compiler/rustc_target/src/spec/abi.rs
index 956a5cb5c2f..a99cccd42c4 100644
--- a/compiler/rustc_target/src/spec/abi.rs
+++ b/compiler/rustc_target/src/spec/abi.rs
@@ -68,7 +68,7 @@ pub enum Abi {
 impl Abi {
     pub fn supports_varargs(self) -> bool {
         // * C and Cdecl obviously support varargs.
-        // * C can be based on SysV64 or Win64, so they must support varargs.
+        // * C can be based on Aapcs, SysV64 or Win64, so they must support varargs.
         // * EfiApi is based on Win64 or C, so it also supports it.
         //
         // * Stdcall does not, because it would be impossible for the callee to clean
@@ -79,6 +79,7 @@ impl Abi {
         match self {
             Self::C { .. }
             | Self::Cdecl { .. }
+            | Self::Aapcs { .. }
             | Self::Win64 { .. }
             | Self::SysV64 { .. }
             | Self::EfiApi => true,
diff --git a/compiler/rustc_target/src/spec/i686_pc_windows_gnullvm.rs b/compiler/rustc_target/src/spec/i686_pc_windows_gnullvm.rs
new file mode 100644
index 00000000000..3154b512a52
--- /dev/null
+++ b/compiler/rustc_target/src/spec/i686_pc_windows_gnullvm.rs
@@ -0,0 +1,26 @@
+use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, Target};
+
+pub fn target() -> Target {
+    let mut base = super::windows_gnullvm_base::opts();
+    base.cpu = "pentium4".into();
+    base.max_atomic_width = Some(64);
+    base.frame_pointer = FramePointer::Always; // Required for backtraces
+    base.linker = Some("i686-w64-mingw32-clang".into());
+
+    // Mark all dynamic libraries and executables as compatible with the larger 4GiB address
+    // space available to x86 Windows binaries on x86_64.
+    base.add_pre_link_args(
+        LinkerFlavor::Gnu(Cc::No, Lld::No),
+        &["-m", "i386pe", "--large-address-aware"],
+    );
+
+    Target {
+        llvm_target: "i686-pc-windows-gnu".into(),
+        pointer_width: 32,
+        data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
+            i64:64-f80:32-n8:16:32-a:0:32-S32"
+            .into(),
+        arch: "x86".into(),
+        options: base,
+    }
+}
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index fca99381c0c..8aa72797a0d 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -1420,6 +1420,7 @@ supported_targets! {
     ("x86_64-uwp-windows-gnu", x86_64_uwp_windows_gnu),
 
     ("aarch64-pc-windows-gnullvm", aarch64_pc_windows_gnullvm),
+    ("i686-pc-windows-gnullvm", i686_pc_windows_gnullvm),
     ("x86_64-pc-windows-gnullvm", x86_64_pc_windows_gnullvm),
 
     ("aarch64-pc-windows-msvc", aarch64_pc_windows_msvc),
diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl
index 2a09a7dcd89..2db24c43734 100644
--- a/compiler/rustc_trait_selection/messages.ftl
+++ b/compiler/rustc_trait_selection/messages.ftl
@@ -40,5 +40,7 @@ trait_selection_no_value_in_rustc_on_unimplemented = this attribute must have a
     .label = expected value here
     .note = eg `#[rustc_on_unimplemented(message="foo")]`
 
+trait_selection_trait_has_no_impls = this trait has no implementations, consider adding one
+
 trait_selection_ty_alias_overflow = in case this is a recursive type alias, consider using a struct, enum, or union instead
 trait_selection_unable_to_construct_constant_value = unable to construct a constant value for the unevaluated constant {$unevaluated}
diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
index 6b839d64b87..f7031c5f493 100644
--- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs
+++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
@@ -125,7 +125,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         direction: ty::AliasRelationDirection,
         invert: Invert,
     ) -> QueryResult<'tcx> {
-        self.probe_candidate("normalizes-to").enter(|ecx| {
+        self.probe_misc_candidate("normalizes-to").enter(|ecx| {
             ecx.normalizes_to_inner(param_env, alias, other, direction, invert)?;
             ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
         })
@@ -175,7 +175,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         alias_rhs: ty::AliasTy<'tcx>,
         direction: ty::AliasRelationDirection,
     ) -> QueryResult<'tcx> {
-        self.probe_candidate("args relate").enter(|ecx| {
+        self.probe_misc_candidate("args relate").enter(|ecx| {
             match direction {
                 ty::AliasRelationDirection::Equate => {
                     ecx.eq(param_env, alias_lhs, alias_rhs)?;
@@ -196,7 +196,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         rhs: ty::Term<'tcx>,
         direction: ty::AliasRelationDirection,
     ) -> QueryResult<'tcx> {
-        self.probe_candidate("bidir normalizes-to").enter(|ecx| {
+        self.probe_misc_candidate("bidir normalizes-to").enter(|ecx| {
             ecx.normalizes_to_inner(
                 param_env,
                 lhs.to_alias_ty(ecx.tcx()).unwrap(),
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index 36194f973b5..128b9ad7e47 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -5,8 +5,10 @@ use crate::traits::coherence;
 use rustc_hir::def_id::DefId;
 use rustc_infer::traits::query::NoSolution;
 use rustc_infer::traits::Reveal;
-use rustc_middle::traits::solve::inspect::CandidateKind;
-use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
+use rustc_middle::traits::solve::inspect::ProbeKind;
+use rustc_middle::traits::solve::{
+    CandidateSource, CanonicalResponse, Certainty, Goal, QueryResult,
+};
 use rustc_middle::traits::BuiltinImplSource;
 use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams};
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -27,66 +29,6 @@ pub(super) struct Candidate<'tcx> {
     pub(super) result: CanonicalResponse<'tcx>,
 }
 
-/// Possible ways the given goal can be proven.
-#[derive(Debug, Clone, Copy)]
-pub(super) enum CandidateSource {
-    /// A user written impl.
-    ///
-    /// ## Examples
-    ///
-    /// ```rust
-    /// fn main() {
-    ///     let x: Vec<u32> = Vec::new();
-    ///     // This uses the impl from the standard library to prove `Vec<T>: Clone`.
-    ///     let y = x.clone();
-    /// }
-    /// ```
-    Impl(DefId),
-    /// A builtin impl generated by the compiler. When adding a new special
-    /// trait, try to use actual impls whenever possible. Builtin impls should
-    /// only be used in cases where the impl cannot be manually be written.
-    ///
-    /// Notable examples are auto traits, `Sized`, and `DiscriminantKind`.
-    /// For a list of all traits with builtin impls, check out the
-    /// [`EvalCtxt::assemble_builtin_impl_candidates`] method. Not
-    BuiltinImpl(BuiltinImplSource),
-    /// An assumption from the environment.
-    ///
-    /// More precisely we've used the `n-th` assumption in the `param_env`.
-    ///
-    /// ## Examples
-    ///
-    /// ```rust
-    /// fn is_clone<T: Clone>(x: T) -> (T, T) {
-    ///     // This uses the assumption `T: Clone` from the `where`-bounds
-    ///     // to prove `T: Clone`.
-    ///     (x.clone(), x)
-    /// }
-    /// ```
-    ParamEnv(usize),
-    /// If the self type is an alias type, e.g. an opaque type or a projection,
-    /// we know the bounds on that alias to hold even without knowing its concrete
-    /// underlying type.
-    ///
-    /// More precisely this candidate is using the `n-th` bound in the `item_bounds` of
-    /// the self type.
-    ///
-    /// ## Examples
-    ///
-    /// ```rust
-    /// trait Trait {
-    ///     type Assoc: Clone;
-    /// }
-    ///
-    /// fn foo<T: Trait>(x: <T as Trait>::Assoc) {
-    ///     // We prove `<T as Trait>::Assoc` by looking at the bounds on `Assoc` in
-    ///     // in the trait definition.
-    ///     let _y = x.clone();
-    /// }
-    /// ```
-    AliasBound,
-}
-
 /// Methods used to assemble candidates for either trait or projection goals.
 pub(super) trait GoalKind<'tcx>:
     TypeFoldable<TyCtxt<'tcx>> + Copy + Eq + std::fmt::Display
@@ -399,7 +341,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         let tcx = self.tcx();
         let &ty::Alias(_, projection_ty) = goal.predicate.self_ty().kind() else { return };
 
-        candidates.extend(self.probe(|_| CandidateKind::NormalizedSelfTyAssembly).enter(|ecx| {
+        candidates.extend(self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| {
             if num_steps < ecx.local_overflow_limit() {
                 let normalized_ty = ecx.next_ty_infer();
                 let normalizes_to_goal = goal.with(
@@ -910,7 +852,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             SolverMode::Coherence => {}
         };
 
-        let result = self.probe_candidate("coherence unknowable").enter(|ecx| {
+        let result = self.probe_misc_candidate("coherence unknowable").enter(|ecx| {
             let trait_ref = goal.predicate.trait_ref(tcx);
 
             #[derive(Debug)]
diff --git a/compiler/rustc_trait_selection/src/solve/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonicalize.rs
index a9d182abfb7..f5901057a9d 100644
--- a/compiler/rustc_trait_selection/src/solve/canonicalize.rs
+++ b/compiler/rustc_trait_selection/src/solve/canonicalize.rs
@@ -365,6 +365,17 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
                 // FIXME: we should fold this ty eventually
                 CanonicalVarKind::Const(ui, c.ty())
             }
+            ty::ConstKind::Infer(ty::InferConst::EffectVar(vid)) => {
+                assert_eq!(
+                    self.infcx.root_effect_var(vid),
+                    vid,
+                    "effect var should have been resolved"
+                );
+                let None = self.infcx.probe_effect_var(vid) else {
+                    bug!("effect var should have been resolved");
+                };
+                CanonicalVarKind::Effect
+            }
             ty::ConstKind::Infer(ty::InferConst::Fresh(_)) => {
                 bug!("fresh var during canonicalization: {c:?}")
             }
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
index 63ae83c8ef4..307c0516f70 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
@@ -237,7 +237,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
         tcx: TyCtxt<'tcx>,
         search_graph: &'a mut search_graph::SearchGraph<'tcx>,
         canonical_input: CanonicalInput<'tcx>,
-        goal_evaluation: &mut ProofTreeBuilder<'tcx>,
+        canonical_goal_evaluation: &mut ProofTreeBuilder<'tcx>,
         f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>, Goal<'tcx, ty::Predicate<'tcx>>) -> R,
     ) -> R {
         let intercrate = match search_graph.solver_mode() {
@@ -260,7 +260,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
             search_graph,
             nested_goals: NestedGoals::new(),
             tainted: Ok(()),
-            inspect: goal_evaluation.new_goal_evaluation_step(input),
+            inspect: canonical_goal_evaluation.new_goal_evaluation_step(input),
         };
 
         for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
@@ -274,7 +274,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
 
         let result = f(&mut ecx, input.goal);
 
-        goal_evaluation.goal_evaluation_step(ecx.inspect);
+        canonical_goal_evaluation.goal_evaluation_step(ecx.inspect);
 
         // When creating a query response we clone the opaque type constraints
         // instead of taking them. This would cause an ICE here, since we have
@@ -302,24 +302,25 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
         tcx: TyCtxt<'tcx>,
         search_graph: &'a mut search_graph::SearchGraph<'tcx>,
         canonical_input: CanonicalInput<'tcx>,
-        mut goal_evaluation: &mut ProofTreeBuilder<'tcx>,
+        goal_evaluation: &mut ProofTreeBuilder<'tcx>,
     ) -> QueryResult<'tcx> {
-        goal_evaluation.canonicalized_goal(canonical_input);
+        let mut canonical_goal_evaluation =
+            goal_evaluation.new_canonical_goal_evaluation(canonical_input);
 
         // Deal with overflow, caching, and coinduction.
         //
         // The actual solver logic happens in `ecx.compute_goal`.
-        ensure_sufficient_stack(|| {
+        let result = ensure_sufficient_stack(|| {
             search_graph.with_new_goal(
                 tcx,
                 canonical_input,
-                goal_evaluation,
-                |search_graph, goal_evaluation| {
+                &mut canonical_goal_evaluation,
+                |search_graph, canonical_goal_evaluation| {
                     EvalCtxt::enter_canonical(
                         tcx,
                         search_graph,
                         canonical_input,
-                        goal_evaluation,
+                        canonical_goal_evaluation,
                         |ecx, goal| {
                             let result = ecx.compute_goal(goal);
                             ecx.inspect.query_result(result);
@@ -328,7 +329,11 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
                     )
                 },
             )
-        })
+        });
+
+        canonical_goal_evaluation.query_result(result);
+        goal_evaluation.canonical_goal_evaluation(canonical_goal_evaluation);
+        result
     }
 
     /// Recursively evaluates `goal`, returning whether any inference vars have
@@ -347,7 +352,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
             canonical_goal,
             &mut goal_evaluation,
         );
-        goal_evaluation.query_result(canonical_response);
         let canonical_response = match canonical_response {
             Err(e) => {
                 self.inspect.goal_evaluation(goal_evaluation);
@@ -916,7 +920,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             if candidate_key.def_id != key.def_id {
                 continue;
             }
-            values.extend(self.probe_candidate("opaque type storage").enter(|ecx| {
+            values.extend(self.probe_misc_candidate("opaque type storage").enter(|ecx| {
                 for (a, b) in std::iter::zip(candidate_key.args, key.args) {
                     ecx.eq(param_env, a, b)?;
                 }
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
index 523841951b0..790f9b840f2 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
@@ -382,6 +382,17 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EagerResolver<'_, 'tcx> {
                     }
                 }
             }
+            ty::ConstKind::Infer(ty::InferConst::EffectVar(vid)) => {
+                debug_assert_eq!(c.ty(), self.infcx.tcx.types.bool);
+                match self.infcx.probe_effect_var(vid) {
+                    Some(c) => c.as_const(self.infcx.tcx),
+                    None => ty::Const::new_infer(
+                        self.infcx.tcx,
+                        ty::InferConst::EffectVar(self.infcx.root_effect_var(vid)),
+                        self.infcx.tcx.types.bool,
+                    ),
+                }
+            }
             _ => {
                 if c.has_infer() {
                     c.super_fold_with(self)
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
index 317c43baf8f..f88cfbac3f3 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
@@ -1,5 +1,5 @@
 use super::EvalCtxt;
-use rustc_middle::traits::solve::{inspect, QueryResult};
+use rustc_middle::traits::solve::{inspect, CandidateSource, QueryResult};
 use std::marker::PhantomData;
 
 pub(in crate::solve) struct ProbeCtxt<'me, 'a, 'tcx, F, T> {
@@ -10,7 +10,7 @@ pub(in crate::solve) struct ProbeCtxt<'me, 'a, 'tcx, F, T> {
 
 impl<'tcx, F, T> ProbeCtxt<'_, '_, 'tcx, F, T>
 where
-    F: FnOnce(&T) -> inspect::CandidateKind<'tcx>,
+    F: FnOnce(&T) -> inspect::ProbeKind<'tcx>,
 {
     pub(in crate::solve) fn enter(self, f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> T) -> T {
         let ProbeCtxt { ecx: outer_ecx, probe_kind, _result } = self;
@@ -28,8 +28,8 @@ where
         };
         let r = nested_ecx.infcx.probe(|_| f(&mut nested_ecx));
         if !outer_ecx.inspect.is_noop() {
-            let cand_kind = probe_kind(&r);
-            nested_ecx.inspect.candidate_kind(cand_kind);
+            let probe_kind = probe_kind(&r);
+            nested_ecx.inspect.probe_kind(probe_kind);
             outer_ecx.inspect.goal_candidate(nested_ecx.inspect);
         }
         r
@@ -41,25 +41,45 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
     /// as expensive as necessary to output the desired information.
     pub(in crate::solve) fn probe<F, T>(&mut self, probe_kind: F) -> ProbeCtxt<'_, 'a, 'tcx, F, T>
     where
-        F: FnOnce(&T) -> inspect::CandidateKind<'tcx>,
+        F: FnOnce(&T) -> inspect::ProbeKind<'tcx>,
     {
         ProbeCtxt { ecx: self, probe_kind, _result: PhantomData }
     }
 
-    pub(in crate::solve) fn probe_candidate(
+    pub(in crate::solve) fn probe_misc_candidate(
         &mut self,
         name: &'static str,
     ) -> ProbeCtxt<
         '_,
         'a,
         'tcx,
-        impl FnOnce(&QueryResult<'tcx>) -> inspect::CandidateKind<'tcx>,
+        impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>,
         QueryResult<'tcx>,
     > {
         ProbeCtxt {
             ecx: self,
-            probe_kind: move |result: &QueryResult<'tcx>| inspect::CandidateKind::Candidate {
-                name: name.to_string(),
+            probe_kind: move |result: &QueryResult<'tcx>| inspect::ProbeKind::MiscCandidate {
+                name,
+                result: *result,
+            },
+            _result: PhantomData,
+        }
+    }
+
+    pub(in crate::solve) fn probe_trait_candidate(
+        &mut self,
+        source: CandidateSource,
+    ) -> ProbeCtxt<
+        '_,
+        'a,
+        'tcx,
+        impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>,
+        QueryResult<'tcx>,
+    > {
+        ProbeCtxt {
+            ecx: self,
+            probe_kind: move |result: &QueryResult<'tcx>| inspect::ProbeKind::TraitCandidate {
+                source,
                 result: *result,
             },
             _result: PhantomData,
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
index 42d7a587cac..315df06be41 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
@@ -4,14 +4,14 @@ use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt};
 use rustc_infer::traits::{
     Obligation, PolyTraitObligation, PredicateObligation, Selection, SelectionResult, TraitEngine,
 };
-use rustc_middle::traits::solve::{CanonicalInput, Certainty, Goal};
+use rustc_middle::traits::solve::{CandidateSource, CanonicalInput, Certainty, Goal};
 use rustc_middle::traits::{
     BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, ObligationCause, SelectionError,
 };
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::DUMMY_SP;
 
-use crate::solve::assembly::{Candidate, CandidateSource};
+use crate::solve::assembly::Candidate;
 use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree};
 use crate::solve::inspect::ProofTreeBuilder;
 use crate::traits::StructurallyNormalizeExt;
diff --git a/compiler/rustc_trait_selection/src/solve/inspect.rs b/compiler/rustc_trait_selection/src/solve/inspect.rs
index cda68396321..46025da7683 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect.rs
@@ -1,5 +1,5 @@
 use rustc_middle::traits::query::NoSolution;
-use rustc_middle::traits::solve::inspect::{self, CacheHit, CandidateKind};
+use rustc_middle::traits::solve::inspect::{self, CacheHit, ProbeKind};
 use rustc_middle::traits::solve::{
     CanonicalInput, Certainty, Goal, IsNormalizesToHack, QueryInput, QueryResult,
 };
@@ -9,43 +9,60 @@ use rustc_session::config::DumpSolverProofTree;
 use super::eval_ctxt::UseGlobalCache;
 use super::GenerateProofTree;
 
-#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+#[derive(Eq, PartialEq, Debug)]
 pub struct WipGoalEvaluation<'tcx> {
     pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>,
-    pub canonicalized_goal: Option<CanonicalInput<'tcx>>,
-
-    pub evaluation_steps: Vec<WipGoalEvaluationStep<'tcx>>,
-
-    pub cache_hit: Option<CacheHit>,
+    pub evaluation: Option<WipCanonicalGoalEvaluation<'tcx>>,
     pub is_normalizes_to_hack: IsNormalizesToHack,
     pub returned_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
-
-    pub result: Option<QueryResult<'tcx>>,
 }
 
 impl<'tcx> WipGoalEvaluation<'tcx> {
     pub fn finalize(self) -> inspect::GoalEvaluation<'tcx> {
         inspect::GoalEvaluation {
             uncanonicalized_goal: self.uncanonicalized_goal,
-            canonicalized_goal: self.canonicalized_goal.unwrap(),
-            kind: match self.cache_hit {
-                Some(hit) => inspect::GoalEvaluationKind::CacheHit(hit),
-                None => inspect::GoalEvaluationKind::Uncached {
-                    revisions: self
-                        .evaluation_steps
-                        .into_iter()
-                        .map(WipGoalEvaluationStep::finalize)
-                        .collect(),
-                },
-            },
+            evaluation: self.evaluation.unwrap().finalize(),
             is_normalizes_to_hack: self.is_normalizes_to_hack,
             returned_goals: self.returned_goals,
-            result: self.result.unwrap(),
         }
     }
 }
 
-#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+#[derive(Eq, PartialEq, Debug)]
+pub enum WipGoalEvaluationKind {
+    Overflow,
+    CacheHit(CacheHit),
+}
+
+#[derive(Eq, PartialEq, Debug)]
+pub struct WipCanonicalGoalEvaluation<'tcx> {
+    pub goal: CanonicalInput<'tcx>,
+    pub kind: Option<WipGoalEvaluationKind>,
+    pub revisions: Vec<WipGoalEvaluationStep<'tcx>>,
+    pub result: Option<QueryResult<'tcx>>,
+}
+
+impl<'tcx> WipCanonicalGoalEvaluation<'tcx> {
+    pub fn finalize(self) -> inspect::CanonicalGoalEvaluation<'tcx> {
+        let kind = match self.kind {
+            Some(WipGoalEvaluationKind::Overflow) => inspect::GoalEvaluationKind::Overflow,
+            Some(WipGoalEvaluationKind::CacheHit(hit)) => {
+                inspect::GoalEvaluationKind::CacheHit(hit)
+            }
+            None => inspect::GoalEvaluationKind::Uncached {
+                revisions: self
+                    .revisions
+                    .into_iter()
+                    .map(WipGoalEvaluationStep::finalize)
+                    .collect(),
+            },
+        };
+
+        inspect::CanonicalGoalEvaluation { goal: self.goal, kind, result: self.result.unwrap() }
+    }
+}
+
+#[derive(Eq, PartialEq, Debug)]
 pub struct WipAddedGoalsEvaluation<'tcx> {
     pub evaluations: Vec<Vec<WipGoalEvaluation<'tcx>>>,
     pub result: Option<Result<Certainty, NoSolution>>,
@@ -66,43 +83,36 @@ impl<'tcx> WipAddedGoalsEvaluation<'tcx> {
     }
 }
 
-#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+#[derive(Eq, PartialEq, Debug)]
 pub struct WipGoalEvaluationStep<'tcx> {
     pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
 
-    pub nested_goal_evaluations: Vec<WipAddedGoalsEvaluation<'tcx>>,
-    pub candidates: Vec<WipGoalCandidate<'tcx>>,
-
-    pub result: Option<QueryResult<'tcx>>,
+    pub evaluation: WipGoalCandidate<'tcx>,
 }
 
 impl<'tcx> WipGoalEvaluationStep<'tcx> {
     pub fn finalize(self) -> inspect::GoalEvaluationStep<'tcx> {
-        inspect::GoalEvaluationStep {
-            instantiated_goal: self.instantiated_goal,
-            nested_goal_evaluations: self
-                .nested_goal_evaluations
-                .into_iter()
-                .map(WipAddedGoalsEvaluation::finalize)
-                .collect(),
-            candidates: self.candidates.into_iter().map(WipGoalCandidate::finalize).collect(),
-            result: self.result.unwrap(),
+        let evaluation = self.evaluation.finalize();
+        match evaluation.kind {
+            ProbeKind::Root { .. } => (),
+            _ => unreachable!("unexpected root evaluation: {evaluation:?}"),
         }
+        inspect::GoalEvaluationStep { instantiated_goal: self.instantiated_goal, evaluation }
     }
 }
 
-#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+#[derive(Eq, PartialEq, Debug)]
 pub struct WipGoalCandidate<'tcx> {
-    pub nested_goal_evaluations: Vec<WipAddedGoalsEvaluation<'tcx>>,
+    pub added_goals_evaluations: Vec<WipAddedGoalsEvaluation<'tcx>>,
     pub candidates: Vec<WipGoalCandidate<'tcx>>,
-    pub kind: Option<CandidateKind<'tcx>>,
+    pub kind: Option<ProbeKind<'tcx>>,
 }
 
 impl<'tcx> WipGoalCandidate<'tcx> {
     pub fn finalize(self) -> inspect::GoalCandidate<'tcx> {
         inspect::GoalCandidate {
-            nested_goal_evaluations: self
-                .nested_goal_evaluations
+            added_goals_evaluations: self
+                .added_goals_evaluations
                 .into_iter()
                 .map(WipAddedGoalsEvaluation::finalize)
                 .collect(),
@@ -116,6 +126,7 @@ impl<'tcx> WipGoalCandidate<'tcx> {
 pub enum DebugSolver<'tcx> {
     Root,
     GoalEvaluation(WipGoalEvaluation<'tcx>),
+    CanonicalGoalEvaluation(WipCanonicalGoalEvaluation<'tcx>),
     AddedGoalsEvaluation(WipAddedGoalsEvaluation<'tcx>),
     GoalEvaluationStep(WipGoalEvaluationStep<'tcx>),
     GoalCandidate(WipGoalCandidate<'tcx>),
@@ -127,6 +138,12 @@ impl<'tcx> From<WipGoalEvaluation<'tcx>> for DebugSolver<'tcx> {
     }
 }
 
+impl<'tcx> From<WipCanonicalGoalEvaluation<'tcx>> for DebugSolver<'tcx> {
+    fn from(g: WipCanonicalGoalEvaluation<'tcx>) -> DebugSolver<'tcx> {
+        DebugSolver::CanonicalGoalEvaluation(g)
+    }
+}
+
 impl<'tcx> From<WipAddedGoalsEvaluation<'tcx>> for DebugSolver<'tcx> {
     fn from(g: WipAddedGoalsEvaluation<'tcx>) -> DebugSolver<'tcx> {
         DebugSolver::AddedGoalsEvaluation(g)
@@ -164,11 +181,11 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         }
     }
 
-    fn nested(&self, state: impl Into<DebugSolver<'tcx>>) -> Self {
+    fn nested<T: Into<DebugSolver<'tcx>>>(&self, state: impl FnOnce() -> T) -> Self {
         match &self.state {
             Some(prev_state) => Self {
                 state: Some(Box::new(BuilderData {
-                    tree: state.into(),
+                    tree: state().into(),
                     use_global_cache: prev_state.use_global_cache,
                 })),
             },
@@ -237,37 +254,43 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         goal: Goal<'tcx, ty::Predicate<'tcx>>,
         is_normalizes_to_hack: IsNormalizesToHack,
     ) -> ProofTreeBuilder<'tcx> {
-        if self.state.is_none() {
-            return ProofTreeBuilder { state: None };
-        }
-
-        self.nested(WipGoalEvaluation {
+        self.nested(|| WipGoalEvaluation {
             uncanonicalized_goal: goal,
-            canonicalized_goal: None,
-            evaluation_steps: vec![],
+            evaluation: None,
             is_normalizes_to_hack,
-            cache_hit: None,
             returned_goals: vec![],
+        })
+    }
+
+    pub fn new_canonical_goal_evaluation(
+        &mut self,
+        goal: CanonicalInput<'tcx>,
+    ) -> ProofTreeBuilder<'tcx> {
+        self.nested(|| WipCanonicalGoalEvaluation {
+            goal,
+            kind: None,
+            revisions: vec![],
             result: None,
         })
     }
 
-    pub fn canonicalized_goal(&mut self, canonical_goal: CanonicalInput<'tcx>) {
+    pub fn canonical_goal_evaluation(&mut self, canonical_goal_evaluation: ProofTreeBuilder<'tcx>) {
         if let Some(this) = self.as_mut() {
-            match this {
-                DebugSolver::GoalEvaluation(goal_evaluation) => {
-                    assert_eq!(goal_evaluation.canonicalized_goal.replace(canonical_goal), None);
-                }
+            match (this, canonical_goal_evaluation.state.unwrap().tree) {
+                (
+                    DebugSolver::GoalEvaluation(goal_evaluation),
+                    DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation),
+                ) => goal_evaluation.evaluation = Some(canonical_goal_evaluation),
                 _ => unreachable!(),
             }
         }
     }
 
-    pub fn cache_hit(&mut self, cache_hit: CacheHit) {
+    pub fn goal_evaluation_kind(&mut self, kind: WipGoalEvaluationKind) {
         if let Some(this) = self.as_mut() {
             match this {
-                DebugSolver::GoalEvaluation(goal_evaluation) => {
-                    assert_eq!(goal_evaluation.cache_hit.replace(cache_hit), None);
+                DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => {
+                    assert_eq!(canonical_goal_evaluation.kind.replace(kind), None);
                 }
                 _ => unreachable!(),
             };
@@ -304,22 +327,23 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         &mut self,
         instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
     ) -> ProofTreeBuilder<'tcx> {
-        if self.state.is_none() {
-            return ProofTreeBuilder { state: None };
-        }
-
-        self.nested(WipGoalEvaluationStep {
+        self.nested(|| WipGoalEvaluationStep {
             instantiated_goal,
-            nested_goal_evaluations: vec![],
-            candidates: vec![],
-            result: None,
+            evaluation: WipGoalCandidate {
+                added_goals_evaluations: vec![],
+                candidates: vec![],
+                kind: None,
+            },
         })
     }
-    pub fn goal_evaluation_step(&mut self, goal_eval_step: ProofTreeBuilder<'tcx>) {
+    pub fn goal_evaluation_step(&mut self, goal_evaluation_step: ProofTreeBuilder<'tcx>) {
         if let Some(this) = self.as_mut() {
-            match (this, goal_eval_step.state.unwrap().tree) {
-                (DebugSolver::GoalEvaluation(goal_eval), DebugSolver::GoalEvaluationStep(step)) => {
-                    goal_eval.evaluation_steps.push(step);
+            match (this, goal_evaluation_step.state.unwrap().tree) {
+                (
+                    DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluations),
+                    DebugSolver::GoalEvaluationStep(goal_evaluation_step),
+                ) => {
+                    canonical_goal_evaluations.revisions.push(goal_evaluation_step);
                 }
                 _ => unreachable!(),
             }
@@ -327,22 +351,18 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
     }
 
     pub fn new_goal_candidate(&mut self) -> ProofTreeBuilder<'tcx> {
-        if self.state.is_none() {
-            return ProofTreeBuilder { state: None };
-        }
-
-        self.nested(WipGoalCandidate {
-            nested_goal_evaluations: vec![],
+        self.nested(|| WipGoalCandidate {
+            added_goals_evaluations: vec![],
             candidates: vec![],
             kind: None,
         })
     }
 
-    pub fn candidate_kind(&mut self, candidate_kind: CandidateKind<'tcx>) {
+    pub fn probe_kind(&mut self, probe_kind: ProbeKind<'tcx>) {
         if let Some(this) = self.as_mut() {
             match this {
                 DebugSolver::GoalCandidate(this) => {
-                    assert_eq!(this.kind.replace(candidate_kind), None)
+                    assert_eq!(this.kind.replace(probe_kind), None)
                 }
                 _ => unreachable!(),
             }
@@ -354,7 +374,10 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
             match (this, candidate.state.unwrap().tree) {
                 (
                     DebugSolver::GoalCandidate(WipGoalCandidate { candidates, .. })
-                    | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { candidates, .. }),
+                    | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
+                        evaluation: WipGoalCandidate { candidates, .. },
+                        ..
+                    }),
                     DebugSolver::GoalCandidate(candidate),
                 ) => candidates.push(candidate),
                 _ => unreachable!(),
@@ -363,11 +386,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
     }
 
     pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> {
-        if self.state.is_none() {
-            return ProofTreeBuilder { state: None };
-        }
-
-        self.nested(WipAddedGoalsEvaluation { evaluations: vec![], result: None })
+        self.nested(|| WipAddedGoalsEvaluation { evaluations: vec![], result: None })
     }
 
     pub fn evaluate_added_goals_loop_start(&mut self) {
@@ -392,19 +411,19 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         }
     }
 
-    pub fn added_goals_evaluation(&mut self, goals_evaluation: ProofTreeBuilder<'tcx>) {
+    pub fn added_goals_evaluation(&mut self, added_goals_evaluation: ProofTreeBuilder<'tcx>) {
         if let Some(this) = self.as_mut() {
-            match (this, goals_evaluation.state.unwrap().tree) {
+            match (this, added_goals_evaluation.state.unwrap().tree) {
                 (
                     DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
-                        nested_goal_evaluations,
+                        evaluation: WipGoalCandidate { added_goals_evaluations, .. },
                         ..
                     })
                     | DebugSolver::GoalCandidate(WipGoalCandidate {
-                        nested_goal_evaluations, ..
+                        added_goals_evaluations, ..
                     }),
                     DebugSolver::AddedGoalsEvaluation(added_goals_evaluation),
-                ) => nested_goal_evaluations.push(added_goals_evaluation),
+                ) => added_goals_evaluations.push(added_goals_evaluation),
                 _ => unreachable!(),
             }
         }
@@ -413,15 +432,16 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
     pub fn query_result(&mut self, result: QueryResult<'tcx>) {
         if let Some(this) = self.as_mut() {
             match this {
-                DebugSolver::GoalEvaluation(goal_evaluation) => {
-                    assert_eq!(goal_evaluation.result.replace(result), None);
+                DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => {
+                    assert_eq!(canonical_goal_evaluation.result.replace(result), None);
                 }
                 DebugSolver::GoalEvaluationStep(evaluation_step) => {
-                    assert_eq!(evaluation_step.result.replace(result), None);
+                    assert_eq!(
+                        evaluation_step.evaluation.kind.replace(ProbeKind::Root { result }),
+                        None
+                    );
                 }
-                DebugSolver::Root
-                | DebugSolver::AddedGoalsEvaluation(_)
-                | DebugSolver::GoalCandidate(_) => unreachable!(),
+                _ => unreachable!(),
             }
         }
     }
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index 75a99f799a2..c492408bc76 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -233,9 +233,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
 
     #[instrument(level = "debug", skip(self, goals))]
     fn add_goals(&mut self, goals: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>) {
-        let current_len = self.nested_goals.goals.len();
-        self.nested_goals.goals.extend(goals);
-        debug!("added_goals={:?}", &self.nested_goals.goals[current_len..]);
+        for goal in goals {
+            self.add_goal(goal);
+        }
     }
 
     /// Try to merge multiple possible ways to prove a goal, if that is not possible returns `None`.
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs
index df094af4f1f..3fe914e50be 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs
@@ -8,8 +8,9 @@ use rustc_hir::LangItem;
 use rustc_infer::traits::query::NoSolution;
 use rustc_infer::traits::specialization_graph::LeafDef;
 use rustc_infer::traits::Reveal;
-use rustc_middle::traits::solve::inspect::CandidateKind;
-use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
+use rustc_middle::traits::solve::{
+    CandidateSource, CanonicalResponse, Certainty, Goal, QueryResult,
+};
 use rustc_middle::traits::BuiltinImplSource;
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_middle::ty::ProjectionPredicate;
@@ -113,7 +114,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
         if let Some(projection_pred) = assumption.as_projection_clause() {
             if projection_pred.projection_def_id() == goal.predicate.def_id() {
                 let tcx = ecx.tcx();
-                ecx.probe_candidate("assumption").enter(|ecx| {
+                ecx.probe_misc_candidate("assumption").enter(|ecx| {
                     let assumption_projection_pred =
                         ecx.instantiate_binder_with_infer(projection_pred);
                     ecx.eq(
@@ -155,7 +156,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
             return Err(NoSolution);
         }
 
-        ecx.probe(|r| CandidateKind::Candidate { name: "impl".into(), result: *r }).enter(|ecx| {
+        ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
             let impl_args = ecx.fresh_args_for_item(impl_def_id);
             let impl_trait_ref = impl_trait_ref.instantiate(tcx, impl_args);
 
@@ -369,7 +370,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx> {
         let tcx = ecx.tcx();
-        ecx.probe_candidate("builtin pointee").enter(|ecx| {
+        ecx.probe_misc_candidate("builtin pointee").enter(|ecx| {
             let metadata_ty = match goal.predicate.self_ty().kind() {
                 ty::Bool
                 | ty::Char
@@ -578,7 +579,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
             ),
         };
 
-        ecx.probe_candidate("builtin discriminant kind").enter(|ecx| {
+        ecx.probe_misc_candidate("builtin discriminant kind").enter(|ecx| {
             ecx.eq(goal.param_env, goal.predicate.term, discriminant_ty.into())
                 .expect("expected goal term to be fully unconstrained");
             ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
index 52a11abd545..c816b51f67a 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
@@ -1,6 +1,7 @@
 mod cache;
 
 use self::cache::ProvisionalEntry;
+use super::inspect;
 use super::inspect::ProofTreeBuilder;
 use super::SolverMode;
 use cache::ProvisionalCache;
@@ -185,6 +186,8 @@ impl<'tcx> SearchGraph<'tcx> {
             if let Some(last) = self.stack.raw.last_mut() {
                 last.encountered_overflow = true;
             }
+
+            inspect.goal_evaluation_kind(inspect::WipGoalEvaluationKind::Overflow);
             return Self::response_no_constraints(tcx, input, Certainty::OVERFLOW);
         };
 
@@ -200,6 +203,9 @@ impl<'tcx> SearchGraph<'tcx> {
                     available_depth,
                 )
             {
+                inspect.goal_evaluation_kind(inspect::WipGoalEvaluationKind::CacheHit(
+                    CacheHit::Global,
+                ));
                 self.on_cache_hit(reached_depth, encountered_overflow);
                 return result;
             }
@@ -234,7 +240,9 @@ impl<'tcx> SearchGraph<'tcx> {
             // Finally we can return either the provisional response for that goal if we have a
             // coinductive cycle or an ambiguous result if the cycle is inductive.
             Entry::Occupied(entry_index) => {
-                inspect.cache_hit(CacheHit::Provisional);
+                inspect.goal_evaluation_kind(inspect::WipGoalEvaluationKind::CacheHit(
+                    CacheHit::Provisional,
+                ));
 
                 let entry_index = *entry_index.get();
                 let stack_depth = cache.depth(entry_index);
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index 8685f3100a8..f18eed94a68 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -5,7 +5,7 @@ use super::{EvalCtxt, SolverMode};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{LangItem, Movability};
 use rustc_infer::traits::query::NoSolution;
-use rustc_middle::traits::solve::inspect::CandidateKind;
+use rustc_middle::traits::solve::inspect::ProbeKind;
 use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
 use rustc_middle::traits::{BuiltinImplSource, Reveal};
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections};
@@ -61,7 +61,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
             },
         };
 
-        ecx.probe_candidate("impl").enter(|ecx| {
+        ecx.probe_misc_candidate("impl").enter(|ecx| {
             let impl_args = ecx.fresh_args_for_item(impl_def_id);
             let impl_trait_ref = impl_trait_ref.instantiate(tcx, impl_args);
 
@@ -96,7 +96,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
                 && trait_clause.polarity() == goal.predicate.polarity
             {
                 // FIXME: Constness
-                ecx.probe_candidate("assumption").enter(|ecx| {
+                ecx.probe_misc_candidate("assumption").enter(|ecx| {
                     let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause);
                     ecx.eq(
                         goal.param_env,
@@ -167,7 +167,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
 
         let tcx = ecx.tcx();
 
-        ecx.probe_candidate("trait alias").enter(|ecx| {
+        ecx.probe_misc_candidate("trait alias").enter(|ecx| {
             let nested_obligations = tcx
                 .predicates_of(goal.predicate.def_id())
                 .instantiate(tcx, goal.predicate.trait_ref.args);
@@ -427,7 +427,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx> {
-        ecx.probe(|_| CandidateKind::UnsizeAssembly).enter(|ecx| {
+        ecx.probe(|_| ProbeKind::UnsizeAssembly).enter(|ecx| {
             let a_ty = goal.predicate.self_ty();
             // We need to normalize the b_ty since it's destructured as a `dyn Trait`.
             let Some(b_ty) =
@@ -491,7 +491,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
             Err(NoSolution) => vec![],
         };
 
-        ecx.probe(|_| CandidateKind::UnsizeAssembly).enter(|ecx| {
+        ecx.probe(|_| ProbeKind::UnsizeAssembly).enter(|ecx| {
             let a_ty = goal.predicate.self_ty();
             // We need to normalize the b_ty since it's matched structurally
             // in the other functions below.
@@ -597,7 +597,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             self.walk_vtable(
                 a_principal.with_self_ty(tcx, a_ty),
                 |ecx, new_a_principal, _, vtable_vptr_slot| {
-                    if let Ok(resp) = ecx.probe_candidate("dyn upcast").enter(|ecx| {
+                    if let Ok(resp) = ecx.probe_misc_candidate("dyn upcast").enter(|ecx| {
                         ecx.consider_builtin_upcast_to_principal(
                             goal,
                             a_data,
@@ -640,7 +640,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
              target_projection: ty::PolyExistentialProjection<'tcx>| {
                 source_projection.item_def_id() == target_projection.item_def_id()
                     && ecx
-                        .probe(|_| CandidateKind::UpcastProbe)
+                        .probe(|_| ProbeKind::UpcastProjectionCompatibility)
                         .enter(|ecx| -> Result<(), NoSolution> {
                             ecx.eq(param_env, source_projection, target_projection)?;
                             let _ = ecx.try_evaluate_added_goals()?;
@@ -918,7 +918,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         goal: Goal<'tcx, TraitPredicate<'tcx>>,
         constituent_tys: impl Fn(&EvalCtxt<'_, 'tcx>, Ty<'tcx>) -> Result<Vec<Ty<'tcx>>, NoSolution>,
     ) -> QueryResult<'tcx> {
-        self.probe_candidate("constituent tys").enter(|ecx| {
+        self.probe_misc_candidate("constituent tys").enter(|ecx| {
             ecx.add_goals(
                 constituent_tys(ecx, goal.predicate.self_ty())?
                     .into_iter()
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 746a38f956a..135410691f1 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -2054,7 +2054,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                     tcx: self.tcx,
                     ty_op: |ty| ty,
                     lt_op: |lt| lt,
-                    ct_op: |ct| ct.eval(self.tcx, ty::ParamEnv::empty()),
+                    ct_op: |ct| ct.normalize(self.tcx, ty::ParamEnv::empty()),
                 });
                 cand
             })
@@ -3009,10 +3009,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         // Try to report a help message
         if is_fn_trait
             && let Ok((implemented_kind, params)) = self.type_implements_fn_trait(
-            obligation.param_env,
-            trait_ref.self_ty(),
-            trait_predicate.skip_binder().polarity,
-        )
+                obligation.param_env,
+                trait_ref.self_ty(),
+                trait_predicate.skip_binder().polarity,
+            )
         {
             self.add_help_message_for_fn_trait(trait_ref, err, implemented_kind, params);
         } else if !trait_ref.has_non_region_infer()
@@ -3031,6 +3031,15 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 None,
                 obligation.cause.body_id,
             );
+        } else if trait_ref.def_id().is_local()
+            && self.tcx.trait_impls_of(trait_ref.def_id()).is_empty()
+            && !self.tcx.trait_is_auto(trait_ref.def_id())
+            && !self.tcx.trait_is_alias(trait_ref.def_id())
+        {
+            err.span_help(
+                self.tcx.def_span(trait_ref.def_id()),
+                crate::fluent_generated::trait_selection_trait_has_no_impls,
+            );
         } else if !suggested && !unsatisfied_const {
             // Can't show anything else useful, try to find similar impls.
             let impl_candidates = self.find_similar_impl_candidates(*trait_predicate);
@@ -3041,7 +3050,12 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 err,
                 true,
             ) {
-                self.report_similar_impl_candidates_for_root_obligation(&obligation, *trait_predicate, body_def_id, err);
+                self.report_similar_impl_candidates_for_root_obligation(
+                    &obligation,
+                    *trait_predicate,
+                    body_def_id,
+                    err,
+                );
             }
 
             self.suggest_convert_to_slice(
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
index 0e73bad1918..fe71a9f07df 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
@@ -257,7 +257,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             // Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]`
             if let ty::Array(aty, len) = self_ty.kind() {
                 flags.push((sym::_Self, Some("[]".to_string())));
-                let len = len.try_to_value().and_then(|v| v.try_to_target_usize(self.tcx));
+                let len = len.try_to_valtree().and_then(|v| v.try_to_target_usize(self.tcx));
                 flags.push((sym::_Self, Some(format!("[{aty}; _]"))));
                 if let Some(n) = len {
                     flags.push((sym::_Self, Some(format!("[{aty}; {n}]"))));
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 611ec6b00ef..d6ac7208715 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -838,7 +838,20 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                         obligation.param_env,
                         real_trait_pred_and_base_ty,
                     );
-                    if self.predicate_may_hold(&obligation) {
+                    let sized_obligation = Obligation::new(
+                        self.tcx,
+                        obligation.cause.clone(),
+                        obligation.param_env,
+                        ty::TraitRef::from_lang_item(
+                            self.tcx,
+                            hir::LangItem::Sized,
+                            obligation.cause.span,
+                            [base_ty],
+                        ),
+                    );
+                    if self.predicate_may_hold(&obligation)
+                        && self.predicate_must_hold_modulo_regions(&sized_obligation)
+                    {
                         let call_node = self.tcx.hir().get(*call_hir_id);
                         let msg = "consider dereferencing here";
                         let is_receiver = matches!(
@@ -1792,7 +1805,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                         );
                     } else {
                         err.note(format!(
-                            "`{}` is implemented for `{:?}`, but not for `{:?}`",
+                            "`{}` is implemented for `{}`, but not for `{}`",
                             trait_pred.print_modifiers_and_trait_path(),
                             suggested_ty,
                             trait_pred.skip_binder().self_ty(),
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 3ebf1246a41..f1779451bc5 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -6,6 +6,7 @@ use rustc_infer::infer::DefineOpaqueTypes;
 use rustc_infer::traits::ProjectionCacheKey;
 use rustc_infer::traits::{PolyTraitObligation, SelectionError, TraitEngine};
 use rustc_middle::mir::interpret::ErrorHandled;
+use rustc_middle::traits::DefiningAnchor;
 use rustc_middle::ty::abstract_const::NotConstEvaluatable;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::GenericArgsRef;
@@ -623,9 +624,27 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                     }
                 }
                 ty::PredicateKind::Ambiguous => ProcessResult::Unchanged,
-                ty::PredicateKind::AliasRelate(..) => {
-                    bug!("AliasRelate is only used for new solver")
+                ty::PredicateKind::AliasRelate(..)
+                    if matches!(self.selcx.infcx.defining_use_anchor, DefiningAnchor::Bubble) =>
+                {
+                    ProcessResult::Unchanged
                 }
+                ty::PredicateKind::AliasRelate(a, b, relate) => match relate {
+                    ty::AliasRelationDirection::Equate => match self
+                        .selcx
+                        .infcx
+                        .at(&obligation.cause, obligation.param_env)
+                        .eq(DefineOpaqueTypes::Yes, a, b)
+                    {
+                        Ok(inf_ok) => ProcessResult::Changed(mk_pending(inf_ok.into_obligations())),
+                        Err(_) => ProcessResult::Error(FulfillmentErrorCode::CodeSelectionError(
+                            SelectionError::Unimplemented,
+                        )),
+                    },
+                    ty::AliasRelationDirection::Subtype => {
+                        bug!("AliasRelate with subtyping is only used for new solver")
+                    }
+                },
                 ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
                     match self.selcx.infcx.at(&obligation.cause, obligation.param_env).eq(
                         DefineOpaqueTypes::No,
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index d2210c6d5d9..956f8e047d7 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -288,7 +288,7 @@ pub fn normalize_param_env_or_error<'tcx>(
                     // should actually be okay since without `feature(generic_const_exprs)` the only
                     // const arguments that have a non-empty param env are array repeat counts. These
                     // do not appear in the type system though.
-                    c.eval(self.0, ty::ParamEnv::empty())
+                    c.normalize(self.0, ty::ParamEnv::empty())
                 }
             }
 
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index b31c0e655fb..6444c01a67b 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -761,7 +761,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
                 self.selcx.infcx,
                 &mut self.universes,
                 constant,
-                |constant| constant.eval(tcx, self.param_env),
+                |constant| constant.normalize(tcx, self.param_env),
             )
         }
     }
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index 87beaddc6c2..f785211c548 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -358,7 +358,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
             self.infcx,
             &mut self.universes,
             constant,
-            |constant| constant.eval(self.infcx.tcx, self.param_env),
+            |constant| constant.normalize(self.infcx.tcx, self.param_env),
         ))
     }
 
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 7e4d926dc8d..0d84fee8309 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -37,6 +37,7 @@ use rustc_infer::infer::LateBoundRegionConversionTime;
 use rustc_infer::traits::TraitObligation;
 use rustc_middle::dep_graph::{DepKind, DepNodeIndex};
 use rustc_middle::mir::interpret::ErrorHandled;
+use rustc_middle::traits::DefiningAnchor;
 use rustc_middle::ty::abstract_const::NotConstEvaluatable;
 use rustc_middle::ty::fold::BottomUpFolder;
 use rustc_middle::ty::relate::TypeRelation;
@@ -1000,9 +1001,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                         }
                     }
                 }
-                ty::PredicateKind::AliasRelate(..) => {
-                    bug!("AliasRelate is only used for new solver")
+                ty::PredicateKind::AliasRelate(..)
+                    if matches!(self.infcx.defining_use_anchor, DefiningAnchor::Bubble) =>
+                {
+                    Ok(EvaluatedToAmbig)
                 }
+                ty::PredicateKind::AliasRelate(a, b, relate) => match relate {
+                    ty::AliasRelationDirection::Equate => match self
+                        .infcx
+                        .at(&obligation.cause, obligation.param_env)
+                        .eq(DefineOpaqueTypes::Yes, a, b)
+                    {
+                        Ok(inf_ok) => self.evaluate_predicates_recursively(
+                            previous_stack,
+                            inf_ok.into_obligations(),
+                        ),
+                        Err(_) => Ok(EvaluatedToErr),
+                    },
+                    ty::AliasRelationDirection::Subtype => {
+                        bug!("AliasRelate subtyping is only used for new solver")
+                    }
+                },
                 ty::PredicateKind::Ambiguous => Ok(EvaluatedToAmbig),
                 ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
                     match self.infcx.at(&obligation.cause, obligation.param_env).eq(
diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs
index af2ad547480..6c49e94dc31 100644
--- a/compiler/rustc_transmute/src/lib.rs
+++ b/compiler/rustc_transmute/src/lib.rs
@@ -129,19 +129,16 @@ mod rustc {
             c: Const<'tcx>,
         ) -> Option<Self> {
             use rustc_middle::ty::ScalarInt;
-            use rustc_middle::ty::TypeVisitableExt;
             use rustc_span::symbol::sym;
 
-            let c = c.eval(tcx, param_env);
-
-            if let Err(err) = c.error_reported() {
+            let Ok(cv) = c.eval(tcx, param_env, None) else {
                 return Some(Self {
                     alignment: true,
                     lifetimes: true,
                     safety: true,
                     validity: true,
                 });
-            }
+            };
 
             let adt_def = c.ty().ty_adt_def()?;
 
@@ -153,8 +150,8 @@ mod rustc {
             );
 
             let variant = adt_def.non_enum_variant();
-            let fields = match c.try_to_valtree() {
-                Some(ValTree::Branch(branch)) => branch,
+            let fields = match cv {
+                ValTree::Branch(branch) => branch,
                 _ => {
                     return Some(Self {
                         alignment: true,
diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs
index 802391f1aad..16183403d67 100644
--- a/compiler/rustc_ty_utils/src/abi.rs
+++ b/compiler/rustc_ty_utils/src/abi.rs
@@ -520,6 +520,8 @@ fn fn_abi_adjust_for_abi<'tcx>(
 
                 _ => return,
             }
+            // `Aggregate` ABI must be adjusted to ensure that ABI-compatible Rust types are passed
+            // the same way.
 
             let size = arg.layout.size;
             if arg.layout.is_unsized() || size > Pointer(AddressSpace::DATA).size(cx) {
@@ -586,19 +588,11 @@ fn make_thin_self_ptr<'tcx>(
         // To get the type `*mut RcBox<Self>`, we just keep unwrapping newtypes until we
         // get a built-in pointer type
         let mut fat_pointer_layout = layout;
-        'descend_newtypes: while !fat_pointer_layout.ty.is_unsafe_ptr()
-            && !fat_pointer_layout.ty.is_ref()
-        {
-            for i in 0..fat_pointer_layout.fields.count() {
-                let field_layout = fat_pointer_layout.field(cx, i);
-
-                if !field_layout.is_1zst() {
-                    fat_pointer_layout = field_layout;
-                    continue 'descend_newtypes;
-                }
-            }
-
-            bug!("receiver has no non-1-ZST fields {:?}", fat_pointer_layout);
+        while !fat_pointer_layout.ty.is_unsafe_ptr() && !fat_pointer_layout.ty.is_ref() {
+            fat_pointer_layout = fat_pointer_layout
+                .non_1zst_field(cx)
+                .expect("not exactly one non-1-ZST field in a `DispatchFromDyn` type")
+                .1
         }
 
         fat_pointer_layout.ty
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index e1a15b5cf9f..da2958bf56e 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -55,6 +55,7 @@ fn resolve_instance<'tcx>(
             }
         } else {
             debug!(" => free item");
+            // FIXME(effects): we may want to erase the effect param if that is present on this item.
             ty::InstanceDef::Item(def_id)
         };
 
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index ed7b3496894..904f1b38740 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -7,6 +7,7 @@ use rustc_middle::query::Providers;
 use rustc_middle::ty::layout::{
     IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, MAX_SIMD_LANES,
 };
+use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{
     self, AdtDef, EarlyBinder, GenericArgsRef, ReprOptions, Ty, TyCtxt, TypeVisitableExt,
 };
@@ -937,7 +938,7 @@ fn record_layout_for_printing_outlined<'tcx>(
 
     // (delay format until we actually need it)
     let record = |kind, packed, opt_discr_size, variants| {
-        let type_desc = format!("{:?}", layout.ty);
+        let type_desc = with_no_trimmed_paths!(format!("{}", layout.ty));
         cx.tcx.sess.code_stats.record_type_size(
             kind,
             type_desc,
diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs
index 34fd31e49e1..1fc5d9359a4 100644
--- a/compiler/rustc_ty_utils/src/needs_drop.rs
+++ b/compiler/rustc_ty_utils/src/needs_drop.rs
@@ -19,13 +19,32 @@ fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>
     // needs drop.
     let adt_has_dtor =
         |adt_def: ty::AdtDef<'tcx>| adt_def.destructor(tcx).map(|_| DtorType::Significant);
-    let res =
-        drop_tys_helper(tcx, query.value, query.param_env, adt_has_dtor, false).next().is_some();
+    let res = drop_tys_helper(tcx, query.value, query.param_env, adt_has_dtor, false)
+        .filter(filter_array_elements(tcx, query.param_env))
+        .next()
+        .is_some();
 
     debug!("needs_drop_raw({:?}) = {:?}", query, res);
     res
 }
 
+/// HACK: in order to not mistakenly assume that `[PhantomData<T>; N]` requires drop glue
+/// we check the element type for drop glue. The correct fix would be looking at the
+/// entirety of the code around `needs_drop_components` and this file and come up with
+/// logic that is easier to follow while not repeating any checks that may thus diverge.
+fn filter_array_elements<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+) -> impl Fn(&Result<Ty<'tcx>, AlwaysRequiresDrop>) -> bool {
+    move |ty| match ty {
+        Ok(ty) => match *ty.kind() {
+            ty::Array(elem, _) => tcx.needs_drop_raw(param_env.and(elem)),
+            _ => true,
+        },
+        Err(AlwaysRequiresDrop) => true,
+    }
+}
+
 fn has_significant_drop_raw<'tcx>(
     tcx: TyCtxt<'tcx>,
     query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
@@ -37,6 +56,7 @@ fn has_significant_drop_raw<'tcx>(
         adt_consider_insignificant_dtor(tcx),
         true,
     )
+    .filter(filter_array_elements(tcx, query.param_env))
     .next()
     .is_some();
     debug!("has_significant_drop_raw({:?}) = {:?}", query, res);
diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs
index 371c6119122..e7a6831f5ee 100644
--- a/compiler/rustc_type_ir/src/fold.rs
+++ b/compiler/rustc_type_ir/src/fold.rs
@@ -11,7 +11,7 @@
 //! modification. These are the ones containing the most important type-related
 //! information, such as `Ty`, `Predicate`, `Region`, and `Const`.
 //!
-//! There are three groups of traits involved in each traversal.
+//! There are three traits involved in each traversal.
 //! - `TypeFoldable`. This is implemented once for many types, including:
 //!   - Types of interest, for which the methods delegate to the folder.
 //!   - All other types, including generic containers like `Vec` and `Option`.
@@ -51,6 +51,12 @@ use crate::{visit::TypeVisitable, Interner};
 ///
 /// To implement this conveniently, use the derive macro located in
 /// `rustc_macros`.
+///
+/// This trait is a sub-trait of `TypeVisitable`. This is because many
+/// `TypeFolder` instances use the methods in `TypeVisitableExt` while folding,
+/// which means in practice almost every foldable type needs to also be
+/// visitable. (However, there are some types that are visitable without being
+/// foldable.)
 pub trait TypeFoldable<I: Interner>: TypeVisitable<I> {
     /// The entry point for folding. To fold a value `t` with a folder `f`
     /// call: `t.try_fold_with(f)`.
@@ -58,7 +64,7 @@ pub trait TypeFoldable<I: Interner>: TypeVisitable<I> {
     /// For most types, this just traverses the value, calling `try_fold_with`
     /// on each field/element.
     ///
-    /// For types of interest (such as `Ty`), the implementation of method
+    /// For types of interest (such as `Ty`), the implementation of this method
     /// calls a folder method specifically for that type (such as
     /// `F::try_fold_ty`). This is where control transfers from `TypeFoldable`
     /// to `TypeFolder`.
@@ -121,7 +127,7 @@ pub trait TypeFolder<I: Interner>: FallibleTypeFolder<I, Error = !> {
     }
 
     // The default region folder is a no-op because `Region` is non-recursive
-    // and has no `super_visit_with` method to call. That also explains the
+    // and has no `super_fold_with` method to call. That also explains the
     // lack of `I::Region: TypeSuperFoldable<I>` bound on this method.
     fn fold_region(&mut self, r: I::Region) -> I::Region {
         r
@@ -170,7 +176,7 @@ pub trait FallibleTypeFolder<I: Interner>: Sized {
     }
 
     // The default region folder is a no-op because `Region` is non-recursive
-    // and has no `super_visit_with` method to call. That also explains the
+    // and has no `super_fold_with` method to call. That also explains the
     // lack of `I::Region: TypeSuperFoldable<I>` bound on this method.
     fn try_fold_region(&mut self, r: I::Region) -> Result<I::Region, Self::Error> {
         Ok(r)
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index e0abc7f04f5..e348591ebba 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -574,16 +574,16 @@ rustc_index::newtype_index! {
     pub struct TyVid {}
 }
 
-/// An **int**egral (`u32`, `i32`, `usize`, etc.) type **v**ariable **ID**.
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]
-pub struct IntVid {
-    pub index: u32,
+rustc_index::newtype_index! {
+    /// An **int**egral (`u32`, `i32`, `usize`, etc.) type **v**ariable **ID**.
+    #[debug_format = "?{}i"]
+    pub struct IntVid {}
 }
 
-/// An **float**ing-point (`f32` or `f64`) type **v**ariable **ID**.
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]
-pub struct FloatVid {
-    pub index: u32,
+rustc_index::newtype_index! {
+    /// A **float**ing-point (`f32` or `f64`) type **v**ariable **ID**.
+    #[debug_format = "?{}f"]
+    pub struct FloatVid {}
 }
 
 /// A placeholder for a type that hasn't been inferred yet.
@@ -645,11 +645,11 @@ impl UnifyKey for IntVid {
     type Value = Option<IntVarValue>;
     #[inline] // make this function eligible for inlining - it is quite hot.
     fn index(&self) -> u32 {
-        self.index
+        self.as_u32()
     }
     #[inline]
     fn from_index(i: u32) -> IntVid {
-        IntVid { index: i }
+        IntVid::from_u32(i)
     }
     fn tag() -> &'static str {
         "IntVid"
@@ -662,11 +662,11 @@ impl UnifyKey for FloatVid {
     type Value = Option<FloatVarValue>;
     #[inline]
     fn index(&self) -> u32 {
-        self.index
+        self.as_u32()
     }
     #[inline]
     fn from_index(i: u32) -> FloatVid {
-        FloatVid { index: i }
+        FloatVid::from_u32(i)
     }
     fn tag() -> &'static str {
         "FloatVid"
@@ -770,18 +770,6 @@ impl fmt::Debug for FloatVarValue {
     }
 }
 
-impl fmt::Debug for IntVid {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "?{}i", self.index)
-    }
-}
-
-impl fmt::Debug for FloatVid {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "?{}f", self.index)
-    }
-}
-
 impl fmt::Debug for Variance {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.write_str(match *self {
diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs
index 878c7aec6c1..891a4dda22f 100644
--- a/compiler/rustc_type_ir/src/visit.rs
+++ b/compiler/rustc_type_ir/src/visit.rs
@@ -8,7 +8,7 @@
 //! visitation. These are the ones containing the most important type-related
 //! information, such as `Ty`, `Predicate`, `Region`, and `Const`.
 //!
-//! There are three groups of traits involved in each traversal.
+//! There are three traits involved in each traversal.
 //! - `TypeVisitable`. This is implemented once for many types, including:
 //!   - Types of interest, for which the methods delegate to the visitor.
 //!   - All other types, including generic containers like `Vec` and `Option`.
@@ -17,7 +17,6 @@
 //!   interest, and defines the visiting "skeleton" for these types. (This
 //!   excludes `Region` because it is non-recursive, i.e. it never contains
 //!   other types of interest.)
-//!
 //! - `TypeVisitor`. This is implemented for each visitor. This defines how
 //!   types of interest are visited.
 //!
@@ -60,7 +59,7 @@ pub trait TypeVisitable<I: Interner>: fmt::Debug + Clone {
     ///
     /// For types of interest (such as `Ty`), the implementation of this method
     /// that calls a visitor method specifically for that type (such as
-    /// `V::visit_ty`). This is where control transfers from `TypeFoldable` to
+    /// `V::visit_ty`). This is where control transfers from `TypeVisitable` to
     /// `TypeVisitor`.
     fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy>;
 }
diff --git a/config.example.toml b/config.example.toml
index 5c4bee87553..2e0558c3f1b 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -294,9 +294,11 @@ changelog-seen = 2
 
 # Enable a build of the extended Rust tool set which is not only the compiler
 # but also tools such as Cargo. This will also produce "combined installers"
-# which are used to install Rust and Cargo together. This is disabled by
-# default. The `tools` option (immediately below) specifies which tools should
-# be built if `extended = true`.
+# which are used to install Rust and Cargo together.
+# The `tools` (check `config.example.toml` to see its default value) option specifies
+# which tools should be built if `extended = true`.
+#
+# This is disabled by default.
 #extended = false
 
 # Set of tools to be included in the installation.
diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs
index 5965ec2affa..4ef8af9b034 100644
--- a/library/alloc/src/collections/vec_deque/mod.rs
+++ b/library/alloc/src/collections/vec_deque/mod.rs
@@ -1015,8 +1015,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// Shortens the deque, keeping the first `len` elements and dropping
     /// the rest.
     ///
-    /// If `len` is greater than the deque's current length, this has no
-    /// effect.
+    /// If `len` is greater or equal to the deque's current length, this has
+    /// no effect.
     ///
     /// # Examples
     ///
diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs
index fb8d00e8d87..1e2c35bf735 100644
--- a/library/alloc/src/fmt.rs
+++ b/library/alloc/src/fmt.rs
@@ -177,8 +177,8 @@
 //! These are all flags altering the behavior of the formatter.
 //!
 //! * `+` - This is intended for numeric types and indicates that the sign
-//!         should always be printed. Positive signs are never printed by
-//!         default, and the negative sign is only printed by default for signed values.
+//!         should always be printed. By default only the negative sign of signed values
+//!         is printed, and the sign of positive or unsigned values is omitted.
 //!         This flag indicates that the correct sign (`+` or `-`) should always be printed.
 //! * `-` - Currently not used
 //! * `#` - This flag indicates that the "alternate" form of printing should
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index e43b6ac4039..f435f503fc1 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -144,7 +144,6 @@
 #![feature(ptr_metadata)]
 #![feature(ptr_sub_ptr)]
 #![feature(receiver_trait)]
-#![feature(saturating_int_impl)]
 #![feature(set_ptr_value)]
 #![feature(sized_type_properties)]
 #![feature(slice_from_ptr_range)]
diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs
index 3f19561e1ac..0f767df6063 100644
--- a/library/alloc/src/macros.rs
+++ b/library/alloc/src/macros.rs
@@ -88,15 +88,19 @@ macro_rules! vec {
 ///
 /// A common use for `format!` is concatenation and interpolation of strings.
 /// The same convention is used with [`print!`] and [`write!`] macros,
-/// depending on the intended destination of the string.
+/// depending on the intended destination of the string; all these macros internally use [`format_args!`].
 ///
 /// To convert a single value to a string, use the [`to_string`] method. This
 /// will use the [`Display`] formatting trait.
 ///
+/// To concatenate literals into a `&'static str`, use the [`concat!`] macro.
+///
 /// [`print!`]: ../std/macro.print.html
 /// [`write!`]: core::write
+/// [`format_args!`]: core::format_args
 /// [`to_string`]: crate::string::ToString
 /// [`Display`]: core::fmt::Display
+/// [`concat!`]: core::concat
 ///
 /// # Panics
 ///
@@ -107,11 +111,11 @@ macro_rules! vec {
 /// # Examples
 ///
 /// ```
-/// format!("test");
-/// format!("hello {}", "world!");
-/// format!("x = {}, y = {y}", 10, y = 30);
+/// format!("test");                             // => "test"
+/// format!("hello {}", "world!");               // => "hello world!"
+/// format!("x = {}, y = {val}", 10, val = 30);  // => "x = 10, y = 30"
 /// let (x, y) = (1, 2);
-/// format!("{x} + {y} = 3");
+/// format!("{x} + {y} = 3");                    // => "1 + 2 = 3"
 /// ```
 #[macro_export]
 #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs
index c485680f92e..0fb06b16655 100644
--- a/library/alloc/src/rc.rs
+++ b/library/alloc/src/rc.rs
@@ -1304,6 +1304,7 @@ impl<T: ?Sized, A: Allocator> Rc<T, A> {
     /// assert_eq!(unsafe { &*x_ptr }, "hello");
     /// ```
     #[stable(feature = "rc_raw", since = "1.17.0")]
+    #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
     pub fn into_raw(this: Self) -> *const T {
         let ptr = Self::as_ptr(&this);
         mem::forget(this);
@@ -1327,6 +1328,7 @@ impl<T: ?Sized, A: Allocator> Rc<T, A> {
     /// assert_eq!(unsafe { &*x_ptr }, "hello");
     /// ```
     #[stable(feature = "weak_into_raw", since = "1.45.0")]
+    #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
     pub fn as_ptr(this: &Self) -> *const T {
         let ptr: *mut RcBox<T> = NonNull::as_ptr(this.ptr);
 
diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs
index d3b7558440c..c53e9a5dd7a 100644
--- a/library/alloc/src/sync.rs
+++ b/library/alloc/src/sync.rs
@@ -1454,6 +1454,7 @@ impl<T: ?Sized, A: Allocator> Arc<T, A> {
     /// ```
     #[must_use = "losing the pointer will leak memory"]
     #[stable(feature = "rc_raw", since = "1.17.0")]
+    #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
     pub fn into_raw(this: Self) -> *const T {
         let ptr = Self::as_ptr(&this);
         mem::forget(this);
@@ -1478,6 +1479,7 @@ impl<T: ?Sized, A: Allocator> Arc<T, A> {
     /// ```
     #[must_use]
     #[stable(feature = "rc_as_ptr", since = "1.45.0")]
+    #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
     pub fn as_ptr(this: &Self) -> *const T {
         let ptr: *mut ArcInner<T> = NonNull::as_ptr(this.ptr);
 
diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs
index ae7bac0c85f..02331db3341 100644
--- a/library/alloc/src/vec/mod.rs
+++ b/library/alloc/src/vec/mod.rs
@@ -1110,8 +1110,8 @@ impl<T, A: Allocator> Vec<T, A> {
     /// Shortens the vector, keeping the first `len` elements and dropping
     /// the rest.
     ///
-    /// If `len` is greater than the vector's current length, this has no
-    /// effect.
+    /// If `len` is greater or equal to the vector's current length, this has
+    /// no effect.
     ///
     /// The [`drain`] method can emulate `truncate`, but causes the excess
     /// elements to be returned instead of dropped.
@@ -1258,6 +1258,7 @@ impl<T, A: Allocator> Vec<T, A> {
     /// [`as_mut_ptr`]: Vec::as_mut_ptr
     /// [`as_ptr`]: Vec::as_ptr
     #[stable(feature = "vec_as_ptr", since = "1.37.0")]
+    #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
     #[inline]
     pub fn as_ptr(&self) -> *const T {
         // We shadow the slice method of the same name to avoid going through
@@ -1317,6 +1318,7 @@ impl<T, A: Allocator> Vec<T, A> {
     /// [`as_mut_ptr`]: Vec::as_mut_ptr
     /// [`as_ptr`]: Vec::as_ptr
     #[stable(feature = "vec_as_ptr", since = "1.37.0")]
+    #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
     #[inline]
     pub fn as_mut_ptr(&mut self) -> *mut T {
         // We shadow the slice method of the same name to avoid going through
diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs
index 5378b210e67..cc872a5343d 100644
--- a/library/core/src/ascii/ascii_char.rs
+++ b/library/core/src/ascii/ascii_char.rs
@@ -3,7 +3,7 @@
 //! suggestions from rustc if you get anything slightly wrong in here, and overall
 //! helps with clarity as we're also referring to `char` intentionally in here.
 
-use crate::fmt;
+use crate::fmt::{self, Write};
 use crate::mem::transmute;
 
 /// One of the 128 Unicode characters from U+0000 through U+007F,
@@ -54,7 +54,7 @@ use crate::mem::transmute;
 /// [chart]: https://www.unicode.org/charts/PDF/U0000.pdf
 /// [NIST FIPS 1-2]: https://nvlpubs.nist.gov/nistpubs/Legacy/FIPS/fipspub1-2-1977.pdf
 /// [NamesList]: https://www.unicode.org/Public/15.0.0/ucd/NamesList.txt
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
 #[unstable(feature = "ascii_char", issue = "110998")]
 #[repr(u8)]
 pub enum AsciiChar {
@@ -563,3 +563,40 @@ impl fmt::Display for AsciiChar {
         <str as fmt::Display>::fmt(self.as_str(), f)
     }
 }
+
+#[unstable(feature = "ascii_char", issue = "110998")]
+impl fmt::Debug for AsciiChar {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        #[inline]
+        fn backslash(a: AsciiChar) -> ([AsciiChar; 4], u8) {
+            ([AsciiChar::ReverseSolidus, a, AsciiChar::Null, AsciiChar::Null], 2)
+        }
+
+        let (buf, len) = match self {
+            AsciiChar::Null => backslash(AsciiChar::Digit0),
+            AsciiChar::CharacterTabulation => backslash(AsciiChar::SmallT),
+            AsciiChar::CarriageReturn => backslash(AsciiChar::SmallR),
+            AsciiChar::LineFeed => backslash(AsciiChar::SmallN),
+            AsciiChar::ReverseSolidus => backslash(AsciiChar::ReverseSolidus),
+            AsciiChar::Apostrophe => backslash(AsciiChar::Apostrophe),
+            _ => {
+                let byte = self.to_u8();
+                if !byte.is_ascii_control() {
+                    ([*self, AsciiChar::Null, AsciiChar::Null, AsciiChar::Null], 1)
+                } else {
+                    const HEX_DIGITS: [AsciiChar; 16] = *b"0123456789abcdef".as_ascii().unwrap();
+
+                    let hi = HEX_DIGITS[usize::from(byte >> 4)];
+                    let lo = HEX_DIGITS[usize::from(byte & 0xf)];
+                    ([AsciiChar::ReverseSolidus, AsciiChar::SmallX, hi, lo], 4)
+                }
+            }
+        };
+
+        f.write_char('\'')?;
+        for byte in &buf[..len as usize] {
+            f.write_str(byte.as_str())?;
+        }
+        f.write_char('\'')
+    }
+}
diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs
index 20dce6516bf..3b4d99221f2 100644
--- a/library/core/src/cell.rs
+++ b/library/core/src/cell.rs
@@ -556,6 +556,7 @@ impl<T: ?Sized> Cell<T> {
     #[inline]
     #[stable(feature = "cell_as_ptr", since = "1.12.0")]
     #[rustc_const_stable(feature = "const_cell_as_ptr", since = "1.32.0")]
+    #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
     pub const fn as_ptr(&self) -> *mut T {
         self.value.get()
     }
@@ -1111,6 +1112,7 @@ impl<T: ?Sized> RefCell<T> {
     /// ```
     #[inline]
     #[stable(feature = "cell_as_ptr", since = "1.12.0")]
+    #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
     pub fn as_ptr(&self) -> *mut T {
         self.value.get()
     }
@@ -2105,6 +2107,7 @@ impl<T: ?Sized> UnsafeCell<T> {
     #[inline(always)]
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_const_stable(feature = "const_unsafecell_get", since = "1.32.0")]
+    #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
     pub const fn get(&self) -> *mut T {
         // We can just cast the pointer from `UnsafeCell<T>` to `T` because of
         // #[repr(transparent)]. This exploits std's special status, there is
@@ -2165,6 +2168,7 @@ impl<T: ?Sized> UnsafeCell<T> {
     #[inline(always)]
     #[stable(feature = "unsafe_cell_raw_get", since = "1.56.0")]
     #[rustc_const_stable(feature = "unsafe_cell_raw_get", since = "1.56.0")]
+    #[rustc_diagnostic_item = "unsafe_cell_raw_get"]
     pub const fn raw_get(this: *const Self) -> *mut T {
         // We can just cast the pointer from `UnsafeCell<T>` to `T` because of
         // #[repr(transparent)]. This exploits std's special status, there is
@@ -2247,6 +2251,7 @@ impl<T: ?Sized> SyncUnsafeCell<T> {
     /// when casting to `&mut T`, and ensure that there are no mutations
     /// or mutable aliases going on when casting to `&T`
     #[inline]
+    #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
     pub const fn get(&self) -> *mut T {
         self.value.get()
     }
diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs
index 515b8d20ead..4ac956e7b76 100644
--- a/library/core/src/char/methods.rs
+++ b/library/core/src/char/methods.rs
@@ -9,8 +9,58 @@ use crate::unicode::{self, conversions};
 use super::*;
 
 impl char {
+    /// The lowest valid code point a `char` can have, `'\0'`.
+    ///
+    /// Unlike integer types, `char` actually has a gap in the middle,
+    /// meaning that the range of possible `char`s is smaller than you
+    /// might expect. Ranges of `char` will automatically hop this gap
+    /// for you:
+    ///
+    /// ```
+    /// #![feature(char_min)]
+    /// let dist = u32::from(char::MAX) - u32::from(char::MIN);
+    /// let size = (char::MIN..=char::MAX).count() as u32;
+    /// assert!(size < dist);
+    /// ```
+    ///
+    /// Despite this gap, the `MIN` and [`MAX`] values can be used as bounds for
+    /// all `char` values.
+    ///
+    /// [`MAX`]: char::MAX
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(char_min)]
+    /// # fn something_which_returns_char() -> char { 'a' }
+    /// let c: char = something_which_returns_char();
+    /// assert!(char::MIN <= c);
+    ///
+    /// let value_at_min = u32::from(char::MIN);
+    /// assert_eq!(char::from_u32(value_at_min), Some('\0'));
+    /// ```
+    #[unstable(feature = "char_min", issue = "114298")]
+    pub const MIN: char = '\0';
+
     /// The highest valid code point a `char` can have, `'\u{10FFFF}'`.
     ///
+    /// Unlike integer types, `char` actually has a gap in the middle,
+    /// meaning that the range of possible `char`s is smaller than you
+    /// might expect. Ranges of `char` will automatically hop this gap
+    /// for you:
+    ///
+    /// ```
+    /// #![feature(char_min)]
+    /// let dist = u32::from(char::MAX) - u32::from(char::MIN);
+    /// let size = (char::MIN..=char::MAX).count() as u32;
+    /// assert!(size < dist);
+    /// ```
+    ///
+    /// Despite this gap, the [`MIN`] and `MAX` values can be used as bounds for
+    /// all `char` values.
+    ///
+    /// [`MIN`]: char::MIN
+    ///
     /// # Examples
     ///
     /// ```
@@ -18,7 +68,7 @@ impl char {
     /// let c: char = something_which_returns_char();
     /// assert!(c <= char::MAX);
     ///
-    /// let value_at_max = char::MAX as u32;
+    /// let value_at_max = u32::from(char::MAX);
     /// assert_eq!(char::from_u32(value_at_max), Some('\u{10FFFF}'));
     /// assert_eq!(char::from_u32(value_at_max + 1), None);
     /// ```
diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs
index 3c127efb390..739f0c360fd 100644
--- a/library/core/src/cmp.rs
+++ b/library/core/src/cmp.rs
@@ -63,6 +63,11 @@ use self::Ordering::*;
 /// (transitive) impls are not forced to exist, but these requirements apply
 /// whenever they do exist.
 ///
+/// Violating these requirements is a logic error. The behavior resulting from a logic error is not
+/// specified, but users of the trait must ensure that such logic errors do *not* result in
+/// undefined behavior. This means that `unsafe` code **must not** rely on the correctness of these
+/// methods.
+///
 /// ## Derivable
 ///
 /// This trait can be used with `#[derive]`. When `derive`d on structs, two
@@ -250,6 +255,11 @@ pub macro PartialEq($item:item) {
 /// This property cannot be checked by the compiler, and therefore `Eq` implies
 /// [`PartialEq`], and has no extra methods.
 ///
+/// Violating this property is a logic error. The behavior resulting from a logic error is not
+/// specified, but users of the trait must ensure that such logic errors do *not* result in
+/// undefined behavior. This means that `unsafe` code **must not** rely on the correctness of these
+/// methods.
+///
 /// ## Derivable
 ///
 /// This trait can be used with `#[derive]`. When `derive`d, because `Eq` has
@@ -289,7 +299,8 @@ pub trait Eq: PartialEq<Self> {
     //
     // This should never be implemented by hand.
     #[doc(hidden)]
-    #[no_coverage] // rust-lang/rust#84605
+    #[cfg_attr(bootstrap, no_coverage)] // rust-lang/rust#84605
+    #[cfg_attr(not(bootstrap), coverage(off))] //
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
     fn assert_receiver_is_total_eq(&self) {}
@@ -298,7 +309,9 @@ pub trait Eq: PartialEq<Self> {
 /// Derive macro generating an impl of the trait [`Eq`].
 #[rustc_builtin_macro]
 #[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
-#[allow_internal_unstable(core_intrinsics, derive_eq, structural_match, no_coverage)]
+#[allow_internal_unstable(core_intrinsics, derive_eq, structural_match)]
+#[cfg_attr(bootstrap, allow_internal_unstable(no_coverage))]
+#[cfg_attr(not(bootstrap), allow_internal_unstable(coverage_attribute))]
 pub macro Eq($item:item) {
     /* compiler built-in */
 }
@@ -656,6 +669,11 @@ impl<T: Clone> Clone for Reverse<T> {
 /// It's easy to accidentally make `cmp` and `partial_cmp` disagree by
 /// deriving some of the traits and manually implementing others.
 ///
+/// Violating these requirements is a logic error. The behavior resulting from a logic error is not
+/// specified, but users of the trait must ensure that such logic errors do *not* result in
+/// undefined behavior. This means that `unsafe` code **must not** rely on the correctness of these
+/// methods.
+///
 /// ## Corollaries
 ///
 /// From the above and the requirements of `PartialOrd`, it follows that `<` defines a strict total order.
@@ -889,6 +907,11 @@ pub macro Ord($item:item) {
 /// transitively: if `T: PartialOrd<U>` and `U: PartialOrd<V>` then `U: PartialOrd<T>` and `T:
 /// PartialOrd<V>`.
 ///
+/// Violating these requirements is a logic error. The behavior resulting from a logic error is not
+/// specified, but users of the trait must ensure that such logic errors do *not* result in
+/// undefined behavior. This means that `unsafe` code **must not** rely on the correctness of these
+/// methods.
+///
 /// ## Corollaries
 ///
 /// The following corollaries follow from the above requirements:
diff --git a/library/core/src/error.md b/library/core/src/error.md
index 7771b8adc92..a5deb71e6b8 100644
--- a/library/core/src/error.md
+++ b/library/core/src/error.md
@@ -37,7 +37,7 @@ responsibilities they cover:
 The panic and error systems are not entirely distinct. Often times errors
 that are anticipated runtime failures in an API might instead represent bugs
 to a caller. For these situations the standard library provides APIs for
-constructing panics with an `Error` as it's source.
+constructing panics with an `Error` as its source.
 
 * [`Result::unwrap`]
 * [`Result::expect`]
diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs
index 163a65c909e..7e4077db935 100644
--- a/library/core/src/ffi/c_str.rs
+++ b/library/core/src/ffi/c_str.rs
@@ -511,6 +511,7 @@ impl CStr {
     #[must_use]
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_const_stable(feature = "const_str_as_ptr", since = "1.32.0")]
+    #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
     pub const fn as_ptr(&self) -> *const c_char {
         self.inner.as_ptr()
     }
diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs
index d2c9f980042..9227248041e 100644
--- a/library/core/src/fmt/builders.rs
+++ b/library/core/src/fmt/builders.rs
@@ -40,6 +40,14 @@ impl fmt::Write for PadAdapter<'_, '_> {
 
         Ok(())
     }
+
+    fn write_char(&mut self, c: char) -> fmt::Result {
+        if self.state.on_newline {
+            self.buf.write_str("    ")?;
+        }
+        self.state.on_newline = c == '\n';
+        self.buf.write_char(c)
+    }
 }
 
 /// A struct to help with [`fmt::Debug`](Debug) implementations.
diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs
index 794a57f0922..35b757dc1ee 100644
--- a/library/core/src/hash/mod.rs
+++ b/library/core/src/hash/mod.rs
@@ -153,6 +153,11 @@ mod sip;
 /// Thankfully, you won't need to worry about upholding this property when
 /// deriving both [`Eq`] and `Hash` with `#[derive(PartialEq, Eq, Hash)]`.
 ///
+/// Violating this property is a logic error. The behavior resulting from a logic error is not
+/// specified, but users of the trait must ensure that such logic errors do *not* result in
+/// undefined behavior. This means that `unsafe` code **must not** rely on the correctness of these
+/// methods.
+///
 /// ## Prefix collisions
 ///
 /// Implementations of `hash` should ensure that the data they
diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs
index 44feb0a5638..0e03d0c2d4e 100644
--- a/library/core/src/iter/range.rs
+++ b/library/core/src/iter/range.rs
@@ -1,6 +1,7 @@
 use crate::ascii::Char as AsciiChar;
 use crate::convert::TryFrom;
 use crate::mem;
+use crate::net::{Ipv4Addr, Ipv6Addr};
 use crate::num::NonZeroUsize;
 use crate::ops::{self, Try};
 
@@ -15,7 +16,7 @@ macro_rules! unsafe_impl_trusted_step {
         unsafe impl TrustedStep for $type {}
     )*};
 }
-unsafe_impl_trusted_step![AsciiChar char i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize];
+unsafe_impl_trusted_step![AsciiChar char i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize Ipv4Addr Ipv6Addr];
 
 /// Objects that have a notion of *successor* and *predecessor* operations.
 ///
@@ -527,6 +528,70 @@ impl Step for AsciiChar {
     }
 }
 
+#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
+impl Step for Ipv4Addr {
+    #[inline]
+    fn steps_between(&start: &Ipv4Addr, &end: &Ipv4Addr) -> Option<usize> {
+        u32::steps_between(&start.to_bits(), &end.to_bits())
+    }
+
+    #[inline]
+    fn forward_checked(start: Ipv4Addr, count: usize) -> Option<Ipv4Addr> {
+        u32::forward_checked(start.to_bits(), count).map(Ipv4Addr::from_bits)
+    }
+
+    #[inline]
+    fn backward_checked(start: Ipv4Addr, count: usize) -> Option<Ipv4Addr> {
+        u32::backward_checked(start.to_bits(), count).map(Ipv4Addr::from_bits)
+    }
+
+    #[inline]
+    unsafe fn forward_unchecked(start: Ipv4Addr, count: usize) -> Ipv4Addr {
+        // SAFETY: Since u32 and Ipv4Addr are losslessly convertible,
+        //   this is as safe as the u32 version.
+        Ipv4Addr::from_bits(unsafe { u32::forward_unchecked(start.to_bits(), count) })
+    }
+
+    #[inline]
+    unsafe fn backward_unchecked(start: Ipv4Addr, count: usize) -> Ipv4Addr {
+        // SAFETY: Since u32 and Ipv4Addr are losslessly convertible,
+        //   this is as safe as the u32 version.
+        Ipv4Addr::from_bits(unsafe { u32::backward_unchecked(start.to_bits(), count) })
+    }
+}
+
+#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
+impl Step for Ipv6Addr {
+    #[inline]
+    fn steps_between(&start: &Ipv6Addr, &end: &Ipv6Addr) -> Option<usize> {
+        u128::steps_between(&start.to_bits(), &end.to_bits())
+    }
+
+    #[inline]
+    fn forward_checked(start: Ipv6Addr, count: usize) -> Option<Ipv6Addr> {
+        u128::forward_checked(start.to_bits(), count).map(Ipv6Addr::from_bits)
+    }
+
+    #[inline]
+    fn backward_checked(start: Ipv6Addr, count: usize) -> Option<Ipv6Addr> {
+        u128::backward_checked(start.to_bits(), count).map(Ipv6Addr::from_bits)
+    }
+
+    #[inline]
+    unsafe fn forward_unchecked(start: Ipv6Addr, count: usize) -> Ipv6Addr {
+        // SAFETY: Since u128 and Ipv6Addr are losslessly convertible,
+        //   this is as safe as the u128 version.
+        Ipv6Addr::from_bits(unsafe { u128::forward_unchecked(start.to_bits(), count) })
+    }
+
+    #[inline]
+    unsafe fn backward_unchecked(start: Ipv6Addr, count: usize) -> Ipv6Addr {
+        // SAFETY: Since u128 and Ipv6Addr are losslessly convertible,
+        //   this is as safe as the u128 version.
+        Ipv6Addr::from_bits(unsafe { u128::backward_unchecked(start.to_bits(), count) })
+    }
+}
+
 macro_rules! range_exact_iter_impl {
     ($($t:ty)*) => ($(
         #[stable(feature = "rust1", since = "1.0.0")]
@@ -766,6 +831,15 @@ impl<A: Step> Iterator for ops::Range<A> {
     }
 
     #[inline]
+    fn count(self) -> usize {
+        if self.start < self.end {
+            Step::steps_between(&self.start, &self.end).expect("count overflowed usize")
+        } else {
+            0
+        }
+    }
+
+    #[inline]
     fn nth(&mut self, n: usize) -> Option<A> {
         self.spec_nth(n)
     }
@@ -1163,6 +1237,17 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> {
     }
 
     #[inline]
+    fn count(self) -> usize {
+        if self.is_empty() {
+            return 0;
+        }
+
+        Step::steps_between(&self.start, &self.end)
+            .and_then(|steps| steps.checked_add(1))
+            .expect("count overflowed usize")
+    }
+
+    #[inline]
     fn nth(&mut self, n: usize) -> Option<A> {
         if self.is_empty() {
             return None;
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index c3edd3a9d7d..b5458e667ed 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -110,6 +110,8 @@
 //
 // Library features:
 // tidy-alphabetical-start
+#![cfg_attr(bootstrap, feature(no_coverage))] // rust-lang/rust#84605
+#![cfg_attr(not(bootstrap), feature(coverage_attribute))] // rust-lang/rust#84605
 #![feature(char_indices_offset)]
 #![feature(const_align_of_val)]
 #![feature(const_align_of_val_raw)]
@@ -164,7 +166,6 @@
 #![feature(const_slice_split_at_mut)]
 #![feature(const_str_from_utf8_unchecked_mut)]
 #![feature(const_swap)]
-#![feature(const_transmute_copy)]
 #![feature(const_try)]
 #![feature(const_type_id)]
 #![feature(const_type_name)]
@@ -235,7 +236,6 @@
 #![feature(negative_impls)]
 #![feature(never_type)]
 #![feature(no_core)]
-#![feature(no_coverage)] // rust-lang/rust#84605
 #![feature(platform_intrinsics)]
 #![feature(prelude_import)]
 #![feature(repr_simd)]
diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs
index 46628bcea00..646100fe27b 100644
--- a/library/core/src/macros/mod.rs
+++ b/library/core/src/macros/mod.rs
@@ -1044,7 +1044,7 @@ pub(crate) mod builtin {
     /// expression of type `&'static str` which represents all of the literals
     /// concatenated left-to-right.
     ///
-    /// Integer and floating point literals are stringified in order to be
+    /// Integer and floating point literals are [stringified](core::stringify) in order to be
     /// concatenated.
     ///
     /// # Examples
diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs
index 732fcce0f29..d7abc9a0e23 100644
--- a/library/core/src/mem/mod.rs
+++ b/library/core/src/mem/mod.rs
@@ -1051,7 +1051,7 @@ pub const fn copy<T: Copy>(x: &T) -> T {
 #[inline]
 #[must_use]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[rustc_const_unstable(feature = "const_transmute_copy", issue = "83165")]
+#[rustc_const_stable(feature = "const_transmute_copy", since = "CURRENT_RUSTC_VERSION")]
 pub const unsafe fn transmute_copy<Src, Dst>(src: &Src) -> Dst {
     assert!(
         size_of::<Src>() >= size_of::<Dst>(),
@@ -1126,6 +1126,11 @@ impl<T> fmt::Debug for Discriminant<T> {
 ///
 /// [Reference]: ../../reference/items/enumerations.html#custom-discriminant-values-for-fieldless-enumerations
 ///
+/// The value of a [`Discriminant<T>`] is independent of any *lifetimes* in `T`. As such, reading
+/// or writing a `Discriminant<Foo<'a>>` as a `Discriminant<Foo<'b>>` (whether via [`transmute`] or
+/// otherwise) is always sound. Note that this is **not** true for other kinds of generic
+/// parameters; `Discriminant<Foo<A>>` and `Discriminant<Foo<B>>` might be incompatible.
+///
 /// # Examples
 ///
 /// This can be used to compare enums that carry data, while disregarding
diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs
index 95dcaf5dd73..4232319fecb 100644
--- a/library/core/src/num/mod.rs
+++ b/library/core/src/num/mod.rs
@@ -44,11 +44,10 @@ mod uint_macros; // import uint_impl!
 mod error;
 mod int_log10;
 mod nonzero;
-#[unstable(feature = "saturating_int_impl", issue = "87920")]
 mod saturating;
 mod wrapping;
 
-#[unstable(feature = "saturating_int_impl", issue = "87920")]
+#[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
 pub use saturating::Saturating;
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use wrapping::Wrapping;
diff --git a/library/core/src/num/saturating.rs b/library/core/src/num/saturating.rs
index e9f794e7d62..5757e7498ad 100644
--- a/library/core/src/num/saturating.rs
+++ b/library/core/src/num/saturating.rs
@@ -4,7 +4,7 @@ use crate::fmt;
 use crate::ops::{Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign};
 use crate::ops::{BitXor, BitXorAssign, Div, DivAssign};
 use crate::ops::{Mul, MulAssign, Neg, Not, Rem, RemAssign};
-use crate::ops::{Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign};
+use crate::ops::{Sub, SubAssign};
 
 /// Provides intentionally-saturating arithmetic on `T`.
 ///
@@ -24,7 +24,6 @@ use crate::ops::{Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign};
 /// # Examples
 ///
 /// ```
-/// #![feature(saturating_int_impl)]
 /// use std::num::Saturating;
 ///
 /// let max = Saturating(u32::MAX);
@@ -32,181 +31,186 @@ use crate::ops::{Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign};
 ///
 /// assert_eq!(u32::MAX, (max + one).0);
 /// ```
-#[unstable(feature = "saturating_int_impl", issue = "87920")]
+#[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
 #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default, Hash)]
 #[repr(transparent)]
 #[rustc_diagnostic_item = "Saturating"]
-pub struct Saturating<T>(#[unstable(feature = "saturating_int_impl", issue = "87920")] pub T);
+pub struct Saturating<T>(
+    #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] pub T,
+);
 
-#[unstable(feature = "saturating_int_impl", issue = "87920")]
+#[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
 impl<T: fmt::Debug> fmt::Debug for Saturating<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.0.fmt(f)
     }
 }
 
-#[unstable(feature = "saturating_int_impl", issue = "87920")]
+#[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
 impl<T: fmt::Display> fmt::Display for Saturating<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.0.fmt(f)
     }
 }
 
-#[unstable(feature = "saturating_int_impl", issue = "87920")]
+#[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
 impl<T: fmt::Binary> fmt::Binary for Saturating<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.0.fmt(f)
     }
 }
 
-#[unstable(feature = "saturating_int_impl", issue = "87920")]
+#[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
 impl<T: fmt::Octal> fmt::Octal for Saturating<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.0.fmt(f)
     }
 }
 
-#[unstable(feature = "saturating_int_impl", issue = "87920")]
+#[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
 impl<T: fmt::LowerHex> fmt::LowerHex for Saturating<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.0.fmt(f)
     }
 }
 
-#[unstable(feature = "saturating_int_impl", issue = "87920")]
+#[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
 impl<T: fmt::UpperHex> fmt::UpperHex for Saturating<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.0.fmt(f)
     }
 }
-#[allow(unused_macros)]
-macro_rules! sh_impl_signed {
-    ($t:ident, $f:ident) => {
-        // FIXME what is the correct implementation here? see discussion https://github.com/rust-lang/rust/pull/87921#discussion_r695870065
-        //
-        // #[unstable(feature = "saturating_int_impl", issue = "87920")]
-        // impl Shl<$f> for Saturating<$t> {
-        //     type Output = Saturating<$t>;
-        //
-        //     #[inline]
-        //     fn shl(self, other: $f) -> Saturating<$t> {
-        //         if other < 0 {
-        //             Saturating(self.0.shr((-other & self::shift_max::$t as $f) as u32))
-        //         } else {
-        //             Saturating(self.0.shl((other & self::shift_max::$t as $f) as u32))
-        //         }
-        //     }
-        // }
-        // forward_ref_binop! { impl Shl, shl for Saturating<$t>, $f,
-        // #[unstable(feature = "saturating_int_impl", issue = "87920")] }
-        //
-        // #[unstable(feature = "saturating_int_impl", issue = "87920")]
-        // impl ShlAssign<$f> for Saturating<$t> {
-        //     #[inline]
-        //     fn shl_assign(&mut self, other: $f) {
-        //         *self = *self << other;
-        //     }
-        // }
-        // forward_ref_op_assign! { impl ShlAssign, shl_assign for Saturating<$t>, $f }
-
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
-        impl Shr<$f> for Saturating<$t> {
-            type Output = Saturating<$t>;
-
-            #[inline]
-            fn shr(self, other: $f) -> Saturating<$t> {
-                if other < 0 {
-                    Saturating(self.0.shl((-other & self::shift_max::$t as $f) as u32))
-                } else {
-                    Saturating(self.0.shr((other & self::shift_max::$t as $f) as u32))
-                }
-            }
-        }
-        forward_ref_binop! { impl Shr, shr for Saturating<$t>, $f,
-        #[unstable(feature = "saturating_int_impl", issue = "87920")] }
-
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
-        impl ShrAssign<$f> for Saturating<$t> {
-            #[inline]
-            fn shr_assign(&mut self, other: $f) {
-                *self = *self >> other;
-            }
-        }
-        forward_ref_op_assign! { impl ShrAssign, shr_assign for Saturating<$t>, $f }
-    };
-}
-
-macro_rules! sh_impl_unsigned {
-    ($t:ident, $f:ident) => {
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
-        impl Shl<$f> for Saturating<$t> {
-            type Output = Saturating<$t>;
-
-            #[inline]
-            fn shl(self, other: $f) -> Saturating<$t> {
-                Saturating(self.0.wrapping_shl(other as u32))
-            }
-        }
-        forward_ref_binop! { impl Shl, shl for Saturating<$t>, $f,
-        #[unstable(feature = "saturating_int_impl", issue = "87920")] }
-
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
-        impl ShlAssign<$f> for Saturating<$t> {
-            #[inline]
-            fn shl_assign(&mut self, other: $f) {
-                *self = *self << other;
-            }
-        }
-        forward_ref_op_assign! { impl ShlAssign, shl_assign for Saturating<$t>, $f }
-
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
-        impl Shr<$f> for Saturating<$t> {
-            type Output = Saturating<$t>;
-
-            #[inline]
-            fn shr(self, other: $f) -> Saturating<$t> {
-                Saturating(self.0.wrapping_shr(other as u32))
-            }
-        }
-        forward_ref_binop! { impl Shr, shr for Saturating<$t>, $f,
-        #[unstable(feature = "saturating_int_impl", issue = "87920")] }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
-        impl ShrAssign<$f> for Saturating<$t> {
-            #[inline]
-            fn shr_assign(&mut self, other: $f) {
-                *self = *self >> other;
-            }
-        }
-        forward_ref_op_assign! { impl ShrAssign, shr_assign for Saturating<$t>, $f }
-    };
-}
-
-// FIXME (#23545): uncomment the remaining impls
-macro_rules! sh_impl_all {
-    ($($t:ident)*) => ($(
-        //sh_impl_unsigned! { $t, u8 }
-        //sh_impl_unsigned! { $t, u16 }
-        //sh_impl_unsigned! { $t, u32 }
-        //sh_impl_unsigned! { $t, u64 }
-        //sh_impl_unsigned! { $t, u128 }
-        sh_impl_unsigned! { $t, usize }
-
-        //sh_impl_signed! { $t, i8 }
-        //sh_impl_signed! { $t, i16 }
-        //sh_impl_signed! { $t, i32 }
-        //sh_impl_signed! { $t, i64 }
-        //sh_impl_signed! { $t, i128 }
-        //sh_impl_signed! { $t, isize }
-    )*)
-}
-
-sh_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize }
+// FIXME the correct implementation is not clear. Waiting for a real world use case at https://github.com/rust-lang/libs-team/issues/230
+//
+// #[allow(unused_macros)]
+// macro_rules! sh_impl_signed {
+//     ($t:ident, $f:ident) => {
+//         // FIXME what is the correct implementation here? see discussion https://github.com/rust-lang/rust/pull/87921#discussion_r695870065
+//         //
+//         // #[unstable(feature = "saturating_int_impl", issue = "87920")]
+//         // impl Shl<$f> for Saturating<$t> {
+//         //     type Output = Saturating<$t>;
+//         //
+//         //     #[inline]
+//         //     fn shl(self, other: $f) -> Saturating<$t> {
+//         //         if other < 0 {
+//         //             Saturating(self.0.shr((-other & self::shift_max::$t as $f) as u32))
+//         //         } else {
+//         //             Saturating(self.0.shl((other & self::shift_max::$t as $f) as u32))
+//         //         }
+//         //     }
+//         // }
+//         // forward_ref_binop! { impl Shl, shl for Saturating<$t>, $f,
+//         // #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+//         //
+//         // #[unstable(feature = "saturating_int_impl", issue = "87920")]
+//         // impl ShlAssign<$f> for Saturating<$t> {
+//         //     #[inline]
+//         //     fn shl_assign(&mut self, other: $f) {
+//         //         *self = *self << other;
+//         //     }
+//         // }
+//         // forward_ref_op_assign! { impl ShlAssign, shl_assign for Saturating<$t>, $f }
+//
+//         #[unstable(feature = "saturating_int_impl", issue = "87920")]
+//         impl Shr<$f> for Saturating<$t> {
+//             type Output = Saturating<$t>;
+//
+//             #[inline]
+//             fn shr(self, other: $f) -> Saturating<$t> {
+//                 if other < 0 {
+//                     Saturating(self.0.shl((-other & self::shift_max::$t as $f) as u32))
+//                 } else {
+//                     Saturating(self.0.shr((other & self::shift_max::$t as $f) as u32))
+//                 }
+//             }
+//         }
+//         forward_ref_binop! { impl Shr, shr for Saturating<$t>, $f,
+//         #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+//
+//         #[unstable(feature = "saturating_int_impl", issue = "87920")]
+//         impl ShrAssign<$f> for Saturating<$t> {
+//             #[inline]
+//             fn shr_assign(&mut self, other: $f) {
+//                 *self = *self >> other;
+//             }
+//         }
+//         forward_ref_op_assign! { impl ShrAssign, shr_assign for Saturating<$t>, $f }
+//     };
+// }
+//
+// macro_rules! sh_impl_unsigned {
+//     ($t:ident, $f:ident) => {
+//         #[unstable(feature = "saturating_int_impl", issue = "87920")]
+//         impl Shl<$f> for Saturating<$t> {
+//             type Output = Saturating<$t>;
+//
+//             #[inline]
+//             fn shl(self, other: $f) -> Saturating<$t> {
+//                 Saturating(self.0.wrapping_shl(other as u32))
+//             }
+//         }
+//         forward_ref_binop! { impl Shl, shl for Saturating<$t>, $f,
+//         #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+//
+//         #[unstable(feature = "saturating_int_impl", issue = "87920")]
+//         impl ShlAssign<$f> for Saturating<$t> {
+//             #[inline]
+//             fn shl_assign(&mut self, other: $f) {
+//                 *self = *self << other;
+//             }
+//         }
+//         forward_ref_op_assign! { impl ShlAssign, shl_assign for Saturating<$t>, $f }
+//
+//         #[unstable(feature = "saturating_int_impl", issue = "87920")]
+//         impl Shr<$f> for Saturating<$t> {
+//             type Output = Saturating<$t>;
+//
+//             #[inline]
+//             fn shr(self, other: $f) -> Saturating<$t> {
+//                 Saturating(self.0.wrapping_shr(other as u32))
+//             }
+//         }
+//         forward_ref_binop! { impl Shr, shr for Saturating<$t>, $f,
+//         #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+//
+//         #[unstable(feature = "saturating_int_impl", issue = "87920")]
+//         impl ShrAssign<$f> for Saturating<$t> {
+//             #[inline]
+//             fn shr_assign(&mut self, other: $f) {
+//                 *self = *self >> other;
+//             }
+//         }
+//         forward_ref_op_assign! { impl ShrAssign, shr_assign for Saturating<$t>, $f }
+//     };
+// }
+//
+// // FIXME (#23545): uncomment the remaining impls
+// macro_rules! sh_impl_all {
+//     ($($t:ident)*) => ($(
+//         //sh_impl_unsigned! { $t, u8 }
+//         //sh_impl_unsigned! { $t, u16 }
+//         //sh_impl_unsigned! { $t, u32 }
+//         //sh_impl_unsigned! { $t, u64 }
+//         //sh_impl_unsigned! { $t, u128 }
+//         sh_impl_unsigned! { $t, usize }
+//
+//         //sh_impl_signed! { $t, i8 }
+//         //sh_impl_signed! { $t, i16 }
+//         //sh_impl_signed! { $t, i32 }
+//         //sh_impl_signed! { $t, i64 }
+//         //sh_impl_signed! { $t, i128 }
+//         //sh_impl_signed! { $t, isize }
+//     )*)
+// }
+//
+// sh_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize }
 
 // FIXME(30524): impl Op<T> for Saturating<T>, impl OpAssign<T> for Saturating<T>
 macro_rules! saturating_impl {
     ($($t:ty)*) => ($(
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl Add for Saturating<$t> {
             type Output = Saturating<$t>;
 
@@ -216,9 +220,9 @@ macro_rules! saturating_impl {
             }
         }
         forward_ref_binop! { impl Add, add for Saturating<$t>, Saturating<$t>,
-                #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+                #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl AddAssign for Saturating<$t> {
             #[inline]
             fn add_assign(&mut self, other: Saturating<$t>) {
@@ -227,7 +231,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl AddAssign, add_assign for Saturating<$t>, Saturating<$t> }
 
-        #[unstable(feature = "saturating_int_assign_impl", issue = "92354")]
+        #[stable(feature = "saturating_int_assign_impl", since = "CURRENT_RUSTC_VERSION")]
         impl AddAssign<$t> for Saturating<$t> {
             #[inline]
             fn add_assign(&mut self, other: $t) {
@@ -236,7 +240,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl AddAssign, add_assign for Saturating<$t>, $t }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl Sub for Saturating<$t> {
             type Output = Saturating<$t>;
 
@@ -246,9 +250,9 @@ macro_rules! saturating_impl {
             }
         }
         forward_ref_binop! { impl Sub, sub for Saturating<$t>, Saturating<$t>,
-                #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+                #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl SubAssign for Saturating<$t> {
             #[inline]
             fn sub_assign(&mut self, other: Saturating<$t>) {
@@ -257,7 +261,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl SubAssign, sub_assign for Saturating<$t>, Saturating<$t> }
 
-        #[unstable(feature = "saturating_int_assign_impl", issue = "92354")]
+        #[stable(feature = "saturating_int_assign_impl", since = "CURRENT_RUSTC_VERSION")]
         impl SubAssign<$t> for Saturating<$t> {
             #[inline]
             fn sub_assign(&mut self, other: $t) {
@@ -266,7 +270,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl SubAssign, sub_assign for Saturating<$t>, $t }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl Mul for Saturating<$t> {
             type Output = Saturating<$t>;
 
@@ -276,9 +280,9 @@ macro_rules! saturating_impl {
             }
         }
         forward_ref_binop! { impl Mul, mul for Saturating<$t>, Saturating<$t>,
-                #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+                #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl MulAssign for Saturating<$t> {
             #[inline]
             fn mul_assign(&mut self, other: Saturating<$t>) {
@@ -287,7 +291,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl MulAssign, mul_assign for Saturating<$t>, Saturating<$t> }
 
-        #[unstable(feature = "saturating_int_assign_impl", issue = "92354")]
+        #[stable(feature = "saturating_int_assign_impl", since = "CURRENT_RUSTC_VERSION")]
         impl MulAssign<$t> for Saturating<$t> {
             #[inline]
             fn mul_assign(&mut self, other: $t) {
@@ -301,7 +305,6 @@ macro_rules! saturating_impl {
         /// Basic usage:
         ///
         /// ```
-        /// #![feature(saturating_int_impl)]
         /// use std::num::Saturating;
         ///
         #[doc = concat!("assert_eq!(Saturating(2", stringify!($t), "), Saturating(5", stringify!($t), ") / Saturating(2));")]
@@ -310,12 +313,11 @@ macro_rules! saturating_impl {
         /// ```
         ///
         /// ```should_panic
-        /// #![feature(saturating_int_impl)]
         /// use std::num::Saturating;
         ///
         #[doc = concat!("let _ = Saturating(0", stringify!($t), ") / Saturating(0);")]
         /// ```
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl Div for Saturating<$t> {
             type Output = Saturating<$t>;
 
@@ -325,10 +327,10 @@ macro_rules! saturating_impl {
             }
         }
         forward_ref_binop! { impl Div, div for Saturating<$t>, Saturating<$t>,
-                #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+                #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] }
 
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl DivAssign for Saturating<$t> {
             #[inline]
             fn div_assign(&mut self, other: Saturating<$t>) {
@@ -337,7 +339,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl DivAssign, div_assign for Saturating<$t>, Saturating<$t> }
 
-        #[unstable(feature = "saturating_int_assign_impl", issue = "92354")]
+        #[stable(feature = "saturating_int_assign_impl", since = "CURRENT_RUSTC_VERSION")]
         impl DivAssign<$t> for Saturating<$t> {
             #[inline]
             fn div_assign(&mut self, other: $t) {
@@ -346,7 +348,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl DivAssign, div_assign for Saturating<$t>, $t }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl Rem for Saturating<$t> {
             type Output = Saturating<$t>;
 
@@ -356,9 +358,9 @@ macro_rules! saturating_impl {
             }
         }
         forward_ref_binop! { impl Rem, rem for Saturating<$t>, Saturating<$t>,
-                #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+                #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl RemAssign for Saturating<$t> {
             #[inline]
             fn rem_assign(&mut self, other: Saturating<$t>) {
@@ -367,7 +369,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl RemAssign, rem_assign for Saturating<$t>, Saturating<$t> }
 
-        #[unstable(feature = "saturating_int_assign_impl", issue = "92354")]
+        #[stable(feature = "saturating_int_assign_impl", since = "CURRENT_RUSTC_VERSION")]
         impl RemAssign<$t> for Saturating<$t> {
             #[inline]
             fn rem_assign(&mut self, other: $t) {
@@ -376,7 +378,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl RemAssign, rem_assign for Saturating<$t>, $t }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl Not for Saturating<$t> {
             type Output = Saturating<$t>;
 
@@ -386,9 +388,9 @@ macro_rules! saturating_impl {
             }
         }
         forward_ref_unop! { impl Not, not for Saturating<$t>,
-                #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+                #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl BitXor for Saturating<$t> {
             type Output = Saturating<$t>;
 
@@ -398,9 +400,9 @@ macro_rules! saturating_impl {
             }
         }
         forward_ref_binop! { impl BitXor, bitxor for Saturating<$t>, Saturating<$t>,
-                #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+                #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl BitXorAssign for Saturating<$t> {
             #[inline]
             fn bitxor_assign(&mut self, other: Saturating<$t>) {
@@ -409,7 +411,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl BitXorAssign, bitxor_assign for Saturating<$t>, Saturating<$t> }
 
-        #[unstable(feature = "saturating_int_assign_impl", issue = "92354")]
+        #[stable(feature = "saturating_int_assign_impl", since = "CURRENT_RUSTC_VERSION")]
         impl BitXorAssign<$t> for Saturating<$t> {
             #[inline]
             fn bitxor_assign(&mut self, other: $t) {
@@ -418,7 +420,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl BitXorAssign, bitxor_assign for Saturating<$t>, $t }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl BitOr for Saturating<$t> {
             type Output = Saturating<$t>;
 
@@ -428,9 +430,9 @@ macro_rules! saturating_impl {
             }
         }
         forward_ref_binop! { impl BitOr, bitor for Saturating<$t>, Saturating<$t>,
-                #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+                #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl BitOrAssign for Saturating<$t> {
             #[inline]
             fn bitor_assign(&mut self, other: Saturating<$t>) {
@@ -439,7 +441,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl BitOrAssign, bitor_assign for Saturating<$t>, Saturating<$t> }
 
-        #[unstable(feature = "saturating_int_assign_impl", issue = "92354")]
+        #[stable(feature = "saturating_int_assign_impl", since = "CURRENT_RUSTC_VERSION")]
         impl BitOrAssign<$t> for Saturating<$t> {
             #[inline]
             fn bitor_assign(&mut self, other: $t) {
@@ -448,7 +450,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl BitOrAssign, bitor_assign for Saturating<$t>, $t }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl BitAnd for Saturating<$t> {
             type Output = Saturating<$t>;
 
@@ -458,9 +460,9 @@ macro_rules! saturating_impl {
             }
         }
         forward_ref_binop! { impl BitAnd, bitand for Saturating<$t>, Saturating<$t>,
-                #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+                #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl BitAndAssign for Saturating<$t> {
             #[inline]
             fn bitand_assign(&mut self, other: Saturating<$t>) {
@@ -469,7 +471,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl BitAndAssign, bitand_assign for Saturating<$t>, Saturating<$t> }
 
-        #[unstable(feature = "saturating_int_assign_impl", issue = "92354")]
+        #[stable(feature = "saturating_int_assign_impl", since = "CURRENT_RUSTC_VERSION")]
         impl BitAndAssign<$t> for Saturating<$t> {
             #[inline]
             fn bitand_assign(&mut self, other: $t) {
@@ -493,12 +495,11 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("assert_eq!(<Saturating<", stringify!($t), ">>::MIN, Saturating(", stringify!($t), "::MIN));")]
             /// ```
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const MIN: Self = Self(<$t>::MIN);
 
             /// Returns the largest value that can be represented by this integer type.
@@ -508,12 +509,11 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("assert_eq!(<Saturating<", stringify!($t), ">>::MAX, Saturating(", stringify!($t), "::MAX));")]
             /// ```
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const MAX: Self = Self(<$t>::MAX);
 
             /// Returns the size of this integer type in bits.
@@ -523,12 +523,11 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("assert_eq!(<Saturating<", stringify!($t), ">>::BITS, ", stringify!($t), "::BITS);")]
             /// ```
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const BITS: u32 = <$t>::BITS;
 
             /// Returns the number of ones in the binary representation of `self`.
@@ -538,7 +537,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("let n = Saturating(0b01001100", stringify!($t), ");")]
@@ -550,7 +548,8 @@ macro_rules! saturating_int_impl {
             #[doc(alias = "popcnt")]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const fn count_ones(self) -> u32 {
                 self.0.count_ones()
             }
@@ -562,7 +561,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("assert_eq!(Saturating(!0", stringify!($t), ").count_zeros(), 0);")]
@@ -570,7 +568,8 @@ macro_rules! saturating_int_impl {
             #[inline]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const fn count_zeros(self) -> u32 {
                 self.0.count_zeros()
             }
@@ -582,7 +581,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("let n = Saturating(0b0101000", stringify!($t), ");")]
@@ -592,7 +590,8 @@ macro_rules! saturating_int_impl {
             #[inline]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const fn trailing_zeros(self) -> u32 {
                 self.0.trailing_zeros()
             }
@@ -609,7 +608,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             /// let n: Saturating<i64> = Saturating(0x0123456789ABCDEF);
@@ -620,7 +618,8 @@ macro_rules! saturating_int_impl {
             #[inline]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const fn rotate_left(self, n: u32) -> Self {
                 Saturating(self.0.rotate_left(n))
             }
@@ -637,7 +636,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             /// let n: Saturating<i64> = Saturating(0x0123456789ABCDEF);
@@ -648,7 +646,8 @@ macro_rules! saturating_int_impl {
             #[inline]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const fn rotate_right(self, n: u32) -> Self {
                 Saturating(self.0.rotate_right(n))
             }
@@ -660,7 +659,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             /// let n: Saturating<i16> = Saturating(0b0000000_01010101);
@@ -674,7 +672,8 @@ macro_rules! saturating_int_impl {
             #[inline]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const fn swap_bytes(self) -> Self {
                 Saturating(self.0.swap_bytes())
             }
@@ -689,7 +688,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             /// let n = Saturating(0b0000000_01010101i16);
@@ -701,8 +699,8 @@ macro_rules! saturating_int_impl {
             /// assert_eq!(m, Saturating(-22016));
             /// ```
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
-            #[rustc_const_unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
             pub const fn reverse_bits(self) -> Self {
@@ -719,7 +717,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("let n = Saturating(0x1A", stringify!($t), ");")]
@@ -732,7 +729,8 @@ macro_rules! saturating_int_impl {
             /// ```
             #[inline]
             #[must_use]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const fn from_be(x: Self) -> Self {
                 Saturating(<$t>::from_be(x.0))
             }
@@ -747,7 +745,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("let n = Saturating(0x1A", stringify!($t), ");")]
@@ -760,7 +757,8 @@ macro_rules! saturating_int_impl {
             /// ```
             #[inline]
             #[must_use]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const fn from_le(x: Self) -> Self {
                 Saturating(<$t>::from_le(x.0))
             }
@@ -775,7 +773,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("let n = Saturating(0x1A", stringify!($t), ");")]
@@ -787,7 +784,8 @@ macro_rules! saturating_int_impl {
             /// }
             /// ```
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
             pub const fn to_be(self) -> Self {
@@ -804,7 +802,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("let n = Saturating(0x1A", stringify!($t), ");")]
@@ -816,7 +813,8 @@ macro_rules! saturating_int_impl {
             /// }
             /// ```
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
             pub const fn to_le(self) -> Self {
@@ -830,7 +828,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("assert_eq!(Saturating(3", stringify!($t), ").pow(4), Saturating(81));")]
@@ -839,17 +836,17 @@ macro_rules! saturating_int_impl {
             /// Results that are too large are saturated:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             /// assert_eq!(Saturating(3i8).pow(5), Saturating(127));
             /// assert_eq!(Saturating(3i8).pow(6), Saturating(127));
             /// ```
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
-            pub fn pow(self, exp: u32) -> Self {
+            pub const fn pow(self, exp: u32) -> Self {
                 Saturating(self.0.saturating_pow(exp))
             }
         }
@@ -868,7 +865,6 @@ macro_rules! saturating_int_impl_signed {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("let n = Saturating(", stringify!($t), "::MAX >> 2);")]
@@ -876,7 +872,8 @@ macro_rules! saturating_int_impl_signed {
             /// assert_eq!(n.leading_zeros(), 3);
             /// ```
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
             pub const fn leading_zeros(self) -> u32 {
@@ -891,7 +888,6 @@ macro_rules! saturating_int_impl_signed {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("assert_eq!(Saturating(100", stringify!($t), ").abs(), Saturating(100));")]
@@ -901,10 +897,11 @@ macro_rules! saturating_int_impl_signed {
             #[doc = concat!("assert_eq!(Saturating(", stringify!($t), "::MIN).abs(), Saturating(", stringify!($t), "::MAX));")]
             /// ```
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
-            pub fn abs(self) -> Saturating<$t> {
+            pub const fn abs(self) -> Saturating<$t> {
                 Saturating(self.0.saturating_abs())
             }
 
@@ -919,7 +916,6 @@ macro_rules! saturating_int_impl_signed {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("assert_eq!(Saturating(10", stringify!($t), ").signum(), Saturating(1));")]
@@ -927,10 +923,11 @@ macro_rules! saturating_int_impl_signed {
             #[doc = concat!("assert_eq!(Saturating(-10", stringify!($t), ").signum(), Saturating(-1));")]
             /// ```
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
-            pub fn signum(self) -> Saturating<$t> {
+            pub const fn signum(self) -> Saturating<$t> {
                 Saturating(self.0.signum())
             }
 
@@ -942,7 +939,6 @@ macro_rules! saturating_int_impl_signed {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("assert!(Saturating(10", stringify!($t), ").is_positive());")]
@@ -950,7 +946,8 @@ macro_rules! saturating_int_impl_signed {
             /// ```
             #[must_use]
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const fn is_positive(self) -> bool {
                 self.0.is_positive()
             }
@@ -963,7 +960,6 @@ macro_rules! saturating_int_impl_signed {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("assert!(Saturating(-10", stringify!($t), ").is_negative());")]
@@ -971,13 +967,14 @@ macro_rules! saturating_int_impl_signed {
             /// ```
             #[must_use]
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const fn is_negative(self) -> bool {
                 self.0.is_negative()
             }
         }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl Neg for Saturating<$t> {
             type Output = Self;
             #[inline]
@@ -986,7 +983,7 @@ macro_rules! saturating_int_impl_signed {
             }
         }
         forward_ref_unop! { impl Neg, neg for Saturating<$t>,
-                #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+                #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] }
     )*)
 }
 
@@ -1002,7 +999,6 @@ macro_rules! saturating_int_impl_unsigned {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("let n = Saturating(", stringify!($t), "::MAX >> 2);")]
@@ -1010,7 +1006,8 @@ macro_rules! saturating_int_impl_unsigned {
             /// assert_eq!(n.leading_zeros(), 2);
             /// ```
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
             pub const fn leading_zeros(self) -> u32 {
@@ -1024,7 +1021,6 @@ macro_rules! saturating_int_impl_unsigned {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("assert!(Saturating(16", stringify!($t), ").is_power_of_two());")]
@@ -1032,8 +1028,9 @@ macro_rules! saturating_int_impl_unsigned {
             /// ```
             #[must_use]
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
-            pub fn is_power_of_two(self) -> bool {
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            pub const fn is_power_of_two(self) -> bool {
                 self.0.is_power_of_two()
             }
 
diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs
index 08c35b6dac3..911761c6edd 100644
--- a/library/core/src/ops/deref.rs
+++ b/library/core/src/ops/deref.rs
@@ -14,6 +14,11 @@
 /// For similar reasons, **this trait should never fail**. Failure during
 /// dereferencing can be extremely confusing when `Deref` is invoked implicitly.
 ///
+/// Violating these requirements is a logic error. The behavior resulting from a logic error is not
+/// specified, but users of the trait must ensure that such logic errors do *not* result in
+/// undefined behavior. This means that `unsafe` code **must not** rely on the correctness of this
+/// method.
+///
 /// # More on `Deref` coercion
 ///
 /// If `T` implements `Deref<Target = U>`, and `x` is a value of type `T`, then:
@@ -114,6 +119,11 @@ impl<T: ?Sized> Deref for &mut T {
 /// dereferencing can be extremely confusing when `DerefMut` is invoked
 /// implicitly.
 ///
+/// Violating these requirements is a logic error. The behavior resulting from a logic error is not
+/// specified, but users of the trait must ensure that such logic errors do *not* result in
+/// undefined behavior. This means that `unsafe` code **must not** rely on the correctness of this
+/// method.
+///
 /// # More on `Deref` coercion
 ///
 /// If `T` implements `DerefMut<Target = U>`, and `x` is a value of type `T`,
diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs
index 80289ca08c3..2cfa896e5b9 100644
--- a/library/core/src/primitive_docs.rs
+++ b/library/core/src/primitive_docs.rs
@@ -612,7 +612,7 @@ mod prim_pointer {}
 /// statically generated up to size 32.
 ///
 /// Arrays of sizes from 1 to 12 (inclusive) implement [`From<Tuple>`], where `Tuple`
-/// is a homogenous [prim@tuple] of appropriate length.
+/// is a homogeneous [prim@tuple] of appropriate length.
 ///
 /// Arrays coerce to [slices (`[T]`)][slice], so a slice method may be called on
 /// an array. Indeed, this provides most of the API for working with arrays.
@@ -676,7 +676,7 @@ mod prim_pointer {}
 /// move_away(roa);
 /// ```
 ///
-/// Arrays can be created from homogenous tuples of appropriate length:
+/// Arrays can be created from homogeneous tuples of appropriate length:
 ///
 /// ```
 /// let tuple: (u32, u32, u32) = (1, 2, 3);
@@ -1065,7 +1065,7 @@ mod prim_str {}
 /// assert_eq!(y, 5);
 /// ```
 ///
-/// Homogenous tuples can be created from arrays of appropriate length:
+/// Homogeneous tuples can be created from arrays of appropriate length:
 ///
 /// ```
 /// let array: [u32; 3] = [1, 2, 3];
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index 41e67fd8435..d1286a1dea7 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -698,6 +698,7 @@ where
 #[inline(always)]
 #[must_use]
 #[unstable(feature = "ptr_from_ref", issue = "106116")]
+#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
 #[rustc_diagnostic_item = "ptr_from_ref"]
 pub const fn from_ref<T: ?Sized>(r: &T) -> *const T {
     r
@@ -710,7 +711,7 @@ pub const fn from_ref<T: ?Sized>(r: &T) -> *const T {
 #[inline(always)]
 #[must_use]
 #[unstable(feature = "ptr_from_ref", issue = "106116")]
-#[rustc_diagnostic_item = "ptr_from_mut"]
+#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
 pub const fn from_mut<T: ?Sized>(r: &mut T) -> *mut T {
     r
 }
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index 9dbb3f9d322..6d623b82c1c 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -109,7 +109,7 @@ impl<T: ?Sized> *mut T {
     /// with [`cast_mut`] on `*const T` and may have documentation value if used instead of implicit
     /// coercion.
     ///
-    /// [`cast_mut`]: #method.cast_mut
+    /// [`cast_mut`]: pointer::cast_mut
     #[stable(feature = "ptr_const_cast", since = "1.65.0")]
     #[rustc_const_stable(feature = "ptr_const_cast", since = "1.65.0")]
     #[rustc_diagnostic_item = "ptr_cast_const"]
@@ -121,7 +121,7 @@ impl<T: ?Sized> *mut T {
     /// Casts a pointer to its raw bits.
     ///
     /// This is equivalent to `as usize`, but is more specific to enhance readability.
-    /// The inverse method is [`from_bits`](#method.from_bits-1).
+    /// The inverse method is [`from_bits`](pointer#method.from_bits-1).
     ///
     /// In particular, `*p as usize` and `p as usize` will both compile for
     /// pointers to numeric types but do very different things, so using this
@@ -157,7 +157,7 @@ impl<T: ?Sized> *mut T {
     /// Creates a pointer from its raw bits.
     ///
     /// This is equivalent to `as *mut T`, but is more specific to enhance readability.
-    /// The inverse method is [`to_bits`](#method.to_bits-1).
+    /// The inverse method is [`to_bits`](pointer#method.to_bits-1).
     ///
     /// # Examples
     ///
@@ -307,7 +307,7 @@ impl<T: ?Sized> *mut T {
     ///
     /// For the mutable counterpart see [`as_mut`].
     ///
-    /// [`as_uninit_ref`]: #method.as_uninit_ref-1
+    /// [`as_uninit_ref`]: pointer#method.as_uninit_ref-1
     /// [`as_mut`]: #method.as_mut
     ///
     /// # Safety
@@ -373,7 +373,7 @@ impl<T: ?Sized> *mut T {
     ///
     /// For the mutable counterpart see [`as_uninit_mut`].
     ///
-    /// [`as_ref`]: #method.as_ref-1
+    /// [`as_ref`]: pointer#method.as_ref-1
     /// [`as_uninit_mut`]: #method.as_uninit_mut
     ///
     /// # Safety
@@ -628,7 +628,7 @@ impl<T: ?Sized> *mut T {
     /// For the shared counterpart see [`as_ref`].
     ///
     /// [`as_uninit_mut`]: #method.as_uninit_mut
-    /// [`as_ref`]: #method.as_ref-1
+    /// [`as_ref`]: pointer#method.as_ref-1
     ///
     /// # Safety
     ///
@@ -693,7 +693,7 @@ impl<T: ?Sized> *mut T {
     /// For the shared counterpart see [`as_uninit_ref`].
     ///
     /// [`as_mut`]: #method.as_mut
-    /// [`as_uninit_ref`]: #method.as_uninit_ref-1
+    /// [`as_uninit_ref`]: pointer#method.as_uninit_ref-1
     ///
     /// # Safety
     ///
@@ -783,7 +783,7 @@ impl<T: ?Sized> *mut T {
     ///
     /// This function is the inverse of [`offset`].
     ///
-    /// [`offset`]: #method.offset-1
+    /// [`offset`]: pointer#method.offset-1
     ///
     /// # Safety
     ///
@@ -2064,7 +2064,7 @@ impl<T> *mut [T] {
     ///
     /// For the mutable counterpart see [`as_uninit_slice_mut`].
     ///
-    /// [`as_ref`]: #method.as_ref-1
+    /// [`as_ref`]: pointer#method.as_ref-1
     /// [`as_uninit_slice_mut`]: #method.as_uninit_slice_mut
     ///
     /// # Safety
diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs
index 6b3c4343ed3..d5bd54fd59a 100644
--- a/library/core/src/ptr/non_null.rs
+++ b/library/core/src/ptr/non_null.rs
@@ -338,6 +338,7 @@ impl<T: ?Sized> NonNull<T> {
     /// ```
     #[stable(feature = "nonnull", since = "1.25.0")]
     #[rustc_const_stable(feature = "const_nonnull_as_ptr", since = "1.32.0")]
+    #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
     #[must_use]
     #[inline(always)]
     pub const fn as_ptr(self) -> *mut T {
@@ -597,6 +598,7 @@ impl<T> NonNull<[T]> {
     #[must_use]
     #[unstable(feature = "slice_ptr_get", issue = "74265")]
     #[rustc_const_unstable(feature = "slice_ptr_get", issue = "74265")]
+    #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
     pub const fn as_mut_ptr(self) -> *mut T {
         self.as_non_null_ptr().as_ptr()
     }
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index d95662afddd..0d635aced85 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -730,6 +730,7 @@ impl<T> [T] {
     /// [`as_mut_ptr`]: slice::as_mut_ptr
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_const_stable(feature = "const_slice_as_ptr", since = "1.32.0")]
+    #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
     #[inline(always)]
     #[must_use]
     pub const fn as_ptr(&self) -> *const T {
@@ -760,6 +761,7 @@ impl<T> [T] {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
     #[rustc_allow_const_fn_unstable(const_mut_refs)]
+    #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
     #[inline(always)]
     #[must_use]
     pub const fn as_mut_ptr(&mut self) -> *mut T {
diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs
index d5b0ab92c09..eb0c424e2d2 100644
--- a/library/core/src/str/mod.rs
+++ b/library/core/src/str/mod.rs
@@ -386,6 +386,7 @@ impl str {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_const_stable(feature = "rustc_str_as_ptr", since = "1.32.0")]
+    #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
     #[must_use]
     #[inline(always)]
     pub const fn as_ptr(&self) -> *const u8 {
@@ -401,6 +402,7 @@ impl str {
     /// It is your responsibility to make sure that the string slice only gets
     /// modified in a way that it remains valid UTF-8.
     #[stable(feature = "str_as_mut_ptr", since = "1.36.0")]
+    #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
     #[must_use]
     #[inline(always)]
     pub fn as_mut_ptr(&mut self) -> *mut u8 {
diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs
index 22a1c09782c..cf1fbe2d389 100644
--- a/library/core/src/sync/atomic.rs
+++ b/library/core/src/sync/atomic.rs
@@ -1018,6 +1018,7 @@ impl AtomicBool {
     #[inline]
     #[stable(feature = "atomic_as_ptr", since = "1.70.0")]
     #[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")]
+    #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
     pub const fn as_ptr(&self) -> *mut bool {
         self.v.get().cast()
     }
@@ -1953,6 +1954,7 @@ impl<T> AtomicPtr<T> {
     #[inline]
     #[stable(feature = "atomic_as_ptr", since = "1.70.0")]
     #[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")]
+    #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
     pub const fn as_ptr(&self) -> *mut *mut T {
         self.p.get()
     }
@@ -2891,6 +2893,7 @@ macro_rules! atomic_int {
             #[inline]
             #[stable(feature = "atomic_as_ptr", since = "1.70.0")]
             #[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")]
+            #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
             pub const fn as_ptr(&self) -> *mut $int_type {
                 self.v.get()
             }
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 1955ef815ff..22ccfc0dbe9 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -152,6 +152,31 @@
 //! contains further primitive shared memory types, including [`atomic`] and
 //! [`mpsc`], which contains the channel types for message passing.
 //!
+//! # Use before and after `main()`
+//!
+//! Many parts of the standard library are expected to work before and after `main()`;
+//! but this is not guaranteed or ensured by tests. It is recommended that you write your own tests
+//! and run them on each platform you wish to support.
+//! This means that use of `std` before/after main, especially of features that interact with the
+//! OS or global state, is exempted from stability and portability guarantees and instead only
+//! provided on a best-effort basis. Nevertheless bug reports are appreciated.
+//!
+//! On the other hand `core` and `alloc` are most likely to work in such environments with
+//! the caveat that any hookable behavior such as panics, oom handling or allocators will also
+//! depend on the compatibility of the hooks.
+//!
+//! Some features may also behave differently outside main, e.g. stdio could become unbuffered,
+//! some panics might turn into aborts, backtraces might not get symbolicated or similar.
+//!
+//! Non-exhaustive list of known limitations:
+//!
+//! - after-main use of thread-locals, which also affects additional features:
+//!   - [`thread::current()`]
+//!   - [`thread::scope()`]
+//!   - [`sync::mpsc`]
+//! - before-main stdio file descriptors are not guaranteed to be open on unix platforms
+//!
+//!
 //! [I/O]: io
 //! [`MIN`]: i32::MIN
 //! [`MAX`]: i32::MAX
@@ -187,7 +212,6 @@
 //! [rust-discord]: https://discord.gg/rust-lang
 //! [array]: prim@array
 //! [slice]: prim@slice
-
 // To run std tests without x.py without ending up with two copies of std, Miri needs to be
 // able to "empty" this crate. See <https://github.com/rust-lang/miri-test-libstd/issues/4>.
 // rustc itself never sets the feature, so this line has no effect there.
@@ -351,7 +375,6 @@
 #![feature(get_many_mut)]
 #![feature(lazy_cell)]
 #![feature(log_syntax)]
-#![feature(saturating_int_impl)]
 #![feature(stdsimd)]
 #![feature(test)]
 #![feature(trace_macros)]
diff --git a/library/std/src/num.rs b/library/std/src/num.rs
index 46064bd2837..9e021b23fec 100644
--- a/library/std/src/num.rs
+++ b/library/std/src/num.rs
@@ -12,7 +12,7 @@ mod tests;
 #[cfg(test)]
 mod benches;
 
-#[unstable(feature = "saturating_int_impl", issue = "87920")]
+#[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
 pub use core::num::Saturating;
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::num::Wrapping;
diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs
index b6dc1a062ed..0eb4e88cfad 100644
--- a/library/std/src/os/unix/fs.rs
+++ b/library/std/src/os/unix/fs.rs
@@ -155,7 +155,7 @@ pub trait FileExt {
     /// flag fail to respect the offset parameter, always appending to the end
     /// of the file instead.
     ///
-    /// It is possible to inadvertantly set this flag, like in the example below.
+    /// It is possible to inadvertently set this flag, like in the example below.
     /// Therefore, it is important to be vigilant while changing options to mitigate
     /// unexpected behaviour.
     ///
diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs
index 80289ca08c3..2cfa896e5b9 100644
--- a/library/std/src/primitive_docs.rs
+++ b/library/std/src/primitive_docs.rs
@@ -612,7 +612,7 @@ mod prim_pointer {}
 /// statically generated up to size 32.
 ///
 /// Arrays of sizes from 1 to 12 (inclusive) implement [`From<Tuple>`], where `Tuple`
-/// is a homogenous [prim@tuple] of appropriate length.
+/// is a homogeneous [prim@tuple] of appropriate length.
 ///
 /// Arrays coerce to [slices (`[T]`)][slice], so a slice method may be called on
 /// an array. Indeed, this provides most of the API for working with arrays.
@@ -676,7 +676,7 @@ mod prim_pointer {}
 /// move_away(roa);
 /// ```
 ///
-/// Arrays can be created from homogenous tuples of appropriate length:
+/// Arrays can be created from homogeneous tuples of appropriate length:
 ///
 /// ```
 /// let tuple: (u32, u32, u32) = (1, 2, 3);
@@ -1065,7 +1065,7 @@ mod prim_str {}
 /// assert_eq!(y, 5);
 /// ```
 ///
-/// Homogenous tuples can be created from arrays of appropriate length:
+/// Homogeneous tuples can be created from arrays of appropriate length:
 ///
 /// ```
 /// let array: [u32; 3] = [1, 2, 3];
diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index 7380b45b00f..762a1e9b408 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -1501,6 +1501,66 @@ impl From<fs::File> for Stdio {
     }
 }
 
+#[stable(feature = "stdio_from_stdio", since = "CURRENT_RUSTC_VERSION")]
+impl From<io::Stdout> for Stdio {
+    /// Redirect command stdout/stderr to our stdout
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// #![feature(exit_status_error)]
+    /// use std::io;
+    /// use std::process::Command;
+    ///
+    /// # fn test() -> Result<(), Box<dyn std::error::Error>> {
+    /// let output = Command::new("whoami")
+    // "whoami" is a command which exists on both Unix and Windows,
+    // and which succeeds, producing some stdout output but no stderr.
+    ///     .stdout(io::stdout())
+    ///     .output()?;
+    /// output.status.exit_ok()?;
+    /// assert!(output.stdout.is_empty());
+    /// # Ok(())
+    /// # }
+    /// #
+    /// # if cfg!(unix) {
+    /// #     test().unwrap();
+    /// # }
+    /// ```
+    fn from(inherit: io::Stdout) -> Stdio {
+        Stdio::from_inner(inherit.into())
+    }
+}
+
+#[stable(feature = "stdio_from_stdio", since = "CURRENT_RUSTC_VERSION")]
+impl From<io::Stderr> for Stdio {
+    /// Redirect command stdout/stderr to our stderr
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// #![feature(exit_status_error)]
+    /// use std::io;
+    /// use std::process::Command;
+    ///
+    /// # fn test() -> Result<(), Box<dyn std::error::Error>> {
+    /// let output = Command::new("whoami")
+    ///     .stdout(io::stderr())
+    ///     .output()?;
+    /// output.status.exit_ok()?;
+    /// assert!(output.stdout.is_empty());
+    /// # Ok(())
+    /// # }
+    /// #
+    /// # if cfg!(unix) {
+    /// #     test().unwrap();
+    /// # }
+    /// ```
+    fn from(inherit: io::Stderr) -> Stdio {
+        Stdio::from_inner(inherit.into())
+    }
+}
+
 /// Describes the result of a process after it has terminated.
 ///
 /// This `struct` is used to represent the exit status or other termination of a child process.
diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs
index f92bb1a4b1f..d353c7bd5de 100644
--- a/library/std/src/sync/mpsc/mod.rs
+++ b/library/std/src/sync/mpsc/mod.rs
@@ -626,11 +626,6 @@ impl<T> Clone for Sender<T> {
     }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> Drop for Sender<T> {
-    fn drop(&mut self) {}
-}
-
 #[stable(feature = "mpsc_debug", since = "1.8.0")]
 impl<T> fmt::Debug for Sender<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -755,11 +750,6 @@ impl<T> Clone for SyncSender<T> {
     }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> Drop for SyncSender<T> {
-    fn drop(&mut self) {}
-}
-
 #[stable(feature = "mpsc_debug", since = "1.8.0")]
 impl<T> fmt::Debug for SyncSender<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -1096,11 +1086,6 @@ impl<T> IntoIterator for Receiver<T> {
     }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> Drop for Receiver<T> {
-    fn drop(&mut self) {}
-}
-
 #[stable(feature = "mpsc_debug", since = "1.8.0")]
 impl<T> fmt::Debug for Receiver<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs
index ade8c75a657..48f163b6db0 100644
--- a/library/std/src/sys/unix/net.rs
+++ b/library/std/src/sys/unix/net.rs
@@ -102,7 +102,7 @@ impl Socket {
         }
     }
 
-    #[cfg(not(target_os = "vxworks"))]
+    #[cfg(not(any(target_os = "vxworks", target_os = "vita")))]
     pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> {
         unsafe {
             let mut fds = [0, 0];
@@ -133,7 +133,7 @@ impl Socket {
         }
     }
 
-    #[cfg(target_os = "vxworks")]
+    #[cfg(any(target_os = "vxworks", target_os = "vita"))]
     pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> {
         unimplemented!()
     }
diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs
index e7d1533f122..f729da44774 100644
--- a/library/std/src/sys/unix/process/process_common.rs
+++ b/library/std/src/sys/unix/process/process_common.rs
@@ -13,7 +13,7 @@ use crate::sys::fd::FileDesc;
 use crate::sys::fs::File;
 use crate::sys::pipe::{self, AnonPipe};
 use crate::sys_common::process::{CommandEnv, CommandEnvs};
-use crate::sys_common::IntoInner;
+use crate::sys_common::{FromInner, IntoInner};
 
 #[cfg(not(target_os = "fuchsia"))]
 use crate::sys::fs::OpenOptions;
@@ -150,6 +150,7 @@ pub enum Stdio {
     Null,
     MakePipe,
     Fd(FileDesc),
+    StaticFd(BorrowedFd<'static>),
 }
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -463,6 +464,11 @@ impl Stdio {
                 }
             }
 
+            Stdio::StaticFd(fd) => {
+                let fd = FileDesc::from_inner(fd.try_clone_to_owned()?);
+                Ok((ChildStdio::Owned(fd), None))
+            }
+
             Stdio::MakePipe => {
                 let (reader, writer) = pipe::anon_pipe()?;
                 let (ours, theirs) = if readable { (writer, reader) } else { (reader, writer) };
@@ -497,6 +503,28 @@ impl From<File> for Stdio {
     }
 }
 
+impl From<io::Stdout> for Stdio {
+    fn from(_: io::Stdout) -> Stdio {
+        // This ought really to be is Stdio::StaticFd(input_argument.as_fd()).
+        // But AsFd::as_fd takes its argument by reference, and yields
+        // a bounded lifetime, so it's no use here. There is no AsStaticFd.
+        //
+        // Additionally AsFd is only implemented for the *locked* versions.
+        // We don't want to lock them here.  (The implications of not locking
+        // are the same as those for process::Stdio::inherit().)
+        //
+        // Arguably the hypothetical AsStaticFd and AsFd<'static>
+        // should be implemented for io::Stdout, not just for StdoutLocked.
+        Stdio::StaticFd(unsafe { BorrowedFd::borrow_raw(libc::STDOUT_FILENO) })
+    }
+}
+
+impl From<io::Stderr> for Stdio {
+    fn from(_: io::Stderr) -> Stdio {
+        Stdio::StaticFd(unsafe { BorrowedFd::borrow_raw(libc::STDERR_FILENO) })
+    }
+}
+
 impl ChildStdio {
     pub fn fd(&self) -> Option<c_int> {
         match *self {
diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs
index 4f2d9cf3655..7c242d4d334 100644
--- a/library/std/src/sys/unix/thread.rs
+++ b/library/std/src/sys/unix/thread.rs
@@ -182,6 +182,9 @@ impl Thread {
         }
 
         if let Some(f) = pthread_setname_np.get() {
+            #[cfg(target_os = "nto")]
+            let name = truncate_cstr::<{ libc::_NTO_THREAD_NAME_MAX as usize }>(name);
+
             let res = unsafe { f(libc::pthread_self(), name.as_ptr()) };
             debug_assert_eq!(res, 0);
         }
@@ -290,6 +293,7 @@ impl Drop for Thread {
     target_os = "ios",
     target_os = "tvos",
     target_os = "watchos",
+    target_os = "nto",
 ))]
 fn truncate_cstr<const MAX_WITH_NUL: usize>(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] {
     let mut result = [0; MAX_WITH_NUL];
diff --git a/library/std/src/sys/unsupported/process.rs b/library/std/src/sys/unsupported/process.rs
index 77b675aaa4e..a639afcc674 100644
--- a/library/std/src/sys/unsupported/process.rs
+++ b/library/std/src/sys/unsupported/process.rs
@@ -27,6 +27,8 @@ pub struct StdioPipes {
     pub stderr: Option<AnonPipe>,
 }
 
+// FIXME: This should be a unit struct, so we can always construct it
+// The value here should be never used, since we cannot spawn processes.
 pub enum Stdio {
     Inherit,
     Null,
@@ -87,8 +89,26 @@ impl From<AnonPipe> for Stdio {
     }
 }
 
+impl From<io::Stdout> for Stdio {
+    fn from(_: io::Stdout) -> Stdio {
+        // FIXME: This is wrong.
+        // Instead, the Stdio we have here should be a unit struct.
+        panic!("unsupported")
+    }
+}
+
+impl From<io::Stderr> for Stdio {
+    fn from(_: io::Stderr) -> Stdio {
+        // FIXME: This is wrong.
+        // Instead, the Stdio we have here should be a unit struct.
+        panic!("unsupported")
+    }
+}
+
 impl From<File> for Stdio {
     fn from(_file: File) -> Stdio {
+        // FIXME: This is wrong.
+        // Instead, the Stdio we have here should be a unit struct.
         panic!("unsupported")
     }
 }
diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs
index 84a75d21305..cd5bf7f1538 100644
--- a/library/std/src/sys/windows/process.rs
+++ b/library/std/src/sys/windows/process.rs
@@ -172,6 +172,7 @@ pub struct Command {
 
 pub enum Stdio {
     Inherit,
+    InheritSpecific { from_stdio_id: c::DWORD },
     Null,
     MakePipe,
     Pipe(AnonPipe),
@@ -555,17 +556,19 @@ fn program_exists(path: &Path) -> Option<Vec<u16>> {
 
 impl Stdio {
     fn to_handle(&self, stdio_id: c::DWORD, pipe: &mut Option<AnonPipe>) -> io::Result<Handle> {
-        match *self {
-            Stdio::Inherit => match stdio::get_handle(stdio_id) {
-                Ok(io) => unsafe {
-                    let io = Handle::from_raw_handle(io);
-                    let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS);
-                    io.into_raw_handle();
-                    ret
-                },
-                // If no stdio handle is available, then propagate the null value.
-                Err(..) => unsafe { Ok(Handle::from_raw_handle(ptr::null_mut())) },
+        let use_stdio_id = |stdio_id| match stdio::get_handle(stdio_id) {
+            Ok(io) => unsafe {
+                let io = Handle::from_raw_handle(io);
+                let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS);
+                io.into_raw_handle();
+                ret
             },
+            // If no stdio handle is available, then propagate the null value.
+            Err(..) => unsafe { Ok(Handle::from_raw_handle(ptr::null_mut())) },
+        };
+        match *self {
+            Stdio::Inherit => use_stdio_id(stdio_id),
+            Stdio::InheritSpecific { from_stdio_id } => use_stdio_id(from_stdio_id),
 
             Stdio::MakePipe => {
                 let ours_readable = stdio_id != c::STD_INPUT_HANDLE;
@@ -613,6 +616,18 @@ impl From<File> for Stdio {
     }
 }
 
+impl From<io::Stdout> for Stdio {
+    fn from(_: io::Stdout) -> Stdio {
+        Stdio::InheritSpecific { from_stdio_id: c::STD_OUTPUT_HANDLE }
+    }
+}
+
+impl From<io::Stderr> for Stdio {
+    fn from(_: io::Stderr) -> Stdio {
+        Stdio::InheritSpecific { from_stdio_id: c::STD_ERROR_HANDLE }
+    }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Processes
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index e4581c2de78..7b26068c294 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -178,7 +178,7 @@ use crate::sys_common::thread;
 use crate::sys_common::thread_info;
 use crate::sys_common::thread_parking::Parker;
 use crate::sys_common::{AsInner, IntoInner};
-use crate::time::Duration;
+use crate::time::{Duration, Instant};
 
 #[stable(feature = "scoped_threads", since = "1.63.0")]
 mod scoped;
@@ -872,6 +872,86 @@ pub fn sleep(dur: Duration) {
     imp::Thread::sleep(dur)
 }
 
+/// Puts the current thread to sleep until the specified deadline has passed.
+///
+/// The thread may still be asleep after the deadline specified due to
+/// scheduling specifics or platform-dependent functionality. It will never
+/// wake before.
+///
+/// This function is blocking, and should not be used in `async` functions.
+///
+/// # Platform-specific behavior
+///
+/// This function uses [`sleep`] internally, see its platform-specific behaviour.
+///
+///
+/// # Examples
+///
+/// A simple game loop that limits the game to 60 frames per second.
+///
+/// ```no_run
+/// #![feature(thread_sleep_until)]
+/// # use std::time::{Duration, Instant};
+/// # use std::thread;
+/// #
+/// # fn update() {}
+/// # fn render() {}
+/// #
+/// let max_fps = 60.0;
+/// let frame_time = Duration::from_secs_f32(1.0/max_fps);
+/// let mut next_frame = Instant::now();
+/// loop {
+///     thread::sleep_until(next_frame);
+///     next_frame += frame_time;
+///     update();
+///     render();
+/// }
+/// ```
+///
+/// A slow api we must not call too fast and which takes a few
+/// tries before succeeding. By using `sleep_until` the time the
+/// api call takes does not influence when we retry or when we give up
+///
+/// ```no_run
+/// #![feature(thread_sleep_until)]
+/// # use std::time::{Duration, Instant};
+/// # use std::thread;
+/// #
+/// # enum Status {
+/// #     Ready(usize),
+/// #     Waiting,
+/// # }
+/// # fn slow_web_api_call() -> Status { Status::Ready(42) }
+/// #
+/// # const MAX_DURATION: Duration = Duration::from_secs(10);
+/// #
+/// # fn try_api_call() -> Result<usize, ()> {
+/// let deadline = Instant::now() + MAX_DURATION;
+/// let delay = Duration::from_millis(250);
+/// let mut next_attempt = Instant::now();
+/// loop {
+///     if Instant::now() > deadline {
+///         break Err(());
+///     }
+///     if let Status::Ready(data) = slow_web_api_call() {
+///         break Ok(data);
+///     }
+///
+///     next_attempt = deadline.min(next_attempt + delay);
+///     thread::sleep_until(next_attempt);
+/// }
+/// # }
+/// # let _data = try_api_call();
+/// ```
+#[unstable(feature = "thread_sleep_until", issue = "113752")]
+pub fn sleep_until(deadline: Instant) {
+    let now = Instant::now();
+
+    if let Some(delay) = deadline.checked_duration_since(now) {
+        sleep(delay);
+    }
+}
+
 /// Used to ensure that `park` and `park_timeout` do not unwind, as that can
 /// cause undefined behaviour if not handled correctly (see #102398 for context).
 struct PanicGuard;
diff --git a/library/stdarch b/library/stdarch
-Subproject d77878b7299dd7e286799a6e8447048b65d2a86
+Subproject 6100854c4b360f84da5ab25e7c75cb2080667dd
diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md
index 253d504d7bd..548281ca506 100644
--- a/src/bootstrap/README.md
+++ b/src/bootstrap/README.md
@@ -1,8 +1,7 @@
 # rustbuild - Bootstrapping Rust
 
-This is an in-progress README which is targeted at helping to explain how Rust
-is bootstrapped and in general, some of the technical details of the build
-system.
+This README is aimed at helping to explain how Rust is bootstrapped and in general,
+some of the technical details of the build system.
 
 Note that this README only covers internal information, not how to use the tool.
 Please check [bootstrapping dev guide][bootstrapping-dev-guide] for further information.
diff --git a/src/bootstrap/bin/_helper.rs b/src/bootstrap/bin/_helper.rs
new file mode 100644
index 00000000000..09aa471dba4
--- /dev/null
+++ b/src/bootstrap/bin/_helper.rs
@@ -0,0 +1,24 @@
+/// Parses the value of the "RUSTC_VERBOSE" environment variable and returns it as a `usize`.
+/// If it was not defined, returns 0 by default.
+///
+/// Panics if "RUSTC_VERBOSE" is defined with the value that is not an unsigned integer.
+fn parse_rustc_verbose() -> usize {
+    use std::str::FromStr;
+
+    match std::env::var("RUSTC_VERBOSE") {
+        Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"),
+        Err(_) => 0,
+    }
+}
+
+/// Parses the value of the "RUSTC_STAGE" environment variable and returns it as a `String`.
+///
+/// If "RUSTC_STAGE" was not set, the program will be terminated with 101.
+fn parse_rustc_stage() -> String {
+    std::env::var("RUSTC_STAGE").unwrap_or_else(|_| {
+        // Don't panic here; it's reasonable to try and run these shims directly. Give a helpful error instead.
+        eprintln!("rustc shim: fatal: RUSTC_STAGE was not set");
+        eprintln!("rustc shim: note: use `x.py build -vvv` to see all environment variables set by bootstrap");
+        exit(101);
+    })
+}
diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs
index 10718aeb89f..20cd63b966b 100644
--- a/src/bootstrap/bin/rustc.rs
+++ b/src/bootstrap/bin/rustc.rs
@@ -16,27 +16,25 @@
 //! never get replaced.
 
 include!("../dylib_util.rs");
+include!("./_helper.rs");
 
 use std::env;
 use std::path::PathBuf;
 use std::process::{exit, Child, Command};
-use std::str::FromStr;
 use std::time::Instant;
 
 fn main() {
     let args = env::args_os().skip(1).collect::<Vec<_>>();
     let arg = |name| args.windows(2).find(|args| args[0] == name).and_then(|args| args[1].to_str());
 
+    let stage = parse_rustc_stage();
+    let verbose = parse_rustc_verbose();
+
     // Detect whether or not we're a build script depending on whether --target
     // is passed (a bit janky...)
     let target = arg("--target");
     let version = args.iter().find(|w| &**w == "-vV");
 
-    let verbose = match env::var("RUSTC_VERBOSE") {
-        Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"),
-        Err(_) => 0,
-    };
-
     // Use a different compiler for build scripts, since there may not yet be a
     // libstd for the real compiler to use. However, if Cargo is attempting to
     // determine the version of the compiler, the real compiler needs to be
@@ -47,12 +45,7 @@ fn main() {
     } else {
         ("RUSTC_REAL", "RUSTC_LIBDIR")
     };
-    let stage = env::var("RUSTC_STAGE").unwrap_or_else(|_| {
-        // Don't panic here; it's reasonable to try and run these shims directly. Give a helpful error instead.
-        eprintln!("rustc shim: fatal: RUSTC_STAGE was not set");
-        eprintln!("rustc shim: note: use `x.py build -vvv` to see all environment variables set by bootstrap");
-        exit(101);
-    });
+
     let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set");
     let on_fail = env::var_os("RUSTC_ON_FAIL").map(Command::new);
 
diff --git a/src/bootstrap/bin/rustdoc.rs b/src/bootstrap/bin/rustdoc.rs
index 4ecb3349816..6561c1c1933 100644
--- a/src/bootstrap/bin/rustdoc.rs
+++ b/src/bootstrap/bin/rustdoc.rs
@@ -9,14 +9,14 @@ use std::process::{exit, Command};
 
 include!("../dylib_util.rs");
 
+include!("./_helper.rs");
+
 fn main() {
     let args = env::args_os().skip(1).collect::<Vec<_>>();
-    let stage = env::var("RUSTC_STAGE").unwrap_or_else(|_| {
-        // Don't panic here; it's reasonable to try and run these shims directly. Give a helpful error instead.
-        eprintln!("rustc shim: fatal: RUSTC_STAGE was not set");
-        eprintln!("rustc shim: note: use `x.py build -vvv` to see all environment variables set by bootstrap");
-        exit(101);
-    });
+
+    let stage = parse_rustc_stage();
+    let verbose = parse_rustc_verbose();
+
     let rustdoc = env::var_os("RUSTDOC_REAL").expect("RUSTDOC_REAL was not set");
     let libdir = env::var_os("RUSTDOC_LIBDIR").expect("RUSTDOC_LIBDIR was not set");
     let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set");
@@ -25,13 +25,6 @@ fn main() {
     // is passed (a bit janky...)
     let target = args.windows(2).find(|w| &*w[0] == "--target").and_then(|w| w[1].to_str());
 
-    use std::str::FromStr;
-
-    let verbose = match env::var("RUSTC_VERBOSE") {
-        Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"),
-        Err(_) => 0,
-    };
-
     let mut dylib_path = dylib_path();
     dylib_path.insert(0, PathBuf::from(libdir.clone()));
 
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index a24a6a4636d..50f1007e1ff 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -302,8 +302,10 @@ impl StepDescription {
         }
     }
 
-    fn maybe_run(&self, builder: &Builder<'_>, pathsets: Vec<PathSet>) {
-        if pathsets.iter().any(|set| self.is_excluded(builder, set)) {
+    fn maybe_run(&self, builder: &Builder<'_>, mut pathsets: Vec<PathSet>) {
+        pathsets.retain(|set| !self.is_excluded(builder, set));
+
+        if pathsets.is_empty() {
             return;
         }
 
@@ -523,7 +525,7 @@ impl<'a> ShouldRun<'a> {
                 .iter()
                 .map(|p| {
                     // assert only if `p` isn't submodule
-                    if !submodules_paths.iter().find(|sm_p| p.contains(*sm_p)).is_some() {
+                    if submodules_paths.iter().find(|sm_p| p.contains(*sm_p)).is_none() {
                         assert!(
                             self.builder.src.join(p).exists(),
                             "`should_run.paths` should correspond to real on-disk paths - use `alias` if there is no relevant path: {}",
@@ -735,6 +737,7 @@ impl<'a> Builder<'a> {
                 test::Incremental,
                 test::Debuginfo,
                 test::UiFullDeps,
+                test::CodegenCranelift,
                 test::Rustdoc,
                 test::RunCoverageRustdoc,
                 test::Pretty,
diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs
index 43b4a34fe5b..80e66622e8b 100644
--- a/src/bootstrap/builder/tests.rs
+++ b/src/bootstrap/builder/tests.rs
@@ -136,9 +136,9 @@ fn test_exclude_kind() {
     let mut config = configure("test", &["A"], &["A"]);
     // Ensure our test is valid, and `test::Rustc` would be run without the exclude.
     assert!(run_build(&[], config.clone()).contains::<test::CrateLibrustc>());
-    // Ensure tests for rustc are skipped.
+    // Ensure tests for rustc are not skipped.
     config.skip = vec![path.clone()];
-    assert!(!run_build(&[], config.clone()).contains::<test::CrateLibrustc>());
+    assert!(run_build(&[], config.clone()).contains::<test::CrateLibrustc>());
     // Ensure builds for rustc are not skipped.
     assert!(run_build(&[], config).contains::<compile::Rustc>());
 }
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index 9c68e5a78d8..2686a8c1752 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -876,10 +876,8 @@ impl Step for Rustc {
                     cargo.rustflag("-Clto=off");
                 }
             }
-        } else {
-            if builder.config.rust_lto == RustcLto::Off {
-                cargo.rustflag("-Clto=off");
-            }
+        } else if builder.config.rust_lto == RustcLto::Off {
+            cargo.rustflag("-Clto=off");
         }
 
         for krate in &*self.crates {
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index e5fdac3ceda..176ef8e92db 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -322,33 +322,23 @@ pub struct RustfmtMetadata {
     pub version: String,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Default)]
 pub enum RustfmtState {
     SystemToolchain(PathBuf),
     Downloaded(PathBuf),
     Unavailable,
+    #[default]
     LazyEvaluated,
 }
 
-impl Default for RustfmtState {
-    fn default() -> Self {
-        RustfmtState::LazyEvaluated
-    }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq)]
+#[derive(Debug, Default, Clone, Copy, PartialEq)]
 pub enum LlvmLibunwind {
+    #[default]
     No,
     InTree,
     System,
 }
 
-impl Default for LlvmLibunwind {
-    fn default() -> Self {
-        Self::No
-    }
-}
-
 impl FromStr for LlvmLibunwind {
     type Err = String;
 
@@ -362,19 +352,14 @@ impl FromStr for LlvmLibunwind {
     }
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub enum SplitDebuginfo {
     Packed,
     Unpacked,
+    #[default]
     Off,
 }
 
-impl Default for SplitDebuginfo {
-    fn default() -> Self {
-        SplitDebuginfo::Off
-    }
-}
-
 impl std::str::FromStr for SplitDebuginfo {
     type Err = ();
 
@@ -1529,7 +1514,7 @@ impl Config {
             let asserts = llvm_assertions.unwrap_or(false);
             config.llvm_from_ci = match llvm.download_ci_llvm {
                 Some(StringOrBool::String(s)) => {
-                    assert!(s == "if-available", "unknown option `{s}` for download-ci-llvm");
+                    assert_eq!(s, "if-available", "unknown option `{s}` for download-ci-llvm");
                     crate::llvm::is_ci_llvm_available(&config, asserts)
                 }
                 Some(StringOrBool::Bool(b)) => b,
diff --git a/src/bootstrap/config/tests.rs b/src/bootstrap/config/tests.rs
index b8f3be96062..aac76cdcbcf 100644
--- a/src/bootstrap/config/tests.rs
+++ b/src/bootstrap/config/tests.rs
@@ -136,7 +136,7 @@ build-config = {}
         "setting string value without quotes"
     );
     assert_eq!(config.gdb, Some("bar".into()), "setting string value with quotes");
-    assert_eq!(config.deny_warnings, false, "setting boolean value");
+    assert!(!config.deny_warnings, "setting boolean value");
     assert_eq!(
         config.tools,
         Some(["cargo".to_string()].into_iter().collect()),
@@ -181,13 +181,13 @@ fn profile_user_dist() {
 
 #[test]
 fn rust_optimize() {
-    assert_eq!(parse("").rust_optimize.is_release(), true);
-    assert_eq!(parse("rust.optimize = false").rust_optimize.is_release(), false);
-    assert_eq!(parse("rust.optimize = true").rust_optimize.is_release(), true);
-    assert_eq!(parse("rust.optimize = 0").rust_optimize.is_release(), false);
-    assert_eq!(parse("rust.optimize = 1").rust_optimize.is_release(), true);
+    assert!(parse("").rust_optimize.is_release());
+    assert!(!parse("rust.optimize = false").rust_optimize.is_release());
+    assert!(parse("rust.optimize = true").rust_optimize.is_release());
+    assert!(!parse("rust.optimize = 0").rust_optimize.is_release());
+    assert!(parse("rust.optimize = 1").rust_optimize.is_release());
+    assert!(parse("rust.optimize = \"s\"").rust_optimize.is_release());
     assert_eq!(parse("rust.optimize = 1").rust_optimize.get_opt_level(), Some("1".to_string()));
-    assert_eq!(parse("rust.optimize = \"s\"").rust_optimize.is_release(), true);
     assert_eq!(parse("rust.optimize = \"s\"").rust_optimize.get_opt_level(), Some("s".to_string()));
 }
 
diff --git a/src/bootstrap/download.rs b/src/bootstrap/download.rs
index 9489297d67d..8e9614ec89a 100644
--- a/src/bootstrap/download.rs
+++ b/src/bootstrap/download.rs
@@ -441,7 +441,7 @@ impl Config {
     }
 
     pub(crate) fn download_beta_toolchain(&self) {
-        self.verbose(&format!("downloading stage0 beta artifacts"));
+        self.verbose("downloading stage0 beta artifacts");
 
         let date = &self.stage0_metadata.compiler.date;
         let version = &self.stage0_metadata.compiler.version;
diff --git a/src/bootstrap/format.rs b/src/bootstrap/format.rs
index d658be0b8eb..11f2762f766 100644
--- a/src/bootstrap/format.rs
+++ b/src/bootstrap/format.rs
@@ -127,8 +127,6 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) {
         Err(_) => false,
     };
 
-    let mut paths = paths.to_vec();
-
     if git_available {
         let in_working_tree = match build
             .config
@@ -201,8 +199,6 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) {
                             "WARN: Something went wrong when running git commands:\n{err}\n\
                             Falling back to formatting all files."
                         );
-                        // Something went wrong when getting the version. Just format all the files.
-                        paths.push(".".into());
                     }
                 }
             }
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 4396bbc51a3..1e001fae8ab 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -116,7 +116,7 @@ pub const VERSION: usize = 2;
 
 /// Extra --check-cfg to add when building
 /// (Mode restriction, config name, config values (if any))
-const EXTRA_CHECK_CFGS: &[(Option<Mode>, &'static str, Option<&[&'static str]>)] = &[
+const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[
     (None, "bootstrap", None),
     (Some(Mode::Rustc), "parallel_compiler", None),
     (Some(Mode::ToolRustc), "parallel_compiler", None),
@@ -1019,7 +1019,7 @@ impl Build {
 
     fn info(&self, msg: &str) {
         match self.config.dry_run {
-            DryRun::SelfCheck => return,
+            DryRun::SelfCheck => (),
             DryRun::Disabled | DryRun::UserSelected => {
                 println!("{msg}");
             }
@@ -1757,10 +1757,11 @@ to download LLVM rather than building it.
         //
         // In these cases we automatically enable Ninja if we find it in the
         // environment.
-        if !self.config.ninja_in_file && self.config.build.contains("msvc") {
-            if cmd_finder.maybe_have("ninja").is_some() {
-                return true;
-            }
+        if !self.config.ninja_in_file
+            && self.config.build.contains("msvc")
+            && cmd_finder.maybe_have("ninja").is_some()
+        {
+            return true;
         }
 
         self.config.ninja_in_file
diff --git a/src/bootstrap/llvm.rs b/src/bootstrap/llvm.rs
index 07502e0e9dd..07288a1863a 100644
--- a/src/bootstrap/llvm.rs
+++ b/src/bootstrap/llvm.rs
@@ -155,7 +155,7 @@ pub(crate) fn detect_llvm_sha(config: &Config, is_git: bool) -> String {
         "".to_owned()
     };
 
-    if &llvm_sha == "" {
+    if llvm_sha.is_empty() {
         eprintln!("error: could not find commit hash for downloading LLVM");
         eprintln!("help: maybe your repository history is too shallow?");
         eprintln!("help: consider disabling `download-ci-llvm`");
@@ -208,10 +208,10 @@ pub(crate) fn is_ci_llvm_available(config: &Config, asserts: bool) -> bool {
         ("x86_64-unknown-netbsd", false),
     ];
 
-    if !supported_platforms.contains(&(&*config.build.triple, asserts)) {
-        if asserts == true || !supported_platforms.contains(&(&*config.build.triple, true)) {
-            return false;
-        }
+    if !supported_platforms.contains(&(&*config.build.triple, asserts))
+        && (asserts || !supported_platforms.contains(&(&*config.build.triple, true)))
+    {
+        return false;
     }
 
     if is_ci_llvm_modified(config) {
@@ -497,11 +497,11 @@ impl Step for Llvm {
             let mut cmd = Command::new(&res.llvm_config);
             let version = output(cmd.arg("--version"));
             let major = version.split('.').next().unwrap();
-            let lib_name = match &llvm_version_suffix {
+
+            match &llvm_version_suffix {
                 Some(version_suffix) => format!("libLLVM-{major}{version_suffix}.{extension}"),
                 None => format!("libLLVM-{major}.{extension}"),
-            };
-            lib_name
+            }
         };
 
         // When building LLVM with LLVM_LINK_LLVM_DYLIB for macOS, an unversioned
@@ -598,9 +598,9 @@ fn configure_cmake(
         } else if target.contains("linux") {
             cfg.define("CMAKE_SYSTEM_NAME", "Linux");
         } else {
-            builder.info(
+            builder.info(&format!(
                 "could not determine CMAKE_SYSTEM_NAME from the target `{target}`, build may fail",
-            );
+            ));
         }
 
         // When cross-compiling we should also set CMAKE_SYSTEM_VERSION, but in
@@ -756,13 +756,15 @@ fn configure_cmake(
 
     // For distribution we want the LLVM tools to be *statically* linked to libstdc++.
     // We also do this if the user explicitly requested static libstdc++.
-    if builder.config.llvm_static_stdcpp {
-        if !target.contains("msvc") && !target.contains("netbsd") && !target.contains("solaris") {
-            if target.contains("apple") || target.contains("windows") {
-                ldflags.push_all("-static-libstdc++");
-            } else {
-                ldflags.push_all("-Wl,-Bsymbolic -static-libstdc++");
-            }
+    if builder.config.llvm_static_stdcpp
+        && !target.contains("msvc")
+        && !target.contains("netbsd")
+        && !target.contains("solaris")
+    {
+        if target.contains("apple") || target.contains("windows") {
+            ldflags.push_all("-static-libstdc++");
+        } else {
+            ldflags.push_all("-Wl,-Bsymbolic -static-libstdc++");
         }
     }
 
diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs
index 144e2acd09e..0febdf250d3 100644
--- a/src/bootstrap/sanity.rs
+++ b/src/bootstrap/sanity.rs
@@ -95,20 +95,19 @@ pub fn check(build: &mut Build) {
                     .unwrap_or(true)
             })
             .any(|build_llvm_ourselves| build_llvm_ourselves);
+
     let need_cmake = building_llvm || build.config.any_sanitizers_enabled();
-    if need_cmake {
-        if cmd_finder.maybe_have("cmake").is_none() {
-            eprintln!(
-                "
+    if need_cmake && cmd_finder.maybe_have("cmake").is_none() {
+        eprintln!(
+            "
 Couldn't find required command: cmake
 
 You should install cmake, or set `download-ci-llvm = true` in the
 `[llvm]` section of `config.toml` to download LLVM rather
 than building it.
 "
-            );
-            crate::exit!(1);
-        }
+        );
+        crate::exit!(1);
     }
 
     build.config.python = build
@@ -202,10 +201,10 @@ than building it.
             .entry(*target)
             .or_insert_with(|| Target::from_triple(&target.triple));
 
-        if target.contains("-none-") || target.contains("nvptx") {
-            if build.no_std(*target) == Some(false) {
-                panic!("All the *-none-* and nvptx* targets are no-std targets")
-            }
+        if (target.contains("-none-") || target.contains("nvptx"))
+            && build.no_std(*target) == Some(false)
+        {
+            panic!("All the *-none-* and nvptx* targets are no-std targets")
         }
 
         // Some environments don't want or need these tools, such as when testing Miri.
diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs
index 30730f50491..ef0234957b5 100644
--- a/src/bootstrap/setup.rs
+++ b/src/bootstrap/setup.rs
@@ -33,6 +33,7 @@ static SETTINGS_HASHES: &[&str] = &[
     "af1b5efe196aed007577899db9dae15d6dbc923d6fa42fa0934e68617ba9bbe0",
     "3468fea433c25fff60be6b71e8a215a732a7b1268b6a83bf10d024344e140541",
     "47d227f424bf889b0d899b9cc992d5695e1b78c406e183cd78eafefbe5488923",
+    "b526bd58d0262dd4dda2bff5bc5515b705fb668a46235ace3e057f807963a11a",
 ];
 static RUST_ANALYZER_SETTINGS: &str = include_str!("../etc/rust_analyzer_settings.json");
 
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index d78e0deda69..ba030f0f525 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -846,7 +846,7 @@ impl Step for RustdocTheme {
         let rustdoc = builder.bootstrap_out.join("rustdoc");
         let mut cmd = builder.tool_cmd(Tool::RustdocTheme);
         cmd.arg(rustdoc.to_str().unwrap())
-            .arg(builder.src.join("src/librustdoc/html/static/css/themes").to_str().unwrap())
+            .arg(builder.src.join("src/librustdoc/html/static/css/rustdoc.css").to_str().unwrap())
             .env("RUSTC_STAGE", self.compiler.stage.to_string())
             .env("RUSTC_SYSROOT", builder.sysroot(self.compiler))
             .env("RUSTDOC_LIBDIR", builder.sysroot_libdir(self.compiler, self.compiler.host))
@@ -1139,16 +1139,14 @@ help: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to
             .map(|filename| builder.src.join("src/etc/completions").join(filename));
         if builder.config.cmd.bless() {
             builder.ensure(crate::run::GenerateCompletions);
-        } else {
-            if crate::flags::get_completion(shells::Bash, &bash).is_some()
-                || crate::flags::get_completion(shells::Fish, &fish).is_some()
-                || crate::flags::get_completion(shells::PowerShell, &powershell).is_some()
-            {
-                eprintln!(
-                    "x.py completions were changed; run `x.py run generate-completions` to update them"
-                );
-                crate::exit!(1);
-            }
+        } else if crate::flags::get_completion(shells::Bash, &bash).is_some()
+            || crate::flags::get_completion(shells::Fish, &fish).is_some()
+            || crate::flags::get_completion(shells::PowerShell, &powershell).is_some()
+        {
+            eprintln!(
+                "x.py completions were changed; run `x.py run generate-completions` to update them"
+            );
+            crate::exit!(1);
         }
     }
 
@@ -1378,7 +1376,7 @@ impl Step for MirOpt {
         let run = |target| {
             builder.ensure(Compiletest {
                 compiler: self.compiler,
-                target: target,
+                target,
                 mode: "mir-opt",
                 suite: "mir-opt",
                 path: "tests/mir-opt",
@@ -2967,3 +2965,129 @@ impl Step for TestHelpers {
             .compile("rust_test_helpers");
     }
 }
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct CodegenCranelift {
+    compiler: Compiler,
+    target: TargetSelection,
+}
+
+impl Step for CodegenCranelift {
+    type Output = ();
+    const DEFAULT: bool = true;
+    const ONLY_HOSTS: bool = true;
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.paths(&["compiler/rustc_codegen_cranelift"])
+    }
+
+    fn make_run(run: RunConfig<'_>) {
+        let builder = run.builder;
+        let host = run.build_triple();
+        let compiler = run.builder.compiler_for(run.builder.top_stage, host, host);
+
+        if builder.doc_tests == DocTests::Only {
+            return;
+        }
+
+        let triple = run.target.triple;
+        let target_supported = if triple.contains("linux") {
+            triple.contains("x86_64") || triple.contains("aarch64") || triple.contains("s390x")
+        } else if triple.contains("darwin") || triple.contains("windows") {
+            triple.contains("x86_64")
+        } else {
+            false
+        };
+        if !target_supported {
+            builder.info("target not supported by rustc_codegen_cranelift. skipping");
+            return;
+        }
+
+        if builder.remote_tested(run.target) {
+            builder.info("remote testing is not supported by rustc_codegen_cranelift. skipping");
+            return;
+        }
+
+        if !builder.config.rust_codegen_backends.contains(&INTERNER.intern_str("cranelift")) {
+            builder.info("cranelift not in rust.codegen-backends. skipping");
+            return;
+        }
+
+        builder.ensure(CodegenCranelift { compiler, target: run.target });
+    }
+
+    fn run(self, builder: &Builder<'_>) {
+        let compiler = self.compiler;
+        let target = self.target;
+
+        builder.ensure(compile::Std::new(compiler, target));
+
+        // If we're not doing a full bootstrap but we're testing a stage2
+        // version of libstd, then what we're actually testing is the libstd
+        // produced in stage1. Reflect that here by updating the compiler that
+        // we're working with automatically.
+        let compiler = builder.compiler_for(compiler.stage, compiler.host, target);
+
+        let build_cargo = || {
+            let mut cargo = builder.cargo(
+                compiler,
+                Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works
+                SourceType::InTree,
+                target,
+                "run",
+            );
+            cargo.current_dir(&builder.src.join("compiler/rustc_codegen_cranelift"));
+            cargo
+                .arg("--manifest-path")
+                .arg(builder.src.join("compiler/rustc_codegen_cranelift/build_system/Cargo.toml"));
+            compile::rustc_cargo_env(builder, &mut cargo, target, compiler.stage);
+
+            // Avoid incremental cache issues when changing rustc
+            cargo.env("CARGO_BUILD_INCREMENTAL", "false");
+
+            cargo
+        };
+
+        builder.info(&format!(
+            "{} cranelift stage{} ({} -> {})",
+            Kind::Test.description(),
+            compiler.stage,
+            &compiler.host,
+            target
+        ));
+        let _time = util::timeit(&builder);
+
+        // FIXME handle vendoring for source tarballs before removing the --skip-test below
+        let download_dir = builder.out.join("cg_clif_download");
+
+        /*
+        let mut prepare_cargo = build_cargo();
+        prepare_cargo.arg("--").arg("prepare").arg("--download-dir").arg(&download_dir);
+        #[allow(deprecated)]
+        builder.config.try_run(&mut prepare_cargo.into()).unwrap();
+        */
+
+        let mut cargo = build_cargo();
+        cargo
+            .arg("--")
+            .arg("test")
+            .arg("--download-dir")
+            .arg(&download_dir)
+            .arg("--out-dir")
+            .arg(builder.stage_out(compiler, Mode::ToolRustc).join("cg_clif"))
+            .arg("--no-unstable-features")
+            .arg("--use-backend")
+            .arg("cranelift")
+            // Avoid having to vendor the standard library dependencies
+            .arg("--sysroot")
+            .arg("llvm")
+            // These tests depend on crates that are not yet vendored
+            // FIXME remove once vendoring is handled
+            .arg("--skip-test")
+            .arg("testsuite.extended_sysroot");
+        cargo.args(builder.config.test_args());
+
+        #[allow(deprecated)]
+        builder.config.try_run(&mut cargo.into()).unwrap();
+    }
+}
diff --git a/src/ci/docker/host-x86_64/dist-various-2/build-wasi-threads-toolchain.sh b/src/ci/docker/host-x86_64/dist-various-2/build-wasi-threads-toolchain.sh
index 7dae90f4403..689fe52863e 100755
--- a/src/ci/docker/host-x86_64/dist-various-2/build-wasi-threads-toolchain.sh
+++ b/src/ci/docker/host-x86_64/dist-various-2/build-wasi-threads-toolchain.sh
@@ -10,7 +10,7 @@ bin="$PWD/clang+llvm-16.0.4-x86_64-linux-gnu-ubuntu-22.04/bin"
 git clone https://github.com/WebAssembly/wasi-libc
 
 cd wasi-libc
-git reset --hard 7018e24d8fe248596819d2e884761676f3542a04
+git reset --hard ec4566beae84e54952637f0bf61bee4b4cacc087
 make -j$(nproc) \
     CC="$bin/clang" \
     NM="$bin/llvm-nm" \
diff --git a/src/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh b/src/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh
index 45174e708dc..4b0d360686f 100755
--- a/src/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh
+++ b/src/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh
@@ -10,7 +10,7 @@ bin="$PWD/clang+llvm-16.0.4-x86_64-linux-gnu-ubuntu-22.04/bin"
 git clone https://github.com/WebAssembly/wasi-libc
 
 cd wasi-libc
-git reset --hard 7018e24d8fe248596819d2e884761676f3542a04
+git reset --hard ec4566beae84e54952637f0bf61bee4b4cacc087
 make -j$(nproc) \
     CC="$bin/clang" \
     NM="$bin/llvm-nm" \
diff --git a/src/ci/docker/host-x86_64/wasm32/Dockerfile b/src/ci/docker/host-x86_64/wasm32/Dockerfile
index 02b4664eb55..0d0f1edd003 100644
--- a/src/ci/docker/host-x86_64/wasm32/Dockerfile
+++ b/src/ci/docker/host-x86_64/wasm32/Dockerfile
@@ -58,5 +58,6 @@ ENV NO_CHANGE_USER=1
 RUN chown 10719 -R /emsdk-portable/
 
 # Exclude library/alloc due to OOM in benches.
+# FIXME: Fix std tests
 ENV SCRIPT python3 ../x.py test --stage 2 --host='' --target $TARGETS \
-    --skip library/alloc
+    --skip library/alloc --skip library/std
diff --git a/src/ci/run.sh b/src/ci/run.sh
index b8cb758bf40..98f2cdac5dc 100755
--- a/src/ci/run.sh
+++ b/src/ci/run.sh
@@ -123,6 +123,9 @@ else
 
   RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.verify-llvm-ir"
 
+  # Test the Cranelift backend in on CI, but don't ship it.
+  RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.codegen-backends=llvm,cranelift"
+
   # We enable this for non-dist builders, since those aren't trying to produce
   # fresh binaries. We currently don't entirely support distributing a fresh
   # copy of the compiler (including llvm tools, etc.) if we haven't actually
diff --git a/src/doc/edition-guide b/src/doc/edition-guide
-Subproject 2751bdcef125468ea2ee006c11992cd1405aebe
+Subproject 34fca48ed284525b2f124bf93c51af36d668549
diff --git a/src/doc/nomicon b/src/doc/nomicon
-Subproject 388750b081c0893c275044d37203f97709e058b
+Subproject e3f3af69dce71cd37a785bccb7e58449197d940
diff --git a/src/doc/reference b/src/doc/reference
-Subproject d43038932adeb16ada80e206d4c073d85129810
+Subproject ee7c676fd6e287459cb407337652412c990686c
diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example
-Subproject 07e0df2f006e59d171c6bf3cafa9d61dbeb520d
+Subproject c954202c1e1720cba5628f99543cc01188c7d6f
diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide
-Subproject b123ab4754127d822ffb38349ce0fbf561f1b2f
+Subproject 08bb147d51e815b96e8db7ba4cf870f201c11ff
diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md
index 2c7c05c0c4b..4d32897cc14 100644
--- a/src/doc/rustc/src/command-line-arguments.md
+++ b/src/doc/rustc/src/command-line-arguments.md
@@ -260,6 +260,10 @@ The valid types of print values are:
   This returns rustc's minimum supported deployment target if no `*_DEPLOYMENT_TARGET` variable
   is present in the environment, or otherwise returns the variable's parsed value.
 
+A filepath may optionally be specified for each requested information kind, in
+the format `--print KIND=PATH`, just like for `--emit`. When a path is
+specified, information will be written there instead of to stdout.
+
 [conditional compilation]: ../reference/conditional-compilation.html
 [deployment target]: https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/cross_development/Configuring/configuring.html
 
diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md
index 2535cd4f12c..5b766318331 100644
--- a/src/doc/rustc/src/instrument-coverage.md
+++ b/src/doc/rustc/src/instrument-coverage.md
@@ -173,9 +173,9 @@ Some of the more notable options in this example include:
 [`llvm-cov report`]: https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-report
 [`llvm-cov show`]: https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-show
 
-> **Note**: Coverage can also be disabled on an individual function by annotating the function with the [`no_coverage` attribute] (which requires the feature flag `#![feature(no_coverage)]`).
+> **Note**: Coverage can also be disabled on an individual function by annotating the function with the [`coverage(off)` attribute] (which requires the feature flag `#![feature(coverage)]`).
 
-[`no_coverage` attribute]: ../unstable-book/language-features/no-coverage.html
+[`coverage` attribute]: ../unstable-book/language-features/coverage.html
 
 ## Interpreting reports
 
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index 2cb0a8f3c17..70b35526ee5 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -181,6 +181,7 @@ target | std | notes
 `wasm32-unknown-emscripten` | ✓ | WebAssembly via Emscripten
 `wasm32-unknown-unknown` | ✓ | WebAssembly
 `wasm32-wasi` | ✓ | WebAssembly with WASI
+[`wasm32-wasi-preview1-threads`](platform-support/wasm32-wasi-preview1-threads.md) | ✓ |  | WebAssembly with WASI Preview 1 and threads
 `x86_64-apple-ios` | ✓ | 64-bit x86 iOS
 [`x86_64-fortanix-unknown-sgx`](platform-support/x86_64-fortanix-unknown-sgx.md) | ✓ | [Fortanix ABI] for 64-bit Intel SGX
 `x86_64-fuchsia` | ✓ | Alias for `x86_64-unknown-fuchsia`
@@ -266,6 +267,7 @@ target | std | host | notes
 [`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * |  | 32-bit x86 QNX Neutrino 7.0 RTOS |
 `i686-apple-darwin` | ✓ | ✓ | 32-bit macOS (10.7+, Lion+)
 `i686-pc-windows-msvc` | * |  | 32-bit Windows XP support
+[`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ |
 `i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku
 [`i686-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/i386 with SSE2
 [`i686-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 32-bit OpenBSD
@@ -323,7 +325,6 @@ target | std | host | notes
 `thumbv7a-pc-windows-msvc` | ? |  |
 `thumbv7a-uwp-windows-msvc` | ✓ |  |
 `thumbv7neon-unknown-linux-musleabihf` | ? |  | Thumb2-mode ARMv7-A Linux with NEON, MUSL
-[`wasm32-wasi-preview1-threads`](platform-support/wasm32-wasi-preview1-threads.md) | ✓ |  | WebAssembly with WASI Preview 1 and threads
 [`wasm64-unknown-unknown`](platform-support/wasm64-unknown-unknown.md) | ? |  | WebAssembly
 `x86_64-apple-ios-macabi` | ✓ |  | Apple Catalyst on x86_64
 [`x86_64-apple-tvos`](platform-support/apple-tvos.md) | ? | | x86 64-bit tvOS
diff --git a/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md b/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md
index fb0cea05d44..a6246fa3bd8 100644
--- a/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md
+++ b/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md
@@ -6,6 +6,7 @@ Windows targets similar to `*-pc-windows-gnu` but using UCRT as the runtime and
 
 Target triples available so far:
 - `aarch64-pc-windows-gnullvm`
+- `i686-pc-windows-gnullvm`
 - `x86_64-pc-windows-gnullvm`
 
 ## Target maintainers
@@ -42,7 +43,7 @@ Once these targets bootstrap themselves on native hardware they should pass Rust
 
 ## Cross-compilation toolchains and C code
 
-Compatible C code can be built with Clang's `aarch64-pc-windows-gnu` and `x86_64-pc-windows-gnu` targets as long as LLVM based C toolchains are used.
+Compatible C code can be built with Clang's `aarch64-pc-windows-gnu`, `i686-pc-windows-gnullvm` and `x86_64-pc-windows-gnu` targets as long as LLVM based C toolchains are used.
 Those include:
 - [llvm-mingw](https://github.com/mstorsjo/llvm-mingw)
 - [MSYS2 with CLANG* environment](https://www.msys2.org/docs/environments)
diff --git a/src/doc/rustc/src/platform-support/wasm32-wasi-preview1-threads.md b/src/doc/rustc/src/platform-support/wasm32-wasi-preview1-threads.md
index b3eb34de638..23b99924899 100644
--- a/src/doc/rustc/src/platform-support/wasm32-wasi-preview1-threads.md
+++ b/src/doc/rustc/src/platform-support/wasm32-wasi-preview1-threads.md
@@ -1,6 +1,6 @@
 # `wasm32-wasi-preview1-threads`
 
-**Tier: 3**
+**Tier: 2**
 
 The `wasm32-wasi-preview1-threads` target is a new and still (as of July 2023) an
 experimental target. This target is an extension to `wasm32-wasi-preview1` target,
@@ -70,12 +70,6 @@ compile `wasm32-wasi-preview1-threads` binaries straight out of the box. You can
 reliably interoperate with C code in this mode (yet).
 
 
-This target is not a stable target. This means that there are not many engines
-which implement the `wasi-threads` feature and if they do they're likely behind a
-flag, for example:
-
-* Wasmtime - `--wasm-features=threads --wasi-modules=experimental-wasi-threads`
-
 Also note that at this time the `wasm32-wasi-preview1-threads` target assumes the
 presence of other merged wasm proposals such as (with their LLVM feature flags):
 
@@ -94,6 +88,17 @@ The target intends to match the corresponding Clang target for its `"C"` ABI.
 > found it's recommended to open an issue either with rust-lang/rust or ideally
 > with LLVM itself.
 
+## Platform requirements
+
+The runtime should support the same set of APIs as any other supported wasi target for interacting with the host environment through the WASI standard. The runtime also should have implemetation of [wasi-threads proposal](https://github.com/WebAssembly/wasi-threads).
+
+This target is not a stable target. This means that there are a few engines
+which implement the `wasi-threads` feature and if they do they're likely behind a
+flag, for example:
+
+* Wasmtime - `--wasm-features=threads --wasi-modules=experimental-wasi-threads`
+* [WAMR](https://github.com/bytecodealliance/wasm-micro-runtime) - needs to be built with WAMR_BUILD_LIB_WASI_THREADS=1
+
 ## Building the target
 
 Users need to install or built wasi-sdk since release 20.0
@@ -110,12 +115,16 @@ After that users can build this by adding it to the `target` list in
 
 ## Building Rust programs
 
-Since it is Tier 3, rust doesn't ship pre-compiled artifacts for this target.
+From Rust Nightly 1.71.1 (2023-08-03) on the artifacts are shipped pre-compiled:
+
+```text
+rustup target add wasm32-wasi-preview1-threads --toolchain nightly
+```
+
+Rust programs can be built for that target:
 
-Specify `wasi-root` as explained in the previous section and then use the `build-std`
-nightly cargo feature to build the standard library:
-```shell
-cargo +nightly build --target=wasm32-wasi-preview1-threads -Zbuild-std
+```text
+rustc --target wasm32-wasi-preview1-threads your-code.rs
 ```
 
 ## Cross-compilation
diff --git a/src/doc/rustdoc/src/how-to-read-rustdoc.md b/src/doc/rustdoc/src/how-to-read-rustdoc.md
index 9deb7009cfe..cd6e29ffd66 100644
--- a/src/doc/rustdoc/src/how-to-read-rustdoc.md
+++ b/src/doc/rustdoc/src/how-to-read-rustdoc.md
@@ -38,6 +38,22 @@ followed by a list of fields or variants for Rust types.
 Finally, the page lists associated functions and trait implementations,
 including automatic and blanket implementations that `rustdoc` knows about.
 
+### Sections
+
+<!-- FIXME: Implementations -->
+<!-- FIXME: Trait Implementations -->
+<!-- FIXME: Implementors -->
+<!-- FIXME: Auto Trait Implementations -->
+
+#### Aliased Type
+
+A type alias is expanded at compile time to its
+[aliased type](https://doc.rust-lang.org/reference/items/type-aliases.html).
+That may involve substituting some or all of the type parameters in the target
+type with types provided by the type alias definition. The Aliased Type section
+shows the result of this expansion, including the types of public fields or
+variants, which may depend on those substitutions.
+
 ### Navigation
 
 Subheadings, variants, fields, and many other things in this documentation
diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md
index f69156b7c05..0d3f6338af4 100644
--- a/src/doc/rustdoc/src/unstable-features.md
+++ b/src/doc/rustdoc/src/unstable-features.md
@@ -625,3 +625,47 @@ and check the values of `feature`: `foo` and `bar`.
 
 This flag enables the generation of links in the source code pages which allow the reader
 to jump to a type definition.
+
+### Custom CSS classes for code blocks
+
+```rust
+#![feature(custom_code_classes_in_docs)]
+
+/// ```custom,{class=language-c}
+/// int main(void) { return 0; }
+/// ```
+pub struct Bar;
+```
+
+The text `int main(void) { return 0; }` is rendered without highlighting in a code block
+with the class `language-c`. This can be used to highlight other languages through JavaScript
+libraries for example.
+
+Without the `custom` attribute, it would be generated as a Rust code example with an additional
+`language-C` CSS class. Therefore, if you specifically don't want it to be a Rust code example,
+don't forget to add the `custom` attribute.
+
+To be noted that you can replace `class=` with `.` to achieve the same result:
+
+```rust
+#![feature(custom_code_classes_in_docs)]
+
+/// ```custom,{.language-c}
+/// int main(void) { return 0; }
+/// ```
+pub struct Bar;
+```
+
+To be noted, `rust` and `.rust`/`class=rust` have different effects: `rust` indicates that this is
+a Rust code block whereas the two others add a "rust" CSS class on the code block.
+
+You can also use double quotes:
+
+```rust
+#![feature(custom_code_classes_in_docs)]
+
+/// ```"not rust" {."hello everyone"}
+/// int main(void) { return 0; }
+/// ```
+pub struct Bar;
+```
diff --git a/src/doc/rustdoc/src/write-documentation/re-exports.md b/src/doc/rustdoc/src/write-documentation/re-exports.md
index 593428b8a70..8ce059cc29c 100644
--- a/src/doc/rustdoc/src/write-documentation/re-exports.md
+++ b/src/doc/rustdoc/src/write-documentation/re-exports.md
@@ -86,7 +86,7 @@ pub use self::Hidden as InlinedHidden;
 ```
 
 The same applies on re-exports themselves: if you have multiple re-exports and some of them have
-`#[doc(hidden)]`, then these ones (and only these) own't appear in the documentation:
+`#[doc(hidden)]`, then these ones (and only these) won't appear in the documentation:
 
 ```rust,ignore (inline)
 mod private_mod {
diff --git a/src/doc/unstable-book/src/compiler-flags/path-options.md b/src/doc/unstable-book/src/compiler-flags/path-options.md
deleted file mode 100644
index 0786ef1f166..00000000000
--- a/src/doc/unstable-book/src/compiler-flags/path-options.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# `--print` Options
-
-The behavior of the `--print` flag can be modified by optionally be specifying a filepath
-for each requested information kind, in the format `--print KIND=PATH`, just like for
-`--emit`. When a path is specified, information will be written there instead of to stdout.
-
-This is unstable feature, so you have to provide `-Zunstable-options` to enable it.
-
-## Examples
-
-`rustc main.rs -Z unstable-options --print cfg=cfgs.txt`
diff --git a/src/doc/unstable-book/src/language-features/no-coverage.md b/src/doc/unstable-book/src/language-features/coverage-attribute.md
index 327cdb39791..0a9bd07de07 100644
--- a/src/doc/unstable-book/src/language-features/no-coverage.md
+++ b/src/doc/unstable-book/src/language-features/coverage-attribute.md
@@ -1,4 +1,4 @@
-# `no_coverage`
+# `coverage_attribute`
 
 The tracking issue for this feature is: [#84605]
 
@@ -6,7 +6,7 @@ The tracking issue for this feature is: [#84605]
 
 ---
 
-The `no_coverage` attribute can be used to selectively disable coverage
+The `coverage` attribute can be used to selectively disable coverage
 instrumentation in an annotated function. This might be useful to:
 
 -   Avoid instrumentation overhead in a performance critical function
@@ -16,14 +16,14 @@ instrumentation in an annotated function. This might be useful to:
 ## Example
 
 ```rust
-#![feature(no_coverage)]
+#![feature(coverage_attribute)]
 
 // `foo()` will get coverage instrumentation (by default)
 fn foo() {
   // ...
 }
 
-#[no_coverage]
+#[coverage(off)]
 fn bar() {
   // ...
 }
diff --git a/src/etc/rust_analyzer_settings.json b/src/etc/rust_analyzer_settings.json
index 6e5e2c35005..32a04cfd5d1 100644
--- a/src/etc/rust_analyzer_settings.json
+++ b/src/etc/rust_analyzer_settings.json
@@ -16,10 +16,10 @@
         "compiler/rustc_codegen_gcc/Cargo.toml"
     ],
     "rust-analyzer.rustfmt.overrideCommand": [
-        "./build/host/rustfmt/bin/rustfmt",
+        "${workspaceFolder}/build/host/rustfmt/bin/rustfmt",
         "--edition=2021"
     ],
-    "rust-analyzer.procMacro.server": "./build/host/stage0/libexec/rust-analyzer-proc-macro-srv",
+    "rust-analyzer.procMacro.server": "${workspaceFolder}/build/host/stage0/libexec/rust-analyzer-proc-macro-srv",
     "rust-analyzer.procMacro.enable": true,
     "rust-analyzer.cargo.buildScripts.enable": true,
     "rust-analyzer.cargo.buildScripts.invocationLocation": "root",
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index cc86a3d7475..dd43ee03383 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -20,7 +20,8 @@ use rustc_span::symbol::{kw, sym, Symbol};
 use crate::clean::{
     self, clean_fn_decl_from_did_and_sig, clean_generics, clean_impl_item, clean_middle_assoc_item,
     clean_middle_field, clean_middle_ty, clean_trait_ref_with_bindings, clean_ty,
-    clean_ty_generics, clean_variant_def, utils, Attributes, AttributesExt, ImplKind, ItemId, Type,
+    clean_ty_alias_inner_type, clean_ty_generics, clean_variant_def, utils, Attributes,
+    AttributesExt, ImplKind, ItemId, Type,
 };
 use crate::core::DocContext;
 use crate::formats::item_type::ItemType;
@@ -289,16 +290,14 @@ fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union {
 
 fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box<clean::TypeAlias> {
     let predicates = cx.tcx.explicit_predicates_of(did);
-    let type_ = clean_middle_ty(
-        ty::Binder::dummy(cx.tcx.type_of(did).instantiate_identity()),
-        cx,
-        Some(did),
-        None,
-    );
+    let ty = cx.tcx.type_of(did).instantiate_identity();
+    let type_ = clean_middle_ty(ty::Binder::dummy(ty), cx, Some(did), None);
+    let inner_type = clean_ty_alias_inner_type(ty, cx);
 
     Box::new(clean::TypeAlias {
         type_,
         generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates),
+        inner_type,
         item_type: None,
     })
 }
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index b584c32a4c7..190b1b038b6 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -24,6 +24,7 @@ use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData};
 use rustc_middle::metadata::Reexport;
 use rustc_middle::middle::resolve_bound_vars as rbv;
 use rustc_middle::ty::fold::TypeFolder;
+use rustc_middle::ty::GenericArgsRef;
 use rustc_middle::ty::TypeVisitableExt;
 use rustc_middle::ty::{self, AdtKind, EarlyBinder, Ty, TyCtxt};
 use rustc_middle::{bug, span_bug};
@@ -541,7 +542,7 @@ fn clean_generic_param_def<'tcx>(
                 },
             )
         }
-        ty::GenericParamDefKind::Const { has_default } => (
+        ty::GenericParamDefKind::Const { has_default, .. } => (
             def.name,
             GenericParamDefKind::Const {
                 ty: Box::new(clean_middle_ty(
@@ -955,6 +956,43 @@ fn clean_ty_generics<'tcx>(
     }
 }
 
+fn clean_ty_alias_inner_type<'tcx>(
+    ty: Ty<'tcx>,
+    cx: &mut DocContext<'tcx>,
+) -> Option<TypeAliasInnerType> {
+    let ty::Adt(adt_def, args) = ty.kind() else {
+        return None;
+    };
+
+    Some(if adt_def.is_enum() {
+        let variants: rustc_index::IndexVec<_, _> = adt_def
+            .variants()
+            .iter()
+            .map(|variant| clean_variant_def_with_args(variant, args, cx))
+            .collect();
+
+        TypeAliasInnerType::Enum {
+            variants,
+            is_non_exhaustive: adt_def.is_variant_list_non_exhaustive(),
+        }
+    } else {
+        let variant = adt_def
+            .variants()
+            .iter()
+            .next()
+            .unwrap_or_else(|| bug!("a struct or union should always have one variant def"));
+
+        let fields: Vec<_> =
+            clean_variant_def_with_args(variant, args, cx).kind.inner_items().cloned().collect();
+
+        if adt_def.is_struct() {
+            TypeAliasInnerType::Struct { ctor_kind: variant.ctor_kind(), fields }
+        } else {
+            TypeAliasInnerType::Union { fields }
+        }
+    })
+}
+
 fn clean_proc_macro<'tcx>(
     item: &hir::Item<'tcx>,
     name: &mut Symbol,
@@ -1222,6 +1260,7 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
                     Box::new(TypeAlias {
                         type_: clean_ty(default, cx),
                         generics,
+                        inner_type: None,
                         item_type: Some(item_type),
                     }),
                     bounds,
@@ -1264,7 +1303,12 @@ pub(crate) fn clean_impl_item<'tcx>(
                     None,
                 );
                 AssocTypeItem(
-                    Box::new(TypeAlias { type_, generics, item_type: Some(item_type) }),
+                    Box::new(TypeAlias {
+                        type_,
+                        generics,
+                        inner_type: None,
+                        item_type: Some(item_type),
+                    }),
                     Vec::new(),
                 )
             }
@@ -1471,6 +1515,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
                                 None,
                             ),
                             generics,
+                            inner_type: None,
                             item_type: None,
                         }),
                         bounds,
@@ -1490,6 +1535,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
                             None,
                         ),
                         generics,
+                        inner_type: None,
                         item_type: None,
                     }),
                     // Associated types inside trait or inherent impls are not allowed to have
@@ -1817,7 +1863,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
                     // does nothing for `ConstKind::Param`.
                     let ct = ty::Const::from_anon_const(cx.tcx, anon_const.def_id);
                     let param_env = cx.tcx.param_env(anon_const.def_id);
-                    print_const(cx, ct.eval(cx.tcx, param_env))
+                    print_const(cx, ct.normalize(cx.tcx, param_env))
                 }
             };
 
@@ -2036,7 +2082,7 @@ pub(crate) fn clean_middle_ty<'tcx>(
         ty::Str => Primitive(PrimitiveType::Str),
         ty::Slice(ty) => Slice(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None))),
         ty::Array(ty, mut n) => {
-            n = n.eval(cx.tcx, ty::ParamEnv::reveal_all());
+            n = n.normalize(cx.tcx, ty::ParamEnv::reveal_all());
             let n = print_const(cx, n);
             Array(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), n.into())
         }
@@ -2363,6 +2409,83 @@ pub(crate) fn clean_variant_def<'tcx>(variant: &ty::VariantDef, cx: &mut DocCont
     )
 }
 
+pub(crate) fn clean_variant_def_with_args<'tcx>(
+    variant: &ty::VariantDef,
+    args: &GenericArgsRef<'tcx>,
+    cx: &mut DocContext<'tcx>,
+) -> Item {
+    let discriminant = match variant.discr {
+        ty::VariantDiscr::Explicit(def_id) => Some(Discriminant { expr: None, value: def_id }),
+        ty::VariantDiscr::Relative(_) => None,
+    };
+
+    use rustc_middle::traits::ObligationCause;
+    use rustc_trait_selection::infer::TyCtxtInferExt;
+    use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
+
+    let infcx = cx.tcx.infer_ctxt().build();
+    let kind = match variant.ctor_kind() {
+        Some(CtorKind::Const) => VariantKind::CLike,
+        Some(CtorKind::Fn) => VariantKind::Tuple(
+            variant
+                .fields
+                .iter()
+                .map(|field| {
+                    let ty = cx.tcx.type_of(field.did).instantiate(cx.tcx, args);
+
+                    // normalize the type to only show concrete types
+                    // note: we do not use try_normalize_erasing_regions since we
+                    // do care about showing the regions
+                    let ty = infcx
+                        .at(&ObligationCause::dummy(), cx.param_env)
+                        .query_normalize(ty)
+                        .map(|normalized| normalized.value)
+                        .unwrap_or(ty);
+
+                    clean_field_with_def_id(
+                        field.did,
+                        field.name,
+                        clean_middle_ty(ty::Binder::dummy(ty), cx, Some(field.did), None),
+                        cx,
+                    )
+                })
+                .collect(),
+        ),
+        None => VariantKind::Struct(VariantStruct {
+            fields: variant
+                .fields
+                .iter()
+                .map(|field| {
+                    let ty = cx.tcx.type_of(field.did).instantiate(cx.tcx, args);
+
+                    // normalize the type to only show concrete types
+                    // note: we do not use try_normalize_erasing_regions since we
+                    // do care about showing the regions
+                    let ty = infcx
+                        .at(&ObligationCause::dummy(), cx.param_env)
+                        .query_normalize(ty)
+                        .map(|normalized| normalized.value)
+                        .unwrap_or(ty);
+
+                    clean_field_with_def_id(
+                        field.did,
+                        field.name,
+                        clean_middle_ty(ty::Binder::dummy(ty), cx, Some(field.did), None),
+                        cx,
+                    )
+                })
+                .collect(),
+        }),
+    };
+
+    Item::from_def_id_and_parts(
+        variant.def_id,
+        Some(variant.name),
+        VariantItem(Variant { kind, discriminant }),
+        cx,
+    )
+}
+
 fn clean_variant_data<'tcx>(
     variant: &hir::VariantData<'tcx>,
     disr_expr: &Option<hir::AnonConst>,
@@ -2617,7 +2740,7 @@ fn clean_maybe_renamed_item<'tcx>(
             ItemKind::TyAlias(hir_ty, generics) => {
                 *cx.current_type_aliases.entry(def_id).or_insert(0) += 1;
                 let rustdoc_ty = clean_ty(hir_ty, cx);
-                let ty = clean_middle_ty(
+                let type_ = clean_middle_ty(
                     ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)),
                     cx,
                     None,
@@ -2630,10 +2753,15 @@ fn clean_maybe_renamed_item<'tcx>(
                         cx.current_type_aliases.remove(&def_id);
                     }
                 }
+
+                let ty = cx.tcx.type_of(def_id).instantiate_identity();
+                let inner_type = clean_ty_alias_inner_type(ty, cx);
+
                 TypeAliasItem(Box::new(TypeAlias {
-                    type_: rustdoc_ty,
                     generics,
-                    item_type: Some(ty),
+                    inner_type,
+                    type_: rustdoc_ty,
+                    item_type: Some(type_),
                 }))
             }
             ItemKind::Enum(ref def, generics) => EnumItem(Enum {
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 9134d5268da..b276745f317 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -25,7 +25,9 @@ use rustc_index::IndexVec;
 use rustc_metadata::rendered_const;
 use rustc_middle::ty::fast_reject::SimplifiedType;
 use rustc_middle::ty::{self, TyCtxt, Visibility};
-use rustc_resolve::rustdoc::{add_doc_fragment, attrs_to_doc_fragments, inner_docs, DocFragment};
+use rustc_resolve::rustdoc::{
+    add_doc_fragment, attrs_to_doc_fragments, inner_docs, span_of_fragments, DocFragment,
+};
 use rustc_session::Session;
 use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
@@ -397,7 +399,7 @@ impl Item {
     }
 
     pub(crate) fn attr_span(&self, tcx: TyCtxt<'_>) -> rustc_span::Span {
-        crate::passes::span_of_attrs(&self.attrs)
+        span_of_fragments(&self.attrs.doc_strings)
             .unwrap_or_else(|| self.span(tcx).map_or(rustc_span::DUMMY_SP, |span| span.inner()))
     }
 
@@ -2231,9 +2233,19 @@ pub(crate) struct PathSegment {
 }
 
 #[derive(Clone, Debug)]
+pub(crate) enum TypeAliasInnerType {
+    Enum { variants: IndexVec<VariantIdx, Item>, is_non_exhaustive: bool },
+    Union { fields: Vec<Item> },
+    Struct { ctor_kind: Option<CtorKind>, fields: Vec<Item> },
+}
+
+#[derive(Clone, Debug)]
 pub(crate) struct TypeAlias {
     pub(crate) type_: Type,
     pub(crate) generics: Generics,
+    /// Inner `AdtDef` type, ie `type TyKind = IrTyKind<Adt, Ty>`,
+    /// to be shown directly on the typedef page.
+    pub(crate) inner_type: Option<TypeAliasInnerType>,
     /// `type_` can come from either the HIR or from metadata. If it comes from HIR, it may be a type
     /// alias instead of the final type. This will always have the final type, regardless of whether
     /// `type_` came from HIR or from metadata.
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index 1ce7efdfc20..99aa979027f 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -157,6 +157,12 @@ pub(crate) struct Options {
     /// Note: this field is duplicated in `RenderOptions` because it's useful
     /// to have it in both places.
     pub(crate) unstable_features: rustc_feature::UnstableFeatures,
+
+    /// All commandline args used to invoke the compiler, with @file args fully expanded.
+    /// This will only be used within debug info, e.g. in the pdb file on windows
+    /// This is mainly useful for other tools that reads that debuginfo to figure out
+    /// how to call the compiler with the same arguments.
+    pub(crate) expanded_args: Vec<String>,
 }
 
 impl fmt::Debug for Options {
@@ -404,9 +410,15 @@ impl Options {
 
         let to_check = matches.opt_strs("check-theme");
         if !to_check.is_empty() {
-            let paths = match theme::load_css_paths(
-                std::str::from_utf8(static_files::STATIC_FILES.theme_light_css.bytes).unwrap(),
-            ) {
+            let mut content =
+                std::str::from_utf8(static_files::STATIC_FILES.rustdoc_css.bytes).unwrap();
+            if let Some((_, inside)) = content.split_once("/* Begin theme: light */") {
+                content = inside;
+            }
+            if let Some((inside, _)) = content.split_once("/* End theme: light */") {
+                content = inside;
+            }
+            let paths = match theme::load_css_paths(content) {
                 Ok(p) => p,
                 Err(e) => {
                     diag.struct_err(e).emit();
@@ -544,9 +556,15 @@ impl Options {
 
         let mut themes = Vec::new();
         if matches.opt_present("theme") {
-            let paths = match theme::load_css_paths(
-                std::str::from_utf8(static_files::STATIC_FILES.theme_light_css.bytes).unwrap(),
-            ) {
+            let mut content =
+                std::str::from_utf8(static_files::STATIC_FILES.rustdoc_css.bytes).unwrap();
+            if let Some((_, inside)) = content.split_once("/* Begin theme: light */") {
+                content = inside;
+            }
+            if let Some((inside, _)) = content.split_once("/* End theme: light */") {
+                content = inside;
+            }
+            let paths = match theme::load_css_paths(content) {
                 Ok(p) => p,
                 Err(e) => {
                     diag.struct_err(e).emit();
@@ -744,6 +762,7 @@ impl Options {
             json_unused_externs,
             scrape_examples_options,
             unstable_features,
+            expanded_args: args,
         };
         let render_options = RenderOptions {
             output,
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index eadff37b592..7cd25ef444b 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -194,6 +194,7 @@ pub(crate) fn create_config(
         describe_lints,
         lint_cap,
         scrape_examples_options,
+        expanded_args,
         ..
     }: RustdocOptions,
     RenderOptions { document_private, .. }: &RenderOptions,
@@ -291,6 +292,7 @@ pub(crate) fn create_config(
         make_codegen_backend: None,
         registry: rustc_driver::diagnostics_registry(),
         ice_file: None,
+        expanded_args,
     }
 }
 
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 36d5adb6304..ea87268877f 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -10,6 +10,7 @@ use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::TyCtxt;
 use rustc_parse::maybe_new_parser_from_source_str;
 use rustc_parse::parser::attr::InnerAttrPolicy;
+use rustc_resolve::rustdoc::span_of_fragments;
 use rustc_session::config::{self, CrateType, ErrorOutputType};
 use rustc_session::parse::ParseSess;
 use rustc_session::{lint, EarlyErrorHandler, Session};
@@ -33,7 +34,6 @@ use crate::clean::{types::AttributesExt, Attributes};
 use crate::config::Options as RustdocOptions;
 use crate::html::markdown::{self, ErrorCodes, Ignore, LangString};
 use crate::lint::init_lints;
-use crate::passes::span_of_attrs;
 
 /// Options that apply to all doctests in a crate or Markdown file (for `rustdoc foo.md`).
 #[derive(Clone, Default)]
@@ -109,6 +109,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
         make_codegen_backend: None,
         registry: rustc_driver::diagnostics_registry(),
         ice_file: None,
+        expanded_args: options.expanded_args.clone(),
     };
 
     let test_args = options.test_args.clone();
@@ -1240,7 +1241,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
                 Some(&crate::html::markdown::ExtraInfo::new(
                     self.tcx,
                     def_id.to_def_id(),
-                    span_of_attrs(&attrs).unwrap_or(sp),
+                    span_of_fragments(&attrs.doc_strings).unwrap_or(sp),
                 )),
             );
         }
diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs
index ceba643ed5f..cf11e2d7899 100644
--- a/src/librustdoc/fold.rs
+++ b/src/librustdoc/fold.rs
@@ -52,10 +52,31 @@ pub(crate) trait DocFolder: Sized {
 
                 VariantItem(Variant { kind, discriminant })
             }
+            TypeAliasItem(mut typealias) => {
+                typealias.inner_type = typealias.inner_type.map(|inner_type| match inner_type {
+                    TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
+                        let variants = variants
+                            .into_iter_enumerated()
+                            .filter_map(|(_, x)| self.fold_item(x))
+                            .collect();
+
+                        TypeAliasInnerType::Enum { variants, is_non_exhaustive }
+                    }
+                    TypeAliasInnerType::Union { fields } => {
+                        let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
+                        TypeAliasInnerType::Union { fields }
+                    }
+                    TypeAliasInnerType::Struct { ctor_kind, fields } => {
+                        let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
+                        TypeAliasInnerType::Struct { ctor_kind, fields }
+                    }
+                });
+
+                TypeAliasItem(typealias)
+            }
             ExternCrateItem { src: _ }
             | ImportItem(_)
             | FunctionItem(_)
-            | TypeAliasItem(_)
             | OpaqueTyItem(_)
             | StaticItem(_)
             | ConstantItem(_)
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index 9a34e7cce8a..4c6e7dfb987 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -457,6 +457,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
             | clean::StructItem(..)
             | clean::UnionItem(..)
             | clean::VariantItem(..)
+            | clean::TypeAliasItem(..)
             | clean::ImplItem(..) => {
                 self.cache.parent_stack.push(ParentStackItem::new(&item));
                 (self.fold_item_recur(item), true)
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 039e8cdb987..d8e36139a78 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -52,8 +52,9 @@ pub(crate) fn render_example_with_highlighting(
     out: &mut Buffer,
     tooltip: Tooltip,
     playground_button: Option<&str>,
+    extra_classes: &[String],
 ) {
-    write_header(out, "rust-example-rendered", None, tooltip);
+    write_header(out, "rust-example-rendered", None, tooltip, extra_classes);
     write_code(out, src, None, None);
     write_footer(out, playground_button);
 }
@@ -65,7 +66,13 @@ pub(crate) fn render_item_decl_with_highlighting(src: &str, out: &mut Buffer) {
     write!(out, "</pre>");
 }
 
-fn write_header(out: &mut Buffer, class: &str, extra_content: Option<Buffer>, tooltip: Tooltip) {
+fn write_header(
+    out: &mut Buffer,
+    class: &str,
+    extra_content: Option<Buffer>,
+    tooltip: Tooltip,
+    extra_classes: &[String],
+) {
     write!(
         out,
         "<div class=\"example-wrap{}\">",
@@ -100,9 +107,19 @@ fn write_header(out: &mut Buffer, class: &str, extra_content: Option<Buffer>, to
         out.push_buffer(extra);
     }
     if class.is_empty() {
-        write!(out, "<pre class=\"rust\">");
+        write!(
+            out,
+            "<pre class=\"rust{}{}\">",
+            if extra_classes.is_empty() { "" } else { " " },
+            extra_classes.join(" "),
+        );
     } else {
-        write!(out, "<pre class=\"rust {class}\">");
+        write!(
+            out,
+            "<pre class=\"rust {class}{}{}\">",
+            if extra_classes.is_empty() { "" } else { " " },
+            extra_classes.join(" "),
+        );
     }
     write!(out, "<code>");
 }
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 98cc38a10d4..177fb1a9426 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -26,6 +26,7 @@
 //! ```
 
 use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::TyCtxt;
 pub(crate) use rustc_resolve::rustdoc::main_body_opts;
@@ -37,8 +38,9 @@ use once_cell::sync::Lazy;
 use std::borrow::Cow;
 use std::collections::VecDeque;
 use std::fmt::Write;
+use std::iter::Peekable;
 use std::ops::{ControlFlow, Range};
-use std::str;
+use std::str::{self, CharIndices};
 
 use crate::clean::RenderedLink;
 use crate::doctest;
@@ -243,11 +245,21 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
                 let parse_result =
                     LangString::parse_without_check(lang, self.check_error_codes, false);
                 if !parse_result.rust {
+                    let added_classes = parse_result.added_classes;
+                    let lang_string = if let Some(lang) = parse_result.unknown.first() {
+                        format!("language-{}", lang)
+                    } else {
+                        String::new()
+                    };
+                    let whitespace = if added_classes.is_empty() { "" } else { " " };
                     return Some(Event::Html(
                         format!(
                             "<div class=\"example-wrap\">\
-                                 <pre class=\"language-{lang}\"><code>{text}</code></pre>\
+                                 <pre class=\"{lang_string}{whitespace}{added_classes}\">\
+                                     <code>{text}</code>\
+                                 </pre>\
                              </div>",
+                            added_classes = added_classes.join(" "),
                             text = Escape(&original_text),
                         )
                         .into(),
@@ -258,6 +270,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
             CodeBlockKind::Indented => Default::default(),
         };
 
+        let added_classes = parse_result.added_classes;
         let lines = original_text.lines().filter_map(|l| map_line(l).for_html());
         let text = lines.intersperse("\n".into()).collect::<String>();
 
@@ -315,6 +328,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
             &mut s,
             tooltip,
             playground_button.as_deref(),
+            &added_classes,
         );
         Some(Event::Html(s.into_inner().into()))
     }
@@ -712,6 +726,17 @@ pub(crate) fn find_testable_code<T: doctest::Tester>(
     enable_per_target_ignores: bool,
     extra_info: Option<&ExtraInfo<'_>>,
 ) {
+    find_codes(doc, tests, error_codes, enable_per_target_ignores, extra_info, false)
+}
+
+pub(crate) fn find_codes<T: doctest::Tester>(
+    doc: &str,
+    tests: &mut T,
+    error_codes: ErrorCodes,
+    enable_per_target_ignores: bool,
+    extra_info: Option<&ExtraInfo<'_>>,
+    include_non_rust: bool,
+) {
     let mut parser = Parser::new(doc).into_offset_iter();
     let mut prev_offset = 0;
     let mut nb_lines = 0;
@@ -734,7 +759,7 @@ pub(crate) fn find_testable_code<T: doctest::Tester>(
                     }
                     CodeBlockKind::Indented => Default::default(),
                 };
-                if !block_info.rust {
+                if !include_non_rust && !block_info.rust {
                     continue;
                 }
 
@@ -784,7 +809,23 @@ impl<'tcx> ExtraInfo<'tcx> {
         ExtraInfo { def_id, sp, tcx }
     }
 
-    fn error_invalid_codeblock_attr(&self, msg: String, help: &'static str) {
+    fn error_invalid_codeblock_attr(&self, msg: impl Into<DiagnosticMessage>) {
+        if let Some(def_id) = self.def_id.as_local() {
+            self.tcx.struct_span_lint_hir(
+                crate::lint::INVALID_CODEBLOCK_ATTRIBUTES,
+                self.tcx.hir().local_def_id_to_hir_id(def_id),
+                self.sp,
+                msg,
+                |l| l,
+            );
+        }
+    }
+
+    fn error_invalid_codeblock_attr_with_help(
+        &self,
+        msg: impl Into<DiagnosticMessage>,
+        help: impl Into<SubdiagnosticMessage>,
+    ) {
         if let Some(def_id) = self.def_id.as_local() {
             self.tcx.struct_span_lint_hir(
                 crate::lint::INVALID_CODEBLOCK_ATTRIBUTES,
@@ -808,6 +849,8 @@ pub(crate) struct LangString {
     pub(crate) compile_fail: bool,
     pub(crate) error_codes: Vec<String>,
     pub(crate) edition: Option<Edition>,
+    pub(crate) added_classes: Vec<String>,
+    pub(crate) unknown: Vec<String>,
 }
 
 #[derive(Eq, PartialEq, Clone, Debug)]
@@ -817,6 +860,276 @@ pub(crate) enum Ignore {
     Some(Vec<String>),
 }
 
+/// This is the parser for fenced codeblocks attributes. It implements the following eBNF:
+///
+/// ```eBNF
+/// lang-string = *(token-list / delimited-attribute-list / comment)
+///
+/// bareword = CHAR *(CHAR)
+/// quoted-string = QUOTE *(NONQUOTE) QUOTE
+/// token = bareword / quoted-string
+/// sep = COMMA/WS *(COMMA/WS)
+/// attribute = (DOT token)/(token EQUAL token)
+/// attribute-list = [sep] attribute *(sep attribute) [sep]
+/// delimited-attribute-list = OPEN-CURLY-BRACKET attribute-list CLOSE-CURLY-BRACKET
+/// token-list = [sep] token *(sep token) [sep]
+/// comment = OPEN_PAREN *(all characters) CLOSE_PAREN
+///
+/// OPEN_PAREN = "("
+/// CLOSE_PARENT = ")"
+/// OPEN-CURLY-BRACKET = "{"
+/// CLOSE-CURLY-BRACKET = "}"
+/// CHAR = ALPHA / DIGIT / "_" / "-" / ":"
+/// QUOTE = %x22
+/// NONQUOTE = %x09 / %x20 / %x21 / %x23-7E ; TAB / SPACE / all printable characters except `"`
+/// COMMA = ","
+/// DOT = "."
+/// EQUAL = "="
+///
+/// ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
+/// DIGIT = %x30-39
+/// WS = %x09 / " "
+/// ```
+pub(crate) struct TagIterator<'a, 'tcx> {
+    inner: Peekable<CharIndices<'a>>,
+    data: &'a str,
+    is_in_attribute_block: bool,
+    extra: Option<&'a ExtraInfo<'tcx>>,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub(crate) enum LangStringToken<'a> {
+    LangToken(&'a str),
+    ClassAttribute(&'a str),
+    KeyValueAttribute(&'a str, &'a str),
+}
+
+fn is_bareword_char(c: char) -> bool {
+    c == '_' || c == '-' || c == ':' || c.is_ascii_alphabetic() || c.is_ascii_digit()
+}
+fn is_separator(c: char) -> bool {
+    c == ' ' || c == ',' || c == '\t'
+}
+
+struct Indices {
+    start: usize,
+    end: usize,
+}
+
+impl<'a, 'tcx> TagIterator<'a, 'tcx> {
+    pub(crate) fn new(data: &'a str, extra: Option<&'a ExtraInfo<'tcx>>) -> Self {
+        Self { inner: data.char_indices().peekable(), data, is_in_attribute_block: false, extra }
+    }
+
+    fn emit_error(&self, err: impl Into<DiagnosticMessage>) {
+        if let Some(extra) = self.extra {
+            extra.error_invalid_codeblock_attr(err);
+        }
+    }
+
+    fn skip_separators(&mut self) -> Option<usize> {
+        while let Some((pos, c)) = self.inner.peek() {
+            if !is_separator(*c) {
+                return Some(*pos);
+            }
+            self.inner.next();
+        }
+        None
+    }
+
+    fn parse_string(&mut self, start: usize) -> Option<Indices> {
+        while let Some((pos, c)) = self.inner.next() {
+            if c == '"' {
+                return Some(Indices { start: start + 1, end: pos });
+            }
+        }
+        self.emit_error("unclosed quote string `\"`");
+        None
+    }
+
+    fn parse_class(&mut self, start: usize) -> Option<LangStringToken<'a>> {
+        while let Some((pos, c)) = self.inner.peek().copied() {
+            if is_bareword_char(c) {
+                self.inner.next();
+            } else {
+                let class = &self.data[start + 1..pos];
+                if class.is_empty() {
+                    self.emit_error(format!("unexpected `{c}` character after `.`"));
+                    return None;
+                } else if self.check_after_token() {
+                    return Some(LangStringToken::ClassAttribute(class));
+                } else {
+                    return None;
+                }
+            }
+        }
+        let class = &self.data[start + 1..];
+        if class.is_empty() {
+            self.emit_error("missing character after `.`");
+            None
+        } else if self.check_after_token() {
+            Some(LangStringToken::ClassAttribute(class))
+        } else {
+            None
+        }
+    }
+
+    fn parse_token(&mut self, start: usize) -> Option<Indices> {
+        while let Some((pos, c)) = self.inner.peek() {
+            if !is_bareword_char(*c) {
+                return Some(Indices { start, end: *pos });
+            }
+            self.inner.next();
+        }
+        self.emit_error("unexpected end");
+        None
+    }
+
+    fn parse_key_value(&mut self, c: char, start: usize) -> Option<LangStringToken<'a>> {
+        let key_indices =
+            if c == '"' { self.parse_string(start)? } else { self.parse_token(start)? };
+        if key_indices.start == key_indices.end {
+            self.emit_error("unexpected empty string as key");
+            return None;
+        }
+
+        if let Some((_, c)) = self.inner.next() {
+            if c != '=' {
+                self.emit_error(format!("expected `=`, found `{}`", c));
+                return None;
+            }
+        } else {
+            self.emit_error("unexpected end");
+            return None;
+        }
+        let value_indices = match self.inner.next() {
+            Some((pos, '"')) => self.parse_string(pos)?,
+            Some((pos, c)) if is_bareword_char(c) => self.parse_token(pos)?,
+            Some((_, c)) => {
+                self.emit_error(format!("unexpected `{c}` character after `=`"));
+                return None;
+            }
+            None => {
+                self.emit_error("expected value after `=`");
+                return None;
+            }
+        };
+        if value_indices.start == value_indices.end {
+            self.emit_error("unexpected empty string as value");
+            None
+        } else if self.check_after_token() {
+            Some(LangStringToken::KeyValueAttribute(
+                &self.data[key_indices.start..key_indices.end],
+                &self.data[value_indices.start..value_indices.end],
+            ))
+        } else {
+            None
+        }
+    }
+
+    /// Returns `false` if an error was emitted.
+    fn check_after_token(&mut self) -> bool {
+        if let Some((_, c)) = self.inner.peek().copied() {
+            if c == '}' || is_separator(c) || c == '(' {
+                true
+            } else {
+                self.emit_error(format!("unexpected `{c}` character"));
+                false
+            }
+        } else {
+            // The error will be caught on the next iteration.
+            true
+        }
+    }
+
+    fn parse_in_attribute_block(&mut self) -> Option<LangStringToken<'a>> {
+        while let Some((pos, c)) = self.inner.next() {
+            if c == '}' {
+                self.is_in_attribute_block = false;
+                return self.next();
+            } else if c == '.' {
+                return self.parse_class(pos);
+            } else if c == '"' || is_bareword_char(c) {
+                return self.parse_key_value(c, pos);
+            } else {
+                self.emit_error(format!("unexpected character `{c}`"));
+                return None;
+            }
+        }
+        self.emit_error("unclosed attribute block (`{}`): missing `}` at the end");
+        None
+    }
+
+    /// Returns `false` if an error was emitted.
+    fn skip_paren_block(&mut self) -> bool {
+        while let Some((_, c)) = self.inner.next() {
+            if c == ')' {
+                return true;
+            }
+        }
+        self.emit_error("unclosed comment: missing `)` at the end");
+        false
+    }
+
+    fn parse_outside_attribute_block(&mut self, start: usize) -> Option<LangStringToken<'a>> {
+        while let Some((pos, c)) = self.inner.next() {
+            if c == '"' {
+                if pos != start {
+                    self.emit_error("expected ` `, `{` or `,` found `\"`");
+                    return None;
+                }
+                let indices = self.parse_string(pos)?;
+                if let Some((_, c)) = self.inner.peek().copied() && c != '{' && !is_separator(c) && c != '(' {
+                    self.emit_error(format!("expected ` `, `{{` or `,` after `\"`, found `{c}`"));
+                    return None;
+                }
+                return Some(LangStringToken::LangToken(&self.data[indices.start..indices.end]));
+            } else if c == '{' {
+                self.is_in_attribute_block = true;
+                return self.next();
+            } else if is_bareword_char(c) {
+                continue;
+            } else if is_separator(c) {
+                if pos != start {
+                    return Some(LangStringToken::LangToken(&self.data[start..pos]));
+                }
+                return self.next();
+            } else if c == '(' {
+                if !self.skip_paren_block() {
+                    return None;
+                }
+                if pos != start {
+                    return Some(LangStringToken::LangToken(&self.data[start..pos]));
+                }
+                return self.next();
+            } else {
+                self.emit_error(format!("unexpected character `{c}`"));
+                return None;
+            }
+        }
+        let token = &self.data[start..];
+        if token.is_empty() { None } else { Some(LangStringToken::LangToken(&self.data[start..])) }
+    }
+}
+
+impl<'a, 'tcx> Iterator for TagIterator<'a, 'tcx> {
+    type Item = LangStringToken<'a>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let Some(start) = self.skip_separators() else {
+            if self.is_in_attribute_block {
+                self.emit_error("unclosed attribute block (`{}`): missing `}` at the end");
+            }
+            return None;
+        };
+        if self.is_in_attribute_block {
+            self.parse_in_attribute_block()
+        } else {
+            self.parse_outside_attribute_block(start)
+        }
+    }
+}
+
 impl Default for LangString {
     fn default() -> Self {
         Self {
@@ -829,6 +1142,8 @@ impl Default for LangString {
             compile_fail: false,
             error_codes: Vec::new(),
             edition: None,
+            added_classes: Vec::new(),
+            unknown: Vec::new(),
         }
     }
 }
@@ -838,86 +1153,67 @@ impl LangString {
         string: &str,
         allow_error_code_check: ErrorCodes,
         enable_per_target_ignores: bool,
-    ) -> LangString {
+    ) -> Self {
         Self::parse(string, allow_error_code_check, enable_per_target_ignores, None)
     }
 
-    fn tokens(string: &str) -> impl Iterator<Item = &str> {
-        // Pandoc, which Rust once used for generating documentation,
-        // expects lang strings to be surrounded by `{}` and for each token
-        // to be proceeded by a `.`. Since some of these lang strings are still
-        // loose in the wild, we strip a pair of surrounding `{}` from the lang
-        // string and a leading `.` from each token.
-
-        let string = string.trim();
-
-        let first = string.chars().next();
-        let last = string.chars().last();
-
-        let string = if first == Some('{') && last == Some('}') {
-            &string[1..string.len() - 1]
-        } else {
-            string
-        };
-
-        string
-            .split(|c| c == ',' || c == ' ' || c == '\t')
-            .map(str::trim)
-            .map(|token| token.strip_prefix('.').unwrap_or(token))
-            .filter(|token| !token.is_empty())
-    }
-
     fn parse(
         string: &str,
         allow_error_code_check: ErrorCodes,
         enable_per_target_ignores: bool,
         extra: Option<&ExtraInfo<'_>>,
-    ) -> LangString {
+    ) -> Self {
         let allow_error_code_check = allow_error_code_check.as_bool();
         let mut seen_rust_tags = false;
         let mut seen_other_tags = false;
+        let mut seen_custom_tag = false;
         let mut data = LangString::default();
         let mut ignores = vec![];
 
         data.original = string.to_owned();
 
-        for token in Self::tokens(string) {
+        for token in TagIterator::new(string, extra) {
             match token {
-                "should_panic" => {
+                LangStringToken::LangToken("should_panic") => {
                     data.should_panic = true;
                     seen_rust_tags = !seen_other_tags;
                 }
-                "no_run" => {
+                LangStringToken::LangToken("no_run") => {
                     data.no_run = true;
                     seen_rust_tags = !seen_other_tags;
                 }
-                "ignore" => {
+                LangStringToken::LangToken("ignore") => {
                     data.ignore = Ignore::All;
                     seen_rust_tags = !seen_other_tags;
                 }
-                x if x.starts_with("ignore-") => {
+                LangStringToken::LangToken(x) if x.starts_with("ignore-") => {
                     if enable_per_target_ignores {
                         ignores.push(x.trim_start_matches("ignore-").to_owned());
                         seen_rust_tags = !seen_other_tags;
                     }
                 }
-                "rust" => {
+                LangStringToken::LangToken("rust") => {
                     data.rust = true;
                     seen_rust_tags = true;
                 }
-                "test_harness" => {
+                LangStringToken::LangToken("custom") => {
+                    seen_custom_tag = true;
+                }
+                LangStringToken::LangToken("test_harness") => {
                     data.test_harness = true;
                     seen_rust_tags = !seen_other_tags || seen_rust_tags;
                 }
-                "compile_fail" => {
+                LangStringToken::LangToken("compile_fail") => {
                     data.compile_fail = true;
                     seen_rust_tags = !seen_other_tags || seen_rust_tags;
                     data.no_run = true;
                 }
-                x if x.starts_with("edition") => {
+                LangStringToken::LangToken(x) if x.starts_with("edition") => {
                     data.edition = x[7..].parse::<Edition>().ok();
                 }
-                x if allow_error_code_check && x.starts_with('E') && x.len() == 5 => {
+                LangStringToken::LangToken(x)
+                    if allow_error_code_check && x.starts_with('E') && x.len() == 5 =>
+                {
                     if x[1..].parse::<u32>().is_ok() {
                         data.error_codes.push(x.to_owned());
                         seen_rust_tags = !seen_other_tags || seen_rust_tags;
@@ -925,7 +1221,7 @@ impl LangString {
                         seen_other_tags = true;
                     }
                 }
-                x if extra.is_some() => {
+                LangStringToken::LangToken(x) if extra.is_some() => {
                     let s = x.to_lowercase();
                     if let Some((flag, help)) = if s == "compile-fail"
                         || s == "compile_fail"
@@ -958,15 +1254,30 @@ impl LangString {
                         None
                     } {
                         if let Some(extra) = extra {
-                            extra.error_invalid_codeblock_attr(
+                            extra.error_invalid_codeblock_attr_with_help(
                                 format!("unknown attribute `{x}`. Did you mean `{flag}`?"),
                                 help,
                             );
                         }
                     }
                     seen_other_tags = true;
+                    data.unknown.push(x.to_owned());
+                }
+                LangStringToken::LangToken(x) => {
+                    seen_other_tags = true;
+                    data.unknown.push(x.to_owned());
+                }
+                LangStringToken::KeyValueAttribute(key, value) => {
+                    if key == "class" {
+                        data.added_classes.push(value.to_owned());
+                    } else if let Some(extra) = extra {
+                        extra
+                            .error_invalid_codeblock_attr(format!("unsupported attribute `{key}`"));
+                    }
+                }
+                LangStringToken::ClassAttribute(class) => {
+                    data.added_classes.push(class.to_owned());
                 }
-                _ => seen_other_tags = true,
             }
         }
 
@@ -975,7 +1286,7 @@ impl LangString {
             data.ignore = Ignore::Some(ignores);
         }
 
-        data.rust &= !seen_other_tags || seen_rust_tags;
+        data.rust &= !seen_custom_tag && (!seen_other_tags || seen_rust_tags);
 
         data
     }
@@ -1574,7 +1885,6 @@ fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> {
     map.insert("crate-search-div".into(), 1);
     // This is the list of IDs used in HTML generated in Rust (including the ones
     // used in tera template files).
-    map.insert("mainThemeStyle".into(), 1);
     map.insert("themeStyle".into(), 1);
     map.insert("settings-menu".into(), 1);
     map.insert("help-button".into(), 1);
@@ -1610,6 +1920,7 @@ fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> {
     map.insert("blanket-implementations-list".into(), 1);
     map.insert("deref-methods".into(), 1);
     map.insert("layout".into(), 1);
+    map.insert("aliased-type".into(), 1);
     map
 }
 
diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs
index db8504d15c7..7d89cb0c4e6 100644
--- a/src/librustdoc/html/markdown/tests.rs
+++ b/src/librustdoc/html/markdown/tests.rs
@@ -1,5 +1,8 @@
 use super::{find_testable_code, plain_text_summary, short_markdown_summary};
-use super::{ErrorCodes, HeadingOffset, IdMap, Ignore, LangString, Markdown, MarkdownItemInfo};
+use super::{
+    ErrorCodes, HeadingOffset, IdMap, Ignore, LangString, LangStringToken, Markdown,
+    MarkdownItemInfo, TagIterator,
+};
 use rustc_span::edition::{Edition, DEFAULT_EDITION};
 
 #[test]
@@ -51,10 +54,32 @@ fn test_lang_string_parse() {
 
     t(Default::default());
     t(LangString { original: "rust".into(), ..Default::default() });
-    t(LangString { original: ".rust".into(), ..Default::default() });
-    t(LangString { original: "{rust}".into(), ..Default::default() });
-    t(LangString { original: "{.rust}".into(), ..Default::default() });
-    t(LangString { original: "sh".into(), rust: false, ..Default::default() });
+    t(LangString {
+        original: "rusta".into(),
+        rust: false,
+        unknown: vec!["rusta".into()],
+        ..Default::default()
+    });
+    // error
+    t(LangString { original: "{rust}".into(), rust: true, ..Default::default() });
+    t(LangString {
+        original: "{.rust}".into(),
+        rust: true,
+        added_classes: vec!["rust".into()],
+        ..Default::default()
+    });
+    t(LangString {
+        original: "custom,{.rust}".into(),
+        rust: false,
+        added_classes: vec!["rust".into()],
+        ..Default::default()
+    });
+    t(LangString {
+        original: "sh".into(),
+        rust: false,
+        unknown: vec!["sh".into()],
+        ..Default::default()
+    });
     t(LangString { original: "ignore".into(), ignore: Ignore::All, ..Default::default() });
     t(LangString {
         original: "ignore-foo".into(),
@@ -70,41 +95,56 @@ fn test_lang_string_parse() {
         compile_fail: true,
         ..Default::default()
     });
-    t(LangString { original: "no_run,example".into(), no_run: true, ..Default::default() });
+    t(LangString {
+        original: "no_run,example".into(),
+        no_run: true,
+        unknown: vec!["example".into()],
+        ..Default::default()
+    });
     t(LangString {
         original: "sh,should_panic".into(),
         should_panic: true,
         rust: false,
+        unknown: vec!["sh".into()],
         ..Default::default()
     });
-    t(LangString { original: "example,rust".into(), ..Default::default() });
     t(LangString {
-        original: "test_harness,.rust".into(),
+        original: "example,rust".into(),
+        unknown: vec!["example".into()],
+        ..Default::default()
+    });
+    t(LangString {
+        original: "test_harness,rusta".into(),
         test_harness: true,
+        unknown: vec!["rusta".into()],
         ..Default::default()
     });
     t(LangString {
         original: "text, no_run".into(),
         no_run: true,
         rust: false,
+        unknown: vec!["text".into()],
         ..Default::default()
     });
     t(LangString {
         original: "text,no_run".into(),
         no_run: true,
         rust: false,
+        unknown: vec!["text".into()],
         ..Default::default()
     });
     t(LangString {
         original: "text,no_run, ".into(),
         no_run: true,
         rust: false,
+        unknown: vec!["text".into()],
         ..Default::default()
     });
     t(LangString {
         original: "text,no_run,".into(),
         no_run: true,
         rust: false,
+        unknown: vec!["text".into()],
         ..Default::default()
     });
     t(LangString {
@@ -117,29 +157,125 @@ fn test_lang_string_parse() {
         edition: Some(Edition::Edition2018),
         ..Default::default()
     });
+    t(LangString {
+        original: "{class=test}".into(),
+        added_classes: vec!["test".into()],
+        rust: true,
+        ..Default::default()
+    });
+    t(LangString {
+        original: "custom,{class=test}".into(),
+        added_classes: vec!["test".into()],
+        rust: false,
+        ..Default::default()
+    });
+    t(LangString {
+        original: "{.test}".into(),
+        added_classes: vec!["test".into()],
+        rust: true,
+        ..Default::default()
+    });
+    t(LangString {
+        original: "custom,{.test}".into(),
+        added_classes: vec!["test".into()],
+        rust: false,
+        ..Default::default()
+    });
+    t(LangString {
+        original: "rust,{class=test,.test2}".into(),
+        added_classes: vec!["test".into(), "test2".into()],
+        rust: true,
+        ..Default::default()
+    });
+    t(LangString {
+        original: "{class=test:with:colon .test1}".into(),
+        added_classes: vec!["test:with:colon".into(), "test1".into()],
+        rust: true,
+        ..Default::default()
+    });
+    t(LangString {
+        original: "custom,{class=test:with:colon .test1}".into(),
+        added_classes: vec!["test:with:colon".into(), "test1".into()],
+        rust: false,
+        ..Default::default()
+    });
+    t(LangString {
+        original: "{class=first,class=second}".into(),
+        added_classes: vec!["first".into(), "second".into()],
+        rust: true,
+        ..Default::default()
+    });
+    t(LangString {
+        original: "custom,{class=first,class=second}".into(),
+        added_classes: vec!["first".into(), "second".into()],
+        rust: false,
+        ..Default::default()
+    });
+    t(LangString {
+        original: "{class=first,.second},unknown".into(),
+        added_classes: vec!["first".into(), "second".into()],
+        rust: false,
+        unknown: vec!["unknown".into()],
+        ..Default::default()
+    });
+    t(LangString {
+        original: "{class=first .second} unknown".into(),
+        added_classes: vec!["first".into(), "second".into()],
+        rust: false,
+        unknown: vec!["unknown".into()],
+        ..Default::default()
+    });
+    // error
+    t(LangString { original: "{.first.second}".into(), rust: true, ..Default::default() });
+    // error
+    t(LangString { original: "{class=first=second}".into(), rust: true, ..Default::default() });
+    // error
+    t(LangString { original: "{class=first.second}".into(), rust: true, ..Default::default() });
+    // error
+    t(LangString { original: "{class=.first}".into(), rust: true, ..Default::default() });
+    t(LangString {
+        original: r#"{class="first"}"#.into(),
+        added_classes: vec!["first".into()],
+        rust: true,
+        ..Default::default()
+    });
+    t(LangString {
+        original: r#"custom,{class="first"}"#.into(),
+        added_classes: vec!["first".into()],
+        rust: false,
+        ..Default::default()
+    });
+    // error
+    t(LangString { original: r#"{class=f"irst"}"#.into(), rust: true, ..Default::default() });
 }
 
 #[test]
 fn test_lang_string_tokenizer() {
-    fn case(lang_string: &str, want: &[&str]) {
-        let have = LangString::tokens(lang_string).collect::<Vec<&str>>();
+    fn case(lang_string: &str, want: &[LangStringToken<'_>]) {
+        let have = TagIterator::new(lang_string, None).collect::<Vec<_>>();
         assert_eq!(have, want, "Unexpected lang string split for `{}`", lang_string);
     }
 
     case("", &[]);
-    case("foo", &["foo"]);
-    case("foo,bar", &["foo", "bar"]);
-    case(".foo,.bar", &["foo", "bar"]);
-    case("{.foo,.bar}", &["foo", "bar"]);
-    case("  {.foo,.bar}  ", &["foo", "bar"]);
-    case("foo bar", &["foo", "bar"]);
-    case("foo\tbar", &["foo", "bar"]);
-    case("foo\t, bar", &["foo", "bar"]);
-    case(" foo , bar ", &["foo", "bar"]);
-    case(",,foo,,bar,,", &["foo", "bar"]);
-    case("foo=bar", &["foo=bar"]);
-    case("a-b-c", &["a-b-c"]);
-    case("a_b_c", &["a_b_c"]);
+    case("foo", &[LangStringToken::LangToken("foo")]);
+    case("foo,bar", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]);
+    case(".foo,.bar", &[]);
+    case(
+        "{.foo,.bar}",
+        &[LangStringToken::ClassAttribute("foo"), LangStringToken::ClassAttribute("bar")],
+    );
+    case(
+        "  {.foo,.bar}  ",
+        &[LangStringToken::ClassAttribute("foo"), LangStringToken::ClassAttribute("bar")],
+    );
+    case("foo bar", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]);
+    case("foo\tbar", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]);
+    case("foo\t, bar", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]);
+    case(" foo , bar ", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]);
+    case(",,foo,,bar,,", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]);
+    case("foo=bar", &[]);
+    case("a-b-c", &[LangStringToken::LangToken("a-b-c")]);
+    case("a_b_c", &[LangStringToken::LangToken("a_b_c")]);
 }
 
 #[test]
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index bb1c186668c..97714afaa45 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -7,8 +7,10 @@ use std::sync::mpsc::{channel, Receiver};
 
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE};
+use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
+use rustc_span::def_id::DefId;
 use rustc_span::edition::Edition;
 use rustc_span::source_map::FileName;
 use rustc_span::{sym, Symbol};
@@ -22,13 +24,13 @@ use super::{
     sidebar::{sidebar_module_like, Sidebar},
     AllTypes, LinkFromSrc, StylePath,
 };
-use crate::clean::{self, types::ExternalLocation, ExternalCrate};
+use crate::clean::{self, types::ExternalLocation, ExternalCrate, TypeAliasItem};
 use crate::config::{ModuleSorting, RenderOptions};
 use crate::docfs::{DocFS, PathError};
 use crate::error::Error;
 use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
-use crate::formats::FormatRenderer;
+use crate::formats::{self, FormatRenderer};
 use crate::html::escape::Escape;
 use crate::html::format::{join_with_double_colon, Buffer};
 use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap};
@@ -105,8 +107,8 @@ pub(crate) struct SharedContext<'tcx> {
     pub(super) module_sorting: ModuleSorting,
     /// Additional CSS files to be added to the generated docs.
     pub(crate) style_files: Vec<StylePath>,
-    /// Suffix to be added on resource files (if suffix is "-v2" then "light.css" becomes
-    /// "light-v2.css").
+    /// Suffix to add on resource files (if suffix is "-v2" then "search-index.js" becomes
+    /// "search-index-v2.js").
     pub(crate) resource_suffix: String,
     /// Optional path string to be used to load static files on output pages. If not set, uses
     /// combinations of `../` to reach the documentation root.
@@ -147,6 +149,53 @@ impl SharedContext<'_> {
     pub(crate) fn edition(&self) -> Edition {
         self.tcx.sess.edition()
     }
+
+    /// Returns a list of impls on the given type, and, if it's a type alias,
+    /// other types that it aliases.
+    pub(crate) fn all_impls_for_item<'a>(
+        &'a self,
+        it: &clean::Item,
+        did: DefId,
+    ) -> Vec<&'a formats::Impl> {
+        let tcx = self.tcx;
+        let cache = &self.cache;
+        let mut saw_impls = FxHashSet::default();
+        let mut v: Vec<&formats::Impl> = cache
+            .impls
+            .get(&did)
+            .map(Vec::as_slice)
+            .unwrap_or(&[])
+            .iter()
+            .filter(|i| saw_impls.insert(i.def_id()))
+            .collect();
+        if let TypeAliasItem(ait) = &*it.kind &&
+            let aliased_clean_type = ait.item_type.as_ref().unwrap_or(&ait.type_) &&
+            let Some(aliased_type_defid) = aliased_clean_type.def_id(cache) &&
+            let Some(av) = cache.impls.get(&aliased_type_defid) &&
+            let Some(alias_def_id) = it.item_id.as_def_id()
+        {
+            // This branch of the compiler compares types structually, but does
+            // not check trait bounds. That's probably fine, since type aliases
+            // don't normally constrain on them anyway.
+            // https://github.com/rust-lang/rust/issues/21903
+            //
+            // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this to use type unification.
+            // Be aware of `tests/rustdoc/issue-112515-impl-ty-alias.rs` which might regress.
+            let aliased_ty = tcx.type_of(alias_def_id).skip_binder();
+            let reject_cx = DeepRejectCtxt {
+                treat_obligation_params: TreatParams::AsCandidateKey,
+            };
+            v.extend(av.iter().filter(|impl_| {
+                if let Some(impl_def_id) = impl_.impl_item.item_id.as_def_id() {
+                    reject_cx.types_may_unify(aliased_ty, tcx.type_of(impl_def_id).skip_binder())
+                        && saw_impls.insert(impl_def_id)
+                } else {
+                    false
+                }
+            }));
+        }
+        v
+    }
 }
 
 impl<'tcx> Context<'tcx> {
@@ -665,21 +714,9 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
                             You need to enable JavaScript be able to update your settings.\
                         </section>\
                      </noscript>\
-                     <link rel=\"stylesheet\" \
-                         href=\"{static_root_path}{settings_css}\">\
-                     <script defer src=\"{static_root_path}{settings_js}\"></script>\
-                     <link rel=\"preload\" href=\"{static_root_path}{theme_light_css}\" \
-                         as=\"style\">\
-                     <link rel=\"preload\" href=\"{static_root_path}{theme_dark_css}\" \
-                         as=\"style\">\
-                     <link rel=\"preload\" href=\"{static_root_path}{theme_ayu_css}\" \
-                         as=\"style\">",
+                     <script defer src=\"{static_root_path}{settings_js}\"></script>",
                     static_root_path = page.get_static_root_path(),
-                    settings_css = static_files::STATIC_FILES.settings_css,
                     settings_js = static_files::STATIC_FILES.settings_js,
-                    theme_light_css = static_files::STATIC_FILES.theme_light_css,
-                    theme_dark_css = static_files::STATIC_FILES.theme_dark_css,
-                    theme_ayu_css = static_files::STATIC_FILES.theme_ayu_css,
                 );
                 // Pre-load all theme CSS files, so that switching feels seamless.
                 //
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index aef8f1a74fb..5adbecd6d04 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -1117,13 +1117,13 @@ pub(crate) fn render_all_impls(
 fn render_assoc_items<'a, 'cx: 'a>(
     cx: &'a mut Context<'cx>,
     containing_item: &'a clean::Item,
-    it: DefId,
+    did: DefId,
     what: AssocItemRender<'a>,
 ) -> impl fmt::Display + 'a + Captures<'cx> {
     let mut derefs = DefIdSet::default();
-    derefs.insert(it);
+    derefs.insert(did);
     display_fn(move |f| {
-        render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs);
+        render_assoc_items_inner(f, cx, containing_item, did, what, &mut derefs);
         Ok(())
     })
 }
@@ -1132,15 +1132,17 @@ fn render_assoc_items_inner(
     mut w: &mut dyn fmt::Write,
     cx: &mut Context<'_>,
     containing_item: &clean::Item,
-    it: DefId,
+    did: DefId,
     what: AssocItemRender<'_>,
     derefs: &mut DefIdSet,
 ) {
     info!("Documenting associated items of {:?}", containing_item.name);
     let shared = Rc::clone(&cx.shared);
-    let cache = &shared.cache;
-    let Some(v) = cache.impls.get(&it) else { return };
-    let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
+    let v = shared.all_impls_for_item(containing_item, did);
+    let v = v.as_slice();
+    let (non_trait, traits): (Vec<&Impl>, _) =
+        v.iter().partition(|i| i.inner_impl().trait_.is_none());
+    let mut saw_impls = FxHashSet::default();
     if !non_trait.is_empty() {
         let mut tmp_buf = Buffer::html();
         let (render_mode, id, class_html) = match what {
@@ -1169,6 +1171,9 @@ fn render_assoc_items_inner(
         };
         let mut impls_buf = Buffer::html();
         for i in &non_trait {
+            if !saw_impls.insert(i.def_id()) {
+                continue;
+            }
             render_impl(
                 &mut impls_buf,
                 cx,
@@ -1214,8 +1219,10 @@ fn render_assoc_items_inner(
 
         let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
             traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
-        let (blanket_impl, concrete): (Vec<&Impl>, _) =
-            concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
+        let (blanket_impl, concrete): (Vec<&Impl>, _) = concrete
+            .into_iter()
+            .filter(|t| saw_impls.insert(t.def_id()))
+            .partition(|t| t.inner_impl().kind.is_blanket());
 
         render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
     }
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index cb78f903462..c6751c9585e 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -1237,6 +1237,75 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
 
     write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
 
+    if let Some(inner_type) = &t.inner_type {
+        write!(
+            w,
+            "<h2 id=\"aliased-type\" class=\"small-section-header\">\
+                Aliased Type<a href=\"#aliased-type\" class=\"anchor\">§</a></h2>"
+        );
+
+        match inner_type {
+            clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
+                let variants_iter = || variants.iter().filter(|i| !i.is_stripped());
+                wrap_item(w, |w| {
+                    let variants_len = variants.len();
+                    let variants_count = variants_iter().count();
+                    let has_stripped_entries = variants_len != variants_count;
+
+                    write!(w, "enum {}{}", it.name.unwrap(), t.generics.print(cx));
+                    render_enum_fields(
+                        w,
+                        cx,
+                        Some(&t.generics),
+                        variants_iter(),
+                        variants_count,
+                        has_stripped_entries,
+                        *is_non_exhaustive,
+                    )
+                });
+                item_variants(w, cx, it, variants_iter());
+            }
+            clean::TypeAliasInnerType::Union { fields } => {
+                wrap_item(w, |w| {
+                    let fields_count = fields.iter().filter(|i| !i.is_stripped()).count();
+                    let has_stripped_fields = fields.len() != fields_count;
+
+                    write!(w, "union {}{}", it.name.unwrap(), t.generics.print(cx));
+                    render_struct_fields(
+                        w,
+                        Some(&t.generics),
+                        None,
+                        fields,
+                        "",
+                        true,
+                        has_stripped_fields,
+                        cx,
+                    );
+                });
+                item_fields(w, cx, it, fields, None);
+            }
+            clean::TypeAliasInnerType::Struct { ctor_kind, fields } => {
+                wrap_item(w, |w| {
+                    let fields_count = fields.iter().filter(|i| !i.is_stripped()).count();
+                    let has_stripped_fields = fields.len() != fields_count;
+
+                    write!(w, "struct {}{}", it.name.unwrap(), t.generics.print(cx));
+                    render_struct_fields(
+                        w,
+                        Some(&t.generics),
+                        *ctor_kind,
+                        fields,
+                        "",
+                        true,
+                        has_stripped_fields,
+                        cx,
+                    );
+                });
+                item_fields(w, cx, it, fields, None);
+            }
+        }
+    }
+
     let def_id = it.item_id.expect_def_id();
     // Render any items associated directly to this alias, as otherwise they
     // won't be visible anywhere in the docs. It would be nice to also show
@@ -1315,6 +1384,12 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>(
     s: &'a [clean::Item],
 ) -> impl fmt::Display + 'a + Captures<'cx> {
     display_fn(|f| {
+        if s.iter()
+            .all(|field| matches!(*field.kind, clean::StrippedItem(box clean::StructFieldItem(..))))
+        {
+            return f.write_str("/* private fields */");
+        }
+
         for (i, ty) in s.iter().enumerate() {
             if i > 0 {
                 f.write_str(", ")?;
@@ -1332,7 +1407,7 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>(
 fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::Enum) {
     let tcx = cx.tcx();
     let count_variants = e.variants().count();
-    wrap_item(w, |mut w| {
+    wrap_item(w, |w| {
         render_attributes_in_code(w, it, tcx);
         write!(
             w,
@@ -1341,148 +1416,179 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
             it.name.unwrap(),
             e.generics.print(cx),
         );
-        if !print_where_clause_and_check(w, &e.generics, cx) {
-            // If there wasn't a `where` clause, we add a whitespace.
-            w.write_str(" ");
-        }
+        render_enum_fields(
+            w,
+            cx,
+            Some(&e.generics),
+            e.variants(),
+            count_variants,
+            e.has_stripped_entries(),
+            it.is_non_exhaustive(),
+        );
+    });
 
-        let variants_stripped = e.has_stripped_entries();
-        if count_variants == 0 && !variants_stripped {
-            w.write_str("{}");
-        } else {
-            w.write_str("{\n");
-            let toggle = should_hide_fields(count_variants);
-            if toggle {
-                toggle_open(&mut w, format_args!("{count_variants} variants"));
-            }
-            for v in e.variants() {
-                w.write_str("    ");
-                let name = v.name.unwrap();
-                match *v.kind {
-                    // FIXME(#101337): Show discriminant
-                    clean::VariantItem(ref var) => match var.kind {
-                        clean::VariantKind::CLike => w.write_str(name.as_str()),
-                        clean::VariantKind::Tuple(ref s) => {
-                            write!(w, "{name}({})", print_tuple_struct_fields(cx, s),);
-                        }
-                        clean::VariantKind::Struct(ref s) => {
-                            render_struct(w, v, None, None, &s.fields, "    ", false, cx);
-                        }
-                    },
-                    _ => unreachable!(),
-                }
-                w.write_str(",\n");
-            }
+    write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
 
-            if variants_stripped && !it.is_non_exhaustive() {
-                w.write_str("    // some variants omitted\n");
-            }
-            if toggle {
-                toggle_close(&mut w);
+    if count_variants != 0 {
+        item_variants(w, cx, it, e.variants());
+    }
+    let def_id = it.item_id.expect_def_id();
+    write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
+    write!(w, "{}", document_type_layout(cx, def_id));
+}
+
+fn render_enum_fields<'a>(
+    mut w: &mut Buffer,
+    cx: &mut Context<'_>,
+    g: Option<&clean::Generics>,
+    variants: impl Iterator<Item = &'a clean::Item>,
+    count_variants: usize,
+    has_stripped_entries: bool,
+    is_non_exhaustive: bool,
+) {
+    if !g.is_some_and(|g| print_where_clause_and_check(w, g, cx)) {
+        // If there wasn't a `where` clause, we add a whitespace.
+        w.write_str(" ");
+    }
+
+    let variants_stripped = has_stripped_entries;
+    if count_variants == 0 && !variants_stripped {
+        w.write_str("{}");
+    } else {
+        w.write_str("{\n");
+        let toggle = should_hide_fields(count_variants);
+        if toggle {
+            toggle_open(&mut w, format_args!("{count_variants} variants"));
+        }
+        const TAB: &str = "    ";
+        for v in variants {
+            w.write_str(TAB);
+            let name = v.name.unwrap();
+            match *v.kind {
+                // FIXME(#101337): Show discriminant
+                clean::VariantItem(ref var) => match var.kind {
+                    clean::VariantKind::CLike => w.write_str(name.as_str()),
+                    clean::VariantKind::Tuple(ref s) => {
+                        write!(w, "{name}({})", print_tuple_struct_fields(cx, s),);
+                    }
+                    clean::VariantKind::Struct(ref s) => {
+                        render_struct(w, v, None, None, &s.fields, TAB, false, cx);
+                    }
+                },
+                _ => unreachable!(),
             }
-            w.write_str("}");
+            w.write_str(",\n");
         }
-    });
 
-    write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
+        if variants_stripped && !is_non_exhaustive {
+            w.write_str("    // some variants omitted\n");
+        }
+        if toggle {
+            toggle_close(&mut w);
+        }
+        w.write_str("}");
+    }
+}
 
-    if count_variants != 0 {
+fn item_variants<'a>(
+    w: &mut Buffer,
+    cx: &mut Context<'_>,
+    it: &clean::Item,
+    variants: impl Iterator<Item = &'a clean::Item>,
+) {
+    let tcx = cx.tcx();
+    write!(
+        w,
+        "<h2 id=\"variants\" class=\"variants small-section-header\">\
+            Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\
+        </h2>\
+        {}\
+        <div class=\"variants\">",
+        document_non_exhaustive_header(it),
+        document_non_exhaustive(it)
+    );
+    for variant in variants {
+        let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
         write!(
             w,
-            "<h2 id=\"variants\" class=\"variants small-section-header\">\
-                Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\
-            </h2>\
-            {}\
-            <div class=\"variants\">",
-            document_non_exhaustive_header(it),
-            document_non_exhaustive(it)
+            "<section id=\"{id}\" class=\"variant\">\
+                <a href=\"#{id}\" class=\"anchor\">§</a>",
         );
-        for variant in e.variants() {
-            let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
-            write!(
-                w,
-                "<section id=\"{id}\" class=\"variant\">\
-                    <a href=\"#{id}\" class=\"anchor\">§</a>",
-            );
-            render_stability_since_raw_with_extra(
-                w,
-                variant.stable_since(tcx),
-                variant.const_stability(tcx),
-                it.stable_since(tcx),
-                it.const_stable_since(tcx),
-                " rightside",
-            );
-            write!(w, "<h3 class=\"code-header\">{name}", name = variant.name.unwrap());
+        render_stability_since_raw_with_extra(
+            w,
+            variant.stable_since(tcx),
+            variant.const_stability(tcx),
+            it.stable_since(tcx),
+            it.const_stable_since(tcx),
+            " rightside",
+        );
+        write!(w, "<h3 class=\"code-header\">{name}", name = variant.name.unwrap());
 
-            let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() };
+        let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() };
 
-            if let clean::VariantKind::Tuple(ref s) = variant_data.kind {
-                write!(w, "({})", print_tuple_struct_fields(cx, s));
-            }
-            w.write_str("</h3></section>");
-
-            let heading_and_fields = match &variant_data.kind {
-                clean::VariantKind::Struct(s) => Some(("Fields", &s.fields)),
-                clean::VariantKind::Tuple(fields) => {
-                    // Documentation on tuple variant fields is rare, so to reduce noise we only emit
-                    // the section if at least one field is documented.
-                    if fields.iter().any(|f| !f.doc_value().is_empty()) {
-                        Some(("Tuple Fields", fields))
-                    } else {
-                        None
-                    }
+        if let clean::VariantKind::Tuple(ref s) = variant_data.kind {
+            write!(w, "({})", print_tuple_struct_fields(cx, s));
+        }
+        w.write_str("</h3></section>");
+
+        let heading_and_fields = match &variant_data.kind {
+            clean::VariantKind::Struct(s) => Some(("Fields", &s.fields)),
+            clean::VariantKind::Tuple(fields) => {
+                // Documentation on tuple variant fields is rare, so to reduce noise we only emit
+                // the section if at least one field is documented.
+                if fields.iter().any(|f| !f.doc_value().is_empty()) {
+                    Some(("Tuple Fields", fields))
+                } else {
+                    None
                 }
-                clean::VariantKind::CLike => None,
-            };
+            }
+            clean::VariantKind::CLike => None,
+        };
 
-            if let Some((heading, fields)) = heading_and_fields {
-                let variant_id =
-                    cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap()));
-                write!(
-                    w,
-                    "<div class=\"sub-variant\" id=\"{variant_id}\">\
-                        <h4>{heading}</h4>\
-                        {}",
-                    document_non_exhaustive(variant)
-                );
-                for field in fields {
-                    match *field.kind {
-                        clean::StrippedItem(box clean::StructFieldItem(_)) => {}
-                        clean::StructFieldItem(ref ty) => {
-                            let id = cx.derive_id(format!(
-                                "variant.{}.field.{}",
-                                variant.name.unwrap(),
-                                field.name.unwrap()
-                            ));
-                            write!(
-                                w,
-                                "<div class=\"sub-variant-field\">\
+        if let Some((heading, fields)) = heading_and_fields {
+            let variant_id =
+                cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap()));
+            write!(
+                w,
+                "<div class=\"sub-variant\" id=\"{variant_id}\">\
+                    <h4>{heading}</h4>\
+                    {}",
+                document_non_exhaustive(variant)
+            );
+            for field in fields {
+                match *field.kind {
+                    clean::StrippedItem(box clean::StructFieldItem(_)) => {}
+                    clean::StructFieldItem(ref ty) => {
+                        let id = cx.derive_id(format!(
+                            "variant.{}.field.{}",
+                            variant.name.unwrap(),
+                            field.name.unwrap()
+                        ));
+                        write!(
+                            w,
+                            "<div class=\"sub-variant-field\">\
                                  <span id=\"{id}\" class=\"small-section-header\">\
                                      <a href=\"#{id}\" class=\"anchor field\">§</a>\
                                      <code>{f}: {t}</code>\
                                  </span>",
-                                f = field.name.unwrap(),
-                                t = ty.print(cx),
-                            );
-                            write!(
-                                w,
-                                "{}</div>",
-                                document(cx, field, Some(variant), HeadingOffset::H5)
-                            );
-                        }
-                        _ => unreachable!(),
+                            f = field.name.unwrap(),
+                            t = ty.print(cx),
+                        );
+                        write!(
+                            w,
+                            "{}</div>",
+                            document(cx, field, Some(variant), HeadingOffset::H5)
+                        );
                     }
+                    _ => unreachable!(),
                 }
-                w.write_str("</div>");
             }
-
-            write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4));
+            w.write_str("</div>");
         }
-        write!(w, "</div>");
+
+        write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4));
     }
-    let def_id = it.item_id.expect_def_id();
-    write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
-    write!(w, "{}", document_type_layout(cx, def_id));
+    write!(w, "</div>");
 }
 
 fn item_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Macro) {
@@ -1593,15 +1699,28 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
 
     write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
 
-    let mut fields = s
-        .fields
+    item_fields(w, cx, it, &s.fields, s.ctor_kind);
+
+    let def_id = it.item_id.expect_def_id();
+    write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
+    write!(w, "{}", document_type_layout(cx, def_id));
+}
+
+fn item_fields(
+    w: &mut Buffer,
+    cx: &mut Context<'_>,
+    it: &clean::Item,
+    fields: &Vec<clean::Item>,
+    ctor_kind: Option<CtorKind>,
+) {
+    let mut fields = fields
         .iter()
         .filter_map(|f| match *f.kind {
             clean::StructFieldItem(ref ty) => Some((f, ty)),
             _ => None,
         })
         .peekable();
-    if let None | Some(CtorKind::Fn) = s.ctor_kind {
+    if let None | Some(CtorKind::Fn) = ctor_kind {
         if fields.peek().is_some() {
             write!(
                 w,
@@ -1609,7 +1728,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
                      {}{}<a href=\"#fields\" class=\"anchor\">§</a>\
                  </h2>\
                  {}",
-                if s.ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
+                if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
                 document_non_exhaustive_header(it),
                 document_non_exhaustive(it)
             );
@@ -1630,9 +1749,6 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
             }
         }
     }
-    let def_id = it.item_id.expect_def_id();
-    write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
-    write!(w, "{}", document_type_layout(cx, def_id));
 }
 
 fn item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) {
@@ -1871,7 +1987,7 @@ fn render_union<'a, 'cx: 'a>(
 }
 
 fn render_struct(
-    mut w: &mut Buffer,
+    w: &mut Buffer,
     it: &clean::Item,
     g: Option<&clean::Generics>,
     ty: Option<CtorKind>,
@@ -1891,6 +2007,29 @@ fn render_struct(
     if let Some(g) = g {
         write!(w, "{}", g.print(cx))
     }
+    render_struct_fields(
+        w,
+        g,
+        ty,
+        fields,
+        tab,
+        structhead,
+        it.has_stripped_entries().unwrap_or(false),
+        cx,
+    )
+}
+
+fn render_struct_fields(
+    mut w: &mut Buffer,
+    g: Option<&clean::Generics>,
+    ty: Option<CtorKind>,
+    fields: &[clean::Item],
+    tab: &str,
+    structhead: bool,
+    has_stripped_entries: bool,
+    cx: &Context<'_>,
+) {
+    let tcx = cx.tcx();
     match ty {
         None => {
             let where_displayed =
@@ -1922,11 +2061,11 @@ fn render_struct(
             }
 
             if has_visible_fields {
-                if it.has_stripped_entries().unwrap() {
+                if has_stripped_entries {
                     write!(w, "\n{tab}    /* private fields */");
                 }
                 write!(w, "\n{tab}");
-            } else if it.has_stripped_entries().unwrap() {
+            } else if has_stripped_entries {
                 write!(w, " /* private fields */ ");
             }
             if toggle {
@@ -1936,21 +2075,31 @@ fn render_struct(
         }
         Some(CtorKind::Fn) => {
             w.write_str("(");
-            for (i, field) in fields.iter().enumerate() {
-                if i > 0 {
-                    w.write_str(", ");
-                }
-                match *field.kind {
-                    clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"),
-                    clean::StructFieldItem(ref ty) => {
-                        write!(
-                            w,
-                            "{}{}",
-                            visibility_print_with_space(field.visibility(tcx), field.item_id, cx),
-                            ty.print(cx),
-                        )
+            if fields.iter().all(|field| {
+                matches!(*field.kind, clean::StrippedItem(box clean::StructFieldItem(..)))
+            }) {
+                write!(w, "/* private fields */");
+            } else {
+                for (i, field) in fields.iter().enumerate() {
+                    if i > 0 {
+                        w.write_str(", ");
+                    }
+                    match *field.kind {
+                        clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"),
+                        clean::StructFieldItem(ref ty) => {
+                            write!(
+                                w,
+                                "{}{}",
+                                visibility_print_with_space(
+                                    field.visibility(tcx),
+                                    field.item_id,
+                                    cx
+                                ),
+                                ty.print(cx),
+                            )
+                        }
+                        _ => unreachable!(),
                     }
-                    _ => unreachable!(),
                 }
             }
             w.write_str(")");
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
index b96b1536156..76f63c6f63e 100644
--- a/src/librustdoc/html/render/sidebar.rs
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -82,7 +82,7 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf
         clean::PrimitiveItem(_) => sidebar_primitive(cx, it),
         clean::UnionItem(ref u) => sidebar_union(cx, it, u),
         clean::EnumItem(ref e) => sidebar_enum(cx, it, e),
-        clean::TypeAliasItem(_) => sidebar_type_alias(cx, it),
+        clean::TypeAliasItem(ref t) => sidebar_type_alias(cx, it, t),
         clean::ModuleItem(ref m) => vec![sidebar_module(&m.items)],
         clean::ForeignTypeItem => sidebar_foreign_type(cx, it),
         _ => vec![],
@@ -230,8 +230,33 @@ fn sidebar_primitive<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBl
     }
 }
 
-fn sidebar_type_alias<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> {
+fn sidebar_type_alias<'a>(
+    cx: &'a Context<'_>,
+    it: &'a clean::Item,
+    t: &'a clean::TypeAlias,
+) -> Vec<LinkBlock<'a>> {
     let mut items = vec![];
+    if let Some(inner_type) = &t.inner_type {
+        items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased type")));
+        match inner_type {
+            clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive: _ } => {
+                let mut variants = variants
+                    .iter()
+                    .filter(|i| !i.is_stripped())
+                    .filter_map(|v| v.name)
+                    .map(|name| Link::new(format!("variant.{name}"), name.to_string()))
+                    .collect::<Vec<_>>();
+                variants.sort_unstable();
+
+                items.push(LinkBlock::new(Link::new("variants", "Variants"), variants));
+            }
+            clean::TypeAliasInnerType::Union { fields }
+            | clean::TypeAliasInnerType::Struct { ctor_kind: _, fields } => {
+                let fields = get_struct_fields_name(fields);
+                items.push(LinkBlock::new(Link::new("fields", "Fields"), fields));
+            }
+        }
+    }
     sidebar_assoc_items(cx, it, &mut items);
     items
 }
@@ -254,11 +279,12 @@ fn sidebar_assoc_items<'a>(
     links: &mut Vec<LinkBlock<'a>>,
 ) {
     let did = it.item_id.expect_def_id();
-    let cache = cx.cache();
+    let v = cx.shared.all_impls_for_item(it, it.item_id.expect_def_id());
+    let v = v.as_slice();
 
     let mut assoc_consts = Vec::new();
     let mut methods = Vec::new();
-    if let Some(v) = cache.impls.get(&did) {
+    if !v.is_empty() {
         let mut used_links = FxHashSet::default();
         let mut id_map = IdMap::new();
 
@@ -294,7 +320,7 @@ fn sidebar_assoc_items<'a>(
                     cx,
                     &mut deref_methods,
                     impl_,
-                    v,
+                    v.iter().copied(),
                     &mut derefs,
                     &mut used_links,
                 );
@@ -324,7 +350,7 @@ fn sidebar_deref_methods<'a>(
     cx: &'a Context<'_>,
     out: &mut Vec<LinkBlock<'a>>,
     impl_: &Impl,
-    v: &[Impl],
+    v: impl Iterator<Item = &'a Impl>,
     derefs: &mut DefIdSet,
     used_links: &mut FxHashSet<String>,
 ) {
@@ -349,7 +375,7 @@ fn sidebar_deref_methods<'a>(
             // Avoid infinite cycles
             return;
         }
-        let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
+        let deref_mut = { v }.any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
         let inner_impl = target
             .def_id(c)
             .or_else(|| {
@@ -400,7 +426,7 @@ fn sidebar_deref_methods<'a>(
                 cx,
                 out,
                 target_deref_impl,
-                target_impls,
+                target_impls.iter(),
                 derefs,
                 used_links,
             );
diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css
index 93aa11a5852..fe0cf6dc8cc 100644
--- a/src/librustdoc/html/static/css/noscript.css
+++ b/src/librustdoc/html/static/css/noscript.css
@@ -28,3 +28,216 @@ nav.sub {
 	    https://github.com/rust-lang/rust/issues/102576 */
 	display: none;
 }
+
+/* Begin: styles for themes
+	Keep the default light and dark themes synchronized with the ones
+	in rustdoc.css */
+
+/* Begin theme: light */
+:root {
+	--main-background-color: white;
+	--main-color: black;
+	--settings-input-color: #2196f3;
+	--settings-input-border-color: #717171;
+	--settings-button-color: #000;
+	--settings-button-border-focus: #717171;
+	--sidebar-background-color: #f5f5f5;
+	--sidebar-background-color-hover: #e0e0e0;
+	--code-block-background-color: #f5f5f5;
+	--scrollbar-track-background-color: #dcdcdc;
+	--scrollbar-thumb-background-color: rgba(36, 37, 39, 0.6);
+	--scrollbar-color: rgba(36, 37, 39, 0.6) #d9d9d9;
+	--headings-border-bottom-color: #ddd;
+	--border-color: #e0e0e0;
+	--button-background-color: #fff;
+	--right-side-color: grey;
+	--code-attribute-color: #999;
+	--toggles-color: #999;
+	--toggle-filter: none;
+	--search-input-focused-border-color: #66afe9;
+	--copy-path-button-color: #999;
+	--copy-path-img-filter: invert(50%);
+	--copy-path-img-hover-filter: invert(35%);
+	--codeblock-error-hover-color: rgb(255, 0, 0);
+	--codeblock-error-color: rgba(255, 0, 0, .5);
+	--codeblock-ignore-hover-color: rgb(255, 142, 0);
+	--codeblock-ignore-color: rgba(255, 142, 0, .6);
+	--warning-border-color: #ff8e00;
+	--type-link-color: #ad378a;
+	--trait-link-color: #6e4fc9;
+	--assoc-item-link-color: #3873ad;
+	--function-link-color: #ad7c37;
+	--macro-link-color: #068000;
+	--keyword-link-color: #3873ad;
+	--mod-link-color: #3873ad;
+	--link-color: #3873ad;
+	--sidebar-link-color: #356da4;
+	--sidebar-current-link-background-color: #fff;
+	--search-result-link-focus-background-color: #ccc;
+	--search-result-border-color: #aaa3;
+	--search-color: #000;
+	--search-error-code-background-color: #d0cccc;
+	--search-results-alias-color: #000;
+	--search-results-grey-color: #999;
+	--search-tab-title-count-color: #888;
+	--search-tab-button-not-selected-border-top-color: #e6e6e6;
+	--search-tab-button-not-selected-background: #e6e6e6;
+	--search-tab-button-selected-border-top-color: #0089ff;
+	--search-tab-button-selected-background: #fff;
+	--stab-background-color: #fff5d6;
+	--stab-code-color: #000;
+	--code-highlight-kw-color: #8959a8;
+	--code-highlight-kw-2-color: #4271ae;
+	--code-highlight-lifetime-color: #b76514;
+	--code-highlight-prelude-color: #4271ae;
+	--code-highlight-prelude-val-color: #c82829;
+	--code-highlight-number-color: #718c00;
+	--code-highlight-string-color: #718c00;
+	--code-highlight-literal-color: #c82829;
+	--code-highlight-attribute-color: #c82829;
+	--code-highlight-self-color: #c82829;
+	--code-highlight-macro-color: #3e999f;
+	--code-highlight-question-mark-color: #ff9011;
+	--code-highlight-comment-color: #8e908c;
+	--code-highlight-doc-comment-color: #4d4d4c;
+	--src-line-numbers-span-color: #c67e2d;
+	--src-line-number-highlighted-background-color: #fdffd3;
+	--test-arrow-color: #f5f5f5;
+	--test-arrow-background-color: rgba(78, 139, 202, 0.2);
+	--test-arrow-hover-color: #f5f5f5;
+	--test-arrow-hover-background-color: rgb(78, 139, 202);
+	--target-background-color: #fdffd3;
+	--target-border-color: #ad7c37;
+	--kbd-color: #000;
+	--kbd-background: #fafbfc;
+	--kbd-box-shadow-color: #c6cbd1;
+	--rust-logo-filter: initial;
+	/* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
+	--crate-search-div-filter: invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg)
+		brightness(114%) contrast(76%);
+	--crate-search-div-hover-filter: invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg)
+		brightness(96%) contrast(93%);
+	--crate-search-hover-border: #717171;
+	--src-sidebar-background-selected: #fff;
+	--src-sidebar-background-hover: #e0e0e0;
+	--table-alt-row-background-color: #f5f5f5;
+	--codeblock-link-background: #eee;
+	--scrape-example-toggle-line-background: #ccc;
+	--scrape-example-toggle-line-hover-background: #999;
+	--scrape-example-code-line-highlight: #fcffd6;
+	--scrape-example-code-line-highlight-focus: #f6fdb0;
+	--scrape-example-help-border-color: #555;
+	--scrape-example-help-color: #333;
+	--scrape-example-help-hover-border-color: #000;
+	--scrape-example-help-hover-color: #000;
+	--scrape-example-code-wrapper-background-start: rgba(255, 255, 255, 1);
+	--scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0);
+}
+/* End theme: light */
+
+@media (prefers-color-scheme: dark) {
+	/* Begin theme: dark */
+	:root {
+		--main-background-color: #353535;
+		--main-color: #ddd;
+		--settings-input-color: #2196f3;
+		--settings-input-border-color: #999;
+		--settings-button-color: #000;
+		--settings-button-border-focus: #ffb900;
+		--sidebar-background-color: #505050;
+		--sidebar-background-color-hover: #676767;
+		--code-block-background-color: #2A2A2A;
+		--scrollbar-track-background-color: #717171;
+		--scrollbar-thumb-background-color: rgba(32, 34, 37, .6);
+		--scrollbar-color: rgba(32,34,37,.6) #5a5a5a;
+		--headings-border-bottom-color: #d2d2d2;
+		--border-color: #e0e0e0;
+		--button-background-color: #f0f0f0;
+		--right-side-color: grey;
+		--code-attribute-color: #999;
+		--toggles-color: #999;
+		--toggle-filter: invert(100%);
+		--search-input-focused-border-color: #008dfd;
+		--copy-path-button-color: #999;
+		--copy-path-img-filter: invert(50%);
+		--copy-path-img-hover-filter: invert(65%);
+		--codeblock-error-hover-color: rgb(255, 0, 0);
+		--codeblock-error-color: rgba(255, 0, 0, .5);
+		--codeblock-ignore-hover-color: rgb(255, 142, 0);
+		--codeblock-ignore-color: rgba(255, 142, 0, .6);
+		--warning-border-color: #ff8e00;
+		--type-link-color: #2dbfb8;
+		--trait-link-color: #b78cf2;
+		--assoc-item-link-color: #d2991d;
+		--function-link-color: #2bab63;
+		--macro-link-color: #09bd00;
+		--keyword-link-color: #d2991d;
+		--mod-link-color:  #d2991d;
+		--link-color: #d2991d;
+		--sidebar-link-color: #fdbf35;
+		--sidebar-current-link-background-color: #444;
+		--search-result-link-focus-background-color: #616161;
+		--search-result-border-color: #aaa3;
+		--search-color: #111;
+		--search-error-code-background-color: #484848;
+		--search-results-alias-color: #fff;
+		--search-results-grey-color: #ccc;
+		--search-tab-title-count-color: #888;
+		--search-tab-button-not-selected-border-top-color: #252525;
+		--search-tab-button-not-selected-background: #252525;
+		--search-tab-button-selected-border-top-color: #0089ff;
+		--search-tab-button-selected-background: #353535;
+		--stab-background-color: #314559;
+		--stab-code-color: #e6e1cf;
+		--code-highlight-kw-color: #ab8ac1;
+		--code-highlight-kw-2-color: #769acb;
+		--code-highlight-lifetime-color: #d97f26;
+		--code-highlight-prelude-color: #769acb;
+		--code-highlight-prelude-val-color: #ee6868;
+		--code-highlight-number-color: #83a300;
+		--code-highlight-string-color: #83a300;
+		--code-highlight-literal-color: #ee6868;
+		--code-highlight-attribute-color: #ee6868;
+		--code-highlight-self-color: #ee6868;
+		--code-highlight-macro-color: #3e999f;
+		--code-highlight-question-mark-color: #ff9011;
+		--code-highlight-comment-color: #8d8d8b;
+		--code-highlight-doc-comment-color: #8ca375;
+		--src-line-numbers-span-color: #3b91e2;
+		--src-line-number-highlighted-background-color: #0a042f;
+		--test-arrow-color: #dedede;
+		--test-arrow-background-color: rgba(78, 139, 202, 0.2);
+		--test-arrow-hover-color: #dedede;
+		--test-arrow-hover-background-color: #4e8bca;
+		--target-background-color: #494a3d;
+		--target-border-color: #bb7410;
+		--kbd-color: #000;
+		--kbd-background: #fafbfc;
+		--kbd-box-shadow-color: #c6cbd1;
+		--rust-logo-filter: drop-shadow(1px 0 0px #fff)
+			drop-shadow(0 1px 0 #fff)
+			drop-shadow(-1px 0 0 #fff)
+			drop-shadow(0 -1px 0 #fff);
+		/* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
+		--crate-search-div-filter: invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg)
+			brightness(90%) contrast(90%);
+		--crate-search-div-hover-filter: invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg)
+			brightness(100%) contrast(91%);
+		--crate-search-hover-border: #2196f3;
+		--src-sidebar-background-selected: #333;
+		--src-sidebar-background-hover: #444;
+		--table-alt-row-background-color: #2a2a2a;
+		--codeblock-link-background: #333;
+		--scrape-example-toggle-line-background: #999;
+		--scrape-example-toggle-line-hover-background: #c5c5c5;
+		--scrape-example-code-line-highlight: #5b3b01;
+		--scrape-example-code-line-highlight-focus: #7c4b0f;
+		--scrape-example-help-border-color: #aaa;
+		--scrape-example-help-color: #eee;
+		--scrape-example-help-hover-border-color: #fff;
+		--scrape-example-help-hover-color: #fff;
+		--scrape-example-code-wrapper-background-start: rgba(53, 53, 53, 1);
+		--scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0);
+	}
+/* End theme: dark */
+}
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index da4da50106a..3b236641337 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -925,6 +925,70 @@ so that we can apply CSS-filters to change the arrow color in themes */
 	top: -5px;
 }
 
+.setting-line {
+	margin: 1.2em 0.6em;
+}
+
+.setting-radio input, .setting-check input {
+	margin-right: 0.3em;
+	height: 1.2rem;
+	width: 1.2rem;
+	border: 2px solid var(--settings-input-border-color);
+	outline: none;
+	-webkit-appearance: none;
+	cursor: pointer;
+}
+.setting-radio input {
+	border-radius: 50%;
+}
+
+.setting-radio span, .setting-check span {
+	padding-bottom: 1px;
+}
+
+.setting-radio {
+	margin-top: 0.1em;
+	margin-bottom: 0.1em;
+	min-width: 3.8em;
+	padding: 0.3em;
+	display: inline-flex;
+	align-items: center;
+	cursor: pointer;
+}
+.setting-radio + .setting-radio {
+	margin-left: 0.5em;
+}
+
+.setting-check {
+	margin-right: 20px;
+	display: flex;
+	align-items: center;
+	cursor: pointer;
+}
+
+.setting-radio input:checked {
+	box-shadow: inset 0 0 0 3px var(--main-background-color);
+	background-color: var(--settings-input-color);
+}
+.setting-check input:checked {
+	background-color: var(--settings-input-color);
+	border-width: 1px;
+	content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">\
+		<path d="M7,25L17,32L33,12" fill="none" stroke="black" stroke-width="5"/>\
+		<path d="M7,23L17,30L33,10" fill="none" stroke="white" stroke-width="5"/></svg>');
+}
+.setting-radio input:focus, .setting-check input:focus {
+	box-shadow: 0 0 1px 1px var(--settings-input-color);
+}
+/* In here we combine both `:focus` and `:checked` properties. */
+.setting-radio input:checked:focus {
+	box-shadow: inset 0 0 0 3px var(--main-background-color),
+		0 0 2px 2px var(--settings-input-color);
+}
+.setting-radio input:hover, .setting-check input:hover {
+	border-color: var(--settings-input-color) !important;
+}
+
 /* use larger max-width for help popover, but not for help.html */
 #help.popover {
 	max-width: 600px;
@@ -2034,3 +2098,413 @@ in src-script.js
 }
 
 /* End: styles for --scrape-examples feature */
+
+/* Begin: styles for themes
+
+	Keep the default light and dark themes synchronized with the ones
+	in noscript.css
+
+	The special "Begin theme" and "End theme" below are used by a lot of
+	tooling to ensure different themes all define all the variables. Do not
+	alter their formatting. */
+
+/* Begin theme: light */
+:root[data-theme="light"] {
+	--main-background-color: white;
+	--main-color: black;
+	--settings-input-color: #2196f3;
+	--settings-input-border-color: #717171;
+	--settings-button-color: #000;
+	--settings-button-border-focus: #717171;
+	--sidebar-background-color: #f5f5f5;
+	--sidebar-background-color-hover: #e0e0e0;
+	--code-block-background-color: #f5f5f5;
+	--scrollbar-track-background-color: #dcdcdc;
+	--scrollbar-thumb-background-color: rgba(36, 37, 39, 0.6);
+	--scrollbar-color: rgba(36, 37, 39, 0.6) #d9d9d9;
+	--headings-border-bottom-color: #ddd;
+	--border-color: #e0e0e0;
+	--button-background-color: #fff;
+	--right-side-color: grey;
+	--code-attribute-color: #999;
+	--toggles-color: #999;
+	--toggle-filter: none;
+	--search-input-focused-border-color: #66afe9;
+	--copy-path-button-color: #999;
+	--copy-path-img-filter: invert(50%);
+	--copy-path-img-hover-filter: invert(35%);
+	--codeblock-error-hover-color: rgb(255, 0, 0);
+	--codeblock-error-color: rgba(255, 0, 0, .5);
+	--codeblock-ignore-hover-color: rgb(255, 142, 0);
+	--codeblock-ignore-color: rgba(255, 142, 0, .6);
+	--warning-border-color: #ff8e00;
+	--type-link-color: #ad378a;
+	--trait-link-color: #6e4fc9;
+	--assoc-item-link-color: #3873ad;
+	--function-link-color: #ad7c37;
+	--macro-link-color: #068000;
+	--keyword-link-color: #3873ad;
+	--mod-link-color: #3873ad;
+	--link-color: #3873ad;
+	--sidebar-link-color: #356da4;
+	--sidebar-current-link-background-color: #fff;
+	--search-result-link-focus-background-color: #ccc;
+	--search-result-border-color: #aaa3;
+	--search-color: #000;
+	--search-error-code-background-color: #d0cccc;
+	--search-results-alias-color: #000;
+	--search-results-grey-color: #999;
+	--search-tab-title-count-color: #888;
+	--search-tab-button-not-selected-border-top-color: #e6e6e6;
+	--search-tab-button-not-selected-background: #e6e6e6;
+	--search-tab-button-selected-border-top-color: #0089ff;
+	--search-tab-button-selected-background: #fff;
+	--stab-background-color: #fff5d6;
+	--stab-code-color: #000;
+	--code-highlight-kw-color: #8959a8;
+	--code-highlight-kw-2-color: #4271ae;
+	--code-highlight-lifetime-color: #b76514;
+	--code-highlight-prelude-color: #4271ae;
+	--code-highlight-prelude-val-color: #c82829;
+	--code-highlight-number-color: #718c00;
+	--code-highlight-string-color: #718c00;
+	--code-highlight-literal-color: #c82829;
+	--code-highlight-attribute-color: #c82829;
+	--code-highlight-self-color: #c82829;
+	--code-highlight-macro-color: #3e999f;
+	--code-highlight-question-mark-color: #ff9011;
+	--code-highlight-comment-color: #8e908c;
+	--code-highlight-doc-comment-color: #4d4d4c;
+	--src-line-numbers-span-color: #c67e2d;
+	--src-line-number-highlighted-background-color: #fdffd3;
+	--test-arrow-color: #f5f5f5;
+	--test-arrow-background-color: rgba(78, 139, 202, 0.2);
+	--test-arrow-hover-color: #f5f5f5;
+	--test-arrow-hover-background-color: rgb(78, 139, 202);
+	--target-background-color: #fdffd3;
+	--target-border-color: #ad7c37;
+	--kbd-color: #000;
+	--kbd-background: #fafbfc;
+	--kbd-box-shadow-color: #c6cbd1;
+	--rust-logo-filter: initial;
+	/* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
+	--crate-search-div-filter: invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg)
+		brightness(114%) contrast(76%);
+	--crate-search-div-hover-filter: invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg)
+		brightness(96%) contrast(93%);
+	--crate-search-hover-border: #717171;
+	--src-sidebar-background-selected: #fff;
+	--src-sidebar-background-hover: #e0e0e0;
+	--table-alt-row-background-color: #f5f5f5;
+	--codeblock-link-background: #eee;
+	--scrape-example-toggle-line-background: #ccc;
+	--scrape-example-toggle-line-hover-background: #999;
+	--scrape-example-code-line-highlight: #fcffd6;
+	--scrape-example-code-line-highlight-focus: #f6fdb0;
+	--scrape-example-help-border-color: #555;
+	--scrape-example-help-color: #333;
+	--scrape-example-help-hover-border-color: #000;
+	--scrape-example-help-hover-color: #000;
+	--scrape-example-code-wrapper-background-start: rgba(255, 255, 255, 1);
+	--scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0);
+}
+/* End theme: light */
+
+/* Begin theme: dark */
+:root[data-theme="dark"] {
+	--main-background-color: #353535;
+	--main-color: #ddd;
+	--settings-input-color: #2196f3;
+	--settings-input-border-color: #999;
+	--settings-button-color: #000;
+	--settings-button-border-focus: #ffb900;
+	--sidebar-background-color: #505050;
+	--sidebar-background-color-hover: #676767;
+	--code-block-background-color: #2A2A2A;
+	--scrollbar-track-background-color: #717171;
+	--scrollbar-thumb-background-color: rgba(32, 34, 37, .6);
+	--scrollbar-color: rgba(32,34,37,.6) #5a5a5a;
+	--headings-border-bottom-color: #d2d2d2;
+	--border-color: #e0e0e0;
+	--button-background-color: #f0f0f0;
+	--right-side-color: grey;
+	--code-attribute-color: #999;
+	--toggles-color: #999;
+	--toggle-filter: invert(100%);
+	--search-input-focused-border-color: #008dfd;
+	--copy-path-button-color: #999;
+	--copy-path-img-filter: invert(50%);
+	--copy-path-img-hover-filter: invert(65%);
+	--codeblock-error-hover-color: rgb(255, 0, 0);
+	--codeblock-error-color: rgba(255, 0, 0, .5);
+	--codeblock-ignore-hover-color: rgb(255, 142, 0);
+	--codeblock-ignore-color: rgba(255, 142, 0, .6);
+	--warning-border-color: #ff8e00;
+	--type-link-color: #2dbfb8;
+	--trait-link-color: #b78cf2;
+	--assoc-item-link-color: #d2991d;
+	--function-link-color: #2bab63;
+	--macro-link-color: #09bd00;
+	--keyword-link-color: #d2991d;
+	--mod-link-color:  #d2991d;
+	--link-color: #d2991d;
+	--sidebar-link-color: #fdbf35;
+	--sidebar-current-link-background-color: #444;
+	--search-result-link-focus-background-color: #616161;
+	--search-result-border-color: #aaa3;
+	--search-color: #111;
+	--search-error-code-background-color: #484848;
+	--search-results-alias-color: #fff;
+	--search-results-grey-color: #ccc;
+	--search-tab-title-count-color: #888;
+	--search-tab-button-not-selected-border-top-color: #252525;
+	--search-tab-button-not-selected-background: #252525;
+	--search-tab-button-selected-border-top-color: #0089ff;
+	--search-tab-button-selected-background: #353535;
+	--stab-background-color: #314559;
+	--stab-code-color: #e6e1cf;
+	--code-highlight-kw-color: #ab8ac1;
+	--code-highlight-kw-2-color: #769acb;
+	--code-highlight-lifetime-color: #d97f26;
+	--code-highlight-prelude-color: #769acb;
+	--code-highlight-prelude-val-color: #ee6868;
+	--code-highlight-number-color: #83a300;
+	--code-highlight-string-color: #83a300;
+	--code-highlight-literal-color: #ee6868;
+	--code-highlight-attribute-color: #ee6868;
+	--code-highlight-self-color: #ee6868;
+	--code-highlight-macro-color: #3e999f;
+	--code-highlight-question-mark-color: #ff9011;
+	--code-highlight-comment-color: #8d8d8b;
+	--code-highlight-doc-comment-color: #8ca375;
+	--src-line-numbers-span-color: #3b91e2;
+	--src-line-number-highlighted-background-color: #0a042f;
+	--test-arrow-color: #dedede;
+	--test-arrow-background-color: rgba(78, 139, 202, 0.2);
+	--test-arrow-hover-color: #dedede;
+	--test-arrow-hover-background-color: #4e8bca;
+	--target-background-color: #494a3d;
+	--target-border-color: #bb7410;
+	--kbd-color: #000;
+	--kbd-background: #fafbfc;
+	--kbd-box-shadow-color: #c6cbd1;
+	--rust-logo-filter: drop-shadow(1px 0 0px #fff)
+		drop-shadow(0 1px 0 #fff)
+		drop-shadow(-1px 0 0 #fff)
+		drop-shadow(0 -1px 0 #fff);
+	/* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
+	--crate-search-div-filter: invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg)
+		brightness(90%) contrast(90%);
+	--crate-search-div-hover-filter: invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg)
+		brightness(100%) contrast(91%);
+	--crate-search-hover-border: #2196f3;
+	--src-sidebar-background-selected: #333;
+	--src-sidebar-background-hover: #444;
+	--table-alt-row-background-color: #2a2a2a;
+	--codeblock-link-background: #333;
+	--scrape-example-toggle-line-background: #999;
+	--scrape-example-toggle-line-hover-background: #c5c5c5;
+	--scrape-example-code-line-highlight: #5b3b01;
+	--scrape-example-code-line-highlight-focus: #7c4b0f;
+	--scrape-example-help-border-color: #aaa;
+	--scrape-example-help-color: #eee;
+	--scrape-example-help-hover-border-color: #fff;
+	--scrape-example-help-hover-color: #fff;
+	--scrape-example-code-wrapper-background-start: rgba(53, 53, 53, 1);
+	--scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0);
+}
+/* End theme: dark */
+
+/* Begin theme: ayu */
+/*
+Based off of the Ayu theme
+Original by Dempfi (https://github.com/dempfi/ayu)
+*/
+:root[data-theme="ayu"] {
+	--main-background-color: #0f1419;
+	--main-color: #c5c5c5;
+	--settings-input-color: #ffb454;
+	--settings-input-border-color: #999;
+	--settings-button-color: #fff;
+	--settings-button-border-focus: #e0e0e0;
+	--sidebar-background-color: #14191f;
+	--sidebar-background-color-hover: rgba(70, 70, 70, 0.33);
+	--code-block-background-color: #191f26;
+	--scrollbar-track-background-color: transparent;
+	--scrollbar-thumb-background-color: #5c6773;
+	--scrollbar-color: #5c6773 #24292f;
+	--headings-border-bottom-color: #5c6773;
+	--border-color: #5c6773;
+	--button-background-color: #141920;
+	--right-side-color: grey;
+	--code-attribute-color: #999;
+	--toggles-color: #999;
+	--toggle-filter: invert(100%);
+	--search-input-focused-border-color: #5c6773; /* Same as `--border-color`. */
+	--copy-path-button-color: #fff;
+	--copy-path-img-filter: invert(70%);
+	--copy-path-img-hover-filter: invert(100%);
+	--codeblock-error-hover-color: rgb(255, 0, 0);
+	--codeblock-error-color: rgba(255, 0, 0, .5);
+	--codeblock-ignore-hover-color: rgb(255, 142, 0);
+	--codeblock-ignore-color: rgba(255, 142, 0, .6);
+	--warning-border-color: #ff8e00;
+	--type-link-color: #ffa0a5;
+	--trait-link-color: #39afd7;
+	--assoc-item-link-color: #39afd7;
+	--function-link-color: #fdd687;
+	--macro-link-color: #a37acc;
+	--keyword-link-color: #39afd7;
+	--mod-link-color: #39afd7;
+	--link-color: #39afd7;
+	--sidebar-link-color: #53b1db;
+	--sidebar-current-link-background-color: transparent;
+	--search-result-link-focus-background-color: #3c3c3c;
+	--search-result-border-color: #aaa3;
+	--search-color: #fff;
+	--search-error-code-background-color: #4f4c4c;
+	--search-results-alias-color: #c5c5c5;
+	--search-results-grey-color: #999;
+	--search-tab-title-count-color: #888;
+	--search-tab-button-not-selected-border-top-color: none;
+	--search-tab-button-not-selected-background: transparent !important;
+	--search-tab-button-selected-border-top-color: none;
+	--search-tab-button-selected-background: #141920 !important;
+	--stab-background-color: #314559;
+	--stab-code-color: #e6e1cf;
+	--code-highlight-kw-color: #ff7733;
+	--code-highlight-kw-2-color: #ff7733;
+	--code-highlight-lifetime-color: #ff7733;
+	--code-highlight-prelude-color: #69f2df;
+	--code-highlight-prelude-val-color: #ff7733;
+	--code-highlight-number-color: #b8cc52;
+	--code-highlight-string-color: #b8cc52;
+	--code-highlight-literal-color: #ff7733;
+	--code-highlight-attribute-color: #e6e1cf;
+	--code-highlight-self-color: #36a3d9;
+	--code-highlight-macro-color: #a37acc;
+	--code-highlight-question-mark-color: #ff9011;
+	--code-highlight-comment-color: #788797;
+	--code-highlight-doc-comment-color: #a1ac88;
+	--src-line-numbers-span-color: #5c6773;
+	--src-line-number-highlighted-background-color: rgba(255, 236, 164, 0.06);
+	--test-arrow-color: #788797;
+	--test-arrow-background-color: rgba(57, 175, 215, 0.09);
+	--test-arrow-hover-color: #c5c5c5;
+	--test-arrow-hover-background-color: rgba(57, 175, 215, 0.368);
+	--target-background-color: rgba(255, 236, 164, 0.06);
+	--target-border-color: rgba(255, 180, 76, 0.85);
+	--kbd-color: #c5c5c5;
+	--kbd-background: #314559;
+	--kbd-box-shadow-color: #5c6773;
+	--rust-logo-filter: drop-shadow(1px 0 0px #fff)
+		drop-shadow(0 1px 0 #fff)
+		drop-shadow(-1px 0 0 #fff)
+		drop-shadow(0 -1px 0 #fff);
+	/* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
+	--crate-search-div-filter: invert(41%) sepia(12%) saturate(487%) hue-rotate(171deg)
+		brightness(94%) contrast(94%);
+	--crate-search-div-hover-filter: invert(98%) sepia(12%) saturate(81%) hue-rotate(343deg)
+		brightness(113%) contrast(76%);
+	--crate-search-hover-border: #e0e0e0;
+	--src-sidebar-background-selected: #14191f;
+	--src-sidebar-background-hover: #14191f;
+	--table-alt-row-background-color: #191f26;
+	--codeblock-link-background: #333;
+	--scrape-example-toggle-line-background: #999;
+	--scrape-example-toggle-line-hover-background: #c5c5c5;
+	--scrape-example-code-line-highlight: #5b3b01;
+	--scrape-example-code-line-highlight-focus: #7c4b0f;
+	--scrape-example-help-border-color: #aaa;
+	--scrape-example-help-color: #eee;
+	--scrape-example-help-hover-border-color: #fff;
+	--scrape-example-help-hover-color: #fff;
+	--scrape-example-code-wrapper-background-start: rgba(15, 20, 25, 1);
+	--scrape-example-code-wrapper-background-end: rgba(15, 20, 25, 0);
+}
+
+:root[data-theme="ayu"] h1,
+:root[data-theme="ayu"] h2,
+:root[data-theme="ayu"] h3,
+:root[data-theme="ayu"] h4,
+:where(:root[data-theme="ayu"]) h1 a,
+:root[data-theme="ayu"] .sidebar h2 a,
+:root[data-theme="ayu"] .sidebar h3 a,
+:root[data-theme="ayu"] #source-sidebar > .title {
+	color: #fff;
+}
+
+:root[data-theme="ayu"] .docblock code {
+	color: #ffb454;
+}
+
+:root[data-theme="ayu"] .docblock a > code {
+	color: #39AFD7 !important;
+}
+
+:root[data-theme="ayu"] .code-header,
+:root[data-theme="ayu"] .docblock pre > code,
+:root[data-theme="ayu"] pre,
+:root[data-theme="ayu"] pre > code,
+:root[data-theme="ayu"] .item-info code,
+:root[data-theme="ayu"] .rustdoc.source .example-wrap {
+	color: #e6e1cf;
+}
+
+:root[data-theme="ayu"] .sidebar .current,
+:root[data-theme="ayu"] .sidebar a:hover,
+:root[data-theme="ayu"] #src-sidebar div.files > a:hover,
+:root[data-theme="ayu"] details.dir-entry summary:hover,
+:root[data-theme="ayu"] #src-sidebar div.files > a:focus,
+:root[data-theme="ayu"] details.dir-entry summary:focus,
+:root[data-theme="ayu"] #src-sidebar div.files > a.selected {
+	color: #ffb44c;
+}
+
+:root[data-theme="ayu"] .sidebar-elems .location {
+	color: #ff7733;
+}
+
+:root[data-theme="ayu"] .src-line-numbers .line-highlighted {
+	color: #708090;
+	padding-right: 7px;
+	border-right: 1px solid #ffb44c;
+}
+
+:root[data-theme="ayu"] .search-results a:hover,
+:root[data-theme="ayu"] .search-results a:focus {
+	color: #fff !important;
+	background-color: #3c3c3c;
+}
+
+:root[data-theme="ayu"] .search-results a {
+	color: #0096cf;
+}
+
+:root[data-theme="ayu"] .search-results a div.desc {
+	color: #c5c5c5;
+}
+
+:root[data-theme="ayu"] .result-name .primitive > i,
+:root[data-theme="ayu"] .result-name .keyword > i {
+	color: #788797;
+}
+
+:root[data-theme="ayu"] #search-tabs > button.selected {
+	border-bottom: 1px solid #ffb44c !important;
+	border-top: none;
+}
+:root[data-theme="ayu"] #search-tabs > button:not(.selected) {
+	border: none;
+	background-color: transparent !important;
+}
+:root[data-theme="ayu"] #search-tabs > button:hover {
+	border-bottom: 1px solid rgba(242, 151, 24, 0.3);
+}
+
+:root[data-theme="ayu"] #settings-menu > a img {
+	filter: invert(100);
+}
+/* End theme: ayu */
+
+/* End: styles for themes */
diff --git a/src/librustdoc/html/static/css/settings.css b/src/librustdoc/html/static/css/settings.css
deleted file mode 100644
index c1324c0760e..00000000000
--- a/src/librustdoc/html/static/css/settings.css
+++ /dev/null
@@ -1,63 +0,0 @@
-.setting-line {
-	margin: 1.2em 0.6em;
-}
-
-.setting-radio input, .setting-check input {
-	margin-right: 0.3em;
-	height: 1.2rem;
-	width: 1.2rem;
-	border: 2px solid var(--settings-input-border-color);
-	outline: none;
-	-webkit-appearance: none;
-	cursor: pointer;
-}
-.setting-radio input {
-	border-radius: 50%;
-}
-
-.setting-radio span, .setting-check span {
-	padding-bottom: 1px;
-}
-
-.setting-radio {
-	margin-top: 0.1em;
-	margin-bottom: 0.1em;
-	min-width: 3.8em;
-	padding: 0.3em;
-	display: inline-flex;
-	align-items: center;
-	cursor: pointer;
-}
-.setting-radio + .setting-radio {
-	margin-left: 0.5em;
-}
-
-.setting-check {
-	margin-right: 20px;
-	display: flex;
-	align-items: center;
-	cursor: pointer;
-}
-
-.setting-radio input:checked {
-	box-shadow: inset 0 0 0 3px var(--main-background-color);
-	background-color: var(--settings-input-color);
-}
-.setting-check input:checked {
-	background-color: var(--settings-input-color);
-	border-width: 1px;
-	content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">\
-		<path d="M7,25L17,32L33,12" fill="none" stroke="black" stroke-width="5"/>\
-		<path d="M7,23L17,30L33,10" fill="none" stroke="white" stroke-width="5"/></svg>');
-}
-.setting-radio input:focus, .setting-check input:focus {
-	box-shadow: 0 0 1px 1px var(--settings-input-color);
-}
-/* In here we combine both `:focus` and `:checked` properties. */
-.setting-radio input:checked:focus {
-	box-shadow: inset 0 0 0 3px var(--main-background-color),
-		0 0 2px 2px var(--settings-input-color);
-}
-.setting-radio input:hover, .setting-check input:hover {
-	border-color: var(--settings-input-color) !important;
-}
diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css
deleted file mode 100644
index 873a1668f8b..00000000000
--- a/src/librustdoc/html/static/css/themes/ayu.css
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
-Based off of the Ayu theme
-Original by Dempfi (https://github.com/dempfi/ayu)
-*/
-
-:root {
-	--main-background-color: #0f1419;
-	--main-color: #c5c5c5;
-	--settings-input-color: #ffb454;
-	--settings-input-border-color: #999;
-	--settings-button-color: #fff;
-	--settings-button-border-focus: #e0e0e0;
-	--sidebar-background-color: #14191f;
-	--sidebar-background-color-hover: rgba(70, 70, 70, 0.33);
-	--code-block-background-color: #191f26;
-	--scrollbar-track-background-color: transparent;
-	--scrollbar-thumb-background-color: #5c6773;
-	--scrollbar-color: #5c6773 #24292f;
-	--headings-border-bottom-color: #5c6773;
-	--border-color: #5c6773;
-	--button-background-color: #141920;
-	--right-side-color: grey;
-	--code-attribute-color: #999;
-	--toggles-color: #999;
-	--toggle-filter: invert(100%);
-	--search-input-focused-border-color: #5c6773; /* Same as `--border-color`. */
-	--copy-path-button-color: #fff;
-	--copy-path-img-filter: invert(70%);
-	--copy-path-img-hover-filter: invert(100%);
-	--codeblock-error-hover-color: rgb(255, 0, 0);
-	--codeblock-error-color: rgba(255, 0, 0, .5);
-	--codeblock-ignore-hover-color: rgb(255, 142, 0);
-	--codeblock-ignore-color: rgba(255, 142, 0, .6);
-	--warning-border-color: #ff8e00;
-	--type-link-color: #ffa0a5;
-	--trait-link-color: #39afd7;
-	--assoc-item-link-color: #39afd7;
-	--function-link-color: #fdd687;
-	--macro-link-color: #a37acc;
-	--keyword-link-color: #39afd7;
-	--mod-link-color: #39afd7;
-	--link-color: #39afd7;
-	--sidebar-link-color: #53b1db;
-	--sidebar-current-link-background-color: transparent;
-	--search-result-link-focus-background-color: #3c3c3c;
-	--search-result-border-color: #aaa3;
-	--search-color: #fff;
-	--search-error-code-background-color: #4f4c4c;
-	--search-results-alias-color: #c5c5c5;
-	--search-results-grey-color: #999;
-	--search-tab-title-count-color: #888;
-	--search-tab-button-not-selected-border-top-color: none;
-	--search-tab-button-not-selected-background: transparent !important;
-	--search-tab-button-selected-border-top-color: none;
-	--search-tab-button-selected-background: #141920 !important;
-	--stab-background-color: #314559;
-	--stab-code-color: #e6e1cf;
-	--code-highlight-kw-color: #ff7733;
-	--code-highlight-kw-2-color: #ff7733;
-	--code-highlight-lifetime-color: #ff7733;
-	--code-highlight-prelude-color: #69f2df;
-	--code-highlight-prelude-val-color: #ff7733;
-	--code-highlight-number-color: #b8cc52;
-	--code-highlight-string-color: #b8cc52;
-	--code-highlight-literal-color: #ff7733;
-	--code-highlight-attribute-color: #e6e1cf;
-	--code-highlight-self-color: #36a3d9;
-	--code-highlight-macro-color: #a37acc;
-	--code-highlight-question-mark-color: #ff9011;
-	--code-highlight-comment-color: #788797;
-	--code-highlight-doc-comment-color: #a1ac88;
-	--src-line-numbers-span-color: #5c6773;
-	--src-line-number-highlighted-background-color: rgba(255, 236, 164, 0.06);
-	--test-arrow-color: #788797;
-	--test-arrow-background-color: rgba(57, 175, 215, 0.09);
-	--test-arrow-hover-color: #c5c5c5;
-	--test-arrow-hover-background-color: rgba(57, 175, 215, 0.368);
-	--target-background-color: rgba(255, 236, 164, 0.06);
-	--target-border-color: rgba(255, 180, 76, 0.85);
-	--kbd-color: #c5c5c5;
-	--kbd-background: #314559;
-	--kbd-box-shadow-color: #5c6773;
-	--rust-logo-filter: drop-shadow(1px 0 0px #fff)
-		drop-shadow(0 1px 0 #fff)
-		drop-shadow(-1px 0 0 #fff)
-		drop-shadow(0 -1px 0 #fff);
-	/* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
-	--crate-search-div-filter: invert(41%) sepia(12%) saturate(487%) hue-rotate(171deg)
-		brightness(94%) contrast(94%);
-	--crate-search-div-hover-filter: invert(98%) sepia(12%) saturate(81%) hue-rotate(343deg)
-		brightness(113%) contrast(76%);
-	--crate-search-hover-border: #e0e0e0;
-	--src-sidebar-background-selected: #14191f;
-	--src-sidebar-background-hover: #14191f;
-	--table-alt-row-background-color: #191f26;
-	--codeblock-link-background: #333;
-	--scrape-example-toggle-line-background: #999;
-	--scrape-example-toggle-line-hover-background: #c5c5c5;
-	--scrape-example-code-line-highlight: #5b3b01;
-	--scrape-example-code-line-highlight-focus: #7c4b0f;
-	--scrape-example-help-border-color: #aaa;
-	--scrape-example-help-color: #eee;
-	--scrape-example-help-hover-border-color: #fff;
-	--scrape-example-help-hover-color: #fff;
-	--scrape-example-code-wrapper-background-start: rgba(15, 20, 25, 1);
-	--scrape-example-code-wrapper-background-end: rgba(15, 20, 25, 0);
-}
-
-h1, h2, h3, h4,
-h1 a, .sidebar h2 a, .sidebar h3 a,
-#src-sidebar > .title {
-	color: #fff;
-}
-h4 {
-	border: none;
-}
-
-.docblock code {
-	color: #ffb454;
-}
-.docblock a > code {
-	color: #39AFD7 !important;
-}
-.code-header,
-.docblock pre > code,
-pre, pre > code,
-.item-info code,
-.rustdoc.src .example-wrap {
-	color: #e6e1cf;
-}
-
-.sidebar .current,
-.sidebar a:hover,
-#src-sidebar div.files > a:hover, details.dir-entry summary:hover,
-#src-sidebar div.files > a:focus, details.dir-entry summary:focus,
-#src-sidebar div.files > a.selected {
-	color: #ffb44c;
-}
-
-.sidebar-elems .location {
-	color: #ff7733;
-}
-
-.src-line-numbers .line-highlighted {
-	color: #708090;
-	padding-right: 7px;
-	border-right: 1px solid #ffb44c;
-}
-
-.search-results a:hover,
-.search-results a:focus {
-	color: #fff !important;
-	background-color: #3c3c3c;
-}
-
-.search-results a {
-	color: #0096cf;
-}
-.search-results a div.desc {
-	color: #c5c5c5;
-}
-
-.result-name .primitive > i, .result-name .keyword > i {
-	color: #788797;
-}
-
-#search-tabs > button.selected {
-	border-bottom: 1px solid #ffb44c !important;
-	border-top: none;
-}
-#search-tabs > button:not(.selected) {
-	border: none;
-	background-color: transparent !important;
-}
-#search-tabs > button:hover {
-	border-bottom: 1px solid rgba(242, 151, 24, 0.3);
-}
-
-#settings-menu > a img {
-	filter: invert(100);
-}
diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css
deleted file mode 100644
index 2b6e28d35a5..00000000000
--- a/src/librustdoc/html/static/css/themes/dark.css
+++ /dev/null
@@ -1,102 +0,0 @@
-:root {
-	--main-background-color: #353535;
-	--main-color: #ddd;
-	--settings-input-color: #2196f3;
-	--settings-input-border-color: #999;
-	--settings-button-color: #000;
-	--settings-button-border-focus: #ffb900;
-	--sidebar-background-color: #505050;
-	--sidebar-background-color-hover: #676767;
-	--code-block-background-color: #2A2A2A;
-	--scrollbar-track-background-color: #717171;
-	--scrollbar-thumb-background-color: rgba(32, 34, 37, .6);
-	--scrollbar-color: rgba(32,34,37,.6) #5a5a5a;
-	--headings-border-bottom-color: #d2d2d2;
-	--border-color: #e0e0e0;
-	--button-background-color: #f0f0f0;
-	--right-side-color: grey;
-	--code-attribute-color: #999;
-	--toggles-color: #999;
-	--toggle-filter: invert(100%);
-	--search-input-focused-border-color: #008dfd;
-	--copy-path-button-color: #999;
-	--copy-path-img-filter: invert(50%);
-	--copy-path-img-hover-filter: invert(65%);
-	--codeblock-error-hover-color: rgb(255, 0, 0);
-	--codeblock-error-color: rgba(255, 0, 0, .5);
-	--codeblock-ignore-hover-color: rgb(255, 142, 0);
-	--codeblock-ignore-color: rgba(255, 142, 0, .6);
-	--warning-border-color: #ff8e00;
-	--type-link-color: #2dbfb8;
-	--trait-link-color: #b78cf2;
-	--assoc-item-link-color: #d2991d;
-	--function-link-color: #2bab63;
-	--macro-link-color: #09bd00;
-	--keyword-link-color: #d2991d;
-	--mod-link-color:  #d2991d;
-	--link-color: #d2991d;
-	--sidebar-link-color: #fdbf35;
-	--sidebar-current-link-background-color: #444;
-	--search-result-link-focus-background-color: #616161;
-	--search-result-border-color: #aaa3;
-	--search-color: #111;
-	--search-error-code-background-color: #484848;
-	--search-results-alias-color: #fff;
-	--search-results-grey-color: #ccc;
-	--search-tab-title-count-color: #888;
-	--search-tab-button-not-selected-border-top-color: #252525;
-	--search-tab-button-not-selected-background: #252525;
-	--search-tab-button-selected-border-top-color: #0089ff;
-	--search-tab-button-selected-background: #353535;
-	--stab-background-color: #314559;
-	--stab-code-color: #e6e1cf;
-	--code-highlight-kw-color: #ab8ac1;
-	--code-highlight-kw-2-color: #769acb;
-	--code-highlight-lifetime-color: #d97f26;
-	--code-highlight-prelude-color: #769acb;
-	--code-highlight-prelude-val-color: #ee6868;
-	--code-highlight-number-color: #83a300;
-	--code-highlight-string-color: #83a300;
-	--code-highlight-literal-color: #ee6868;
-	--code-highlight-attribute-color: #ee6868;
-	--code-highlight-self-color: #ee6868;
-	--code-highlight-macro-color: #3e999f;
-	--code-highlight-question-mark-color: #ff9011;
-	--code-highlight-comment-color: #8d8d8b;
-	--code-highlight-doc-comment-color: #8ca375;
-	--src-line-numbers-span-color: #3b91e2;
-	--src-line-number-highlighted-background-color: #0a042f;
-	--test-arrow-color: #dedede;
-	--test-arrow-background-color: rgba(78, 139, 202, 0.2);
-	--test-arrow-hover-color: #dedede;
-	--test-arrow-hover-background-color: #4e8bca;
-	--target-background-color: #494a3d;
-	--target-border-color: #bb7410;
-	--kbd-color: #000;
-	--kbd-background: #fafbfc;
-	--kbd-box-shadow-color: #c6cbd1;
-	--rust-logo-filter: drop-shadow(1px 0 0px #fff)
-		drop-shadow(0 1px 0 #fff)
-		drop-shadow(-1px 0 0 #fff)
-		drop-shadow(0 -1px 0 #fff);
-	/* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
-	--crate-search-div-filter: invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg)
-		brightness(90%) contrast(90%);
-	--crate-search-div-hover-filter: invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg)
-		brightness(100%) contrast(91%);
-	--crate-search-hover-border: #2196f3;
-	--src-sidebar-background-selected: #333;
-	--src-sidebar-background-hover: #444;
-	--table-alt-row-background-color: #2a2a2a;
-	--codeblock-link-background: #333;
-	--scrape-example-toggle-line-background: #999;
-	--scrape-example-toggle-line-hover-background: #c5c5c5;
-	--scrape-example-code-line-highlight: #5b3b01;
-	--scrape-example-code-line-highlight-focus: #7c4b0f;
-	--scrape-example-help-border-color: #aaa;
-	--scrape-example-help-color: #eee;
-	--scrape-example-help-hover-border-color: #fff;
-	--scrape-example-help-hover-color: #fff;
-	--scrape-example-code-wrapper-background-start: rgba(53, 53, 53, 1);
-	--scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0);
-}
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index cb653d6b8df..097aa0b940d 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -176,13 +176,6 @@ function browserSupportsHistoryApi() {
     return window.history && typeof window.history.pushState === "function";
 }
 
-function loadCss(cssUrl) {
-    const link = document.createElement("link");
-    link.href = cssUrl;
-    link.rel = "stylesheet";
-    document.getElementsByTagName("head")[0].appendChild(link);
-}
-
 function preLoadCss(cssUrl) {
     // https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload
     const link = document.createElement("link");
@@ -210,11 +203,7 @@ function preLoadCss(cssUrl) {
         event.preventDefault();
         // Sending request for the CSS and the JS files at the same time so it will
         // hopefully be loaded when the JS will generate the settings content.
-        loadCss(getVar("static-root-path") + getVar("settings-css"));
         loadScript(getVar("static-root-path") + getVar("settings-js"));
-        preLoadCss(getVar("static-root-path") + getVar("theme-light-css"));
-        preLoadCss(getVar("static-root-path") + getVar("theme-dark-css"));
-        preLoadCss(getVar("static-root-path") + getVar("theme-ayu-css"));
         // Pre-load all theme CSS files, so that switching feels seamless.
         //
         // When loading settings.html as a standalone page, the equivalent HTML is
@@ -852,14 +841,14 @@ function preLoadCss(cssUrl) {
         window.CURRENT_TOOLTIP_ELEMENT = wrapper;
         window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE = e;
         clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);
-        wrapper.onpointerenter = function(ev) {
+        wrapper.onpointerenter = ev => {
             // If this is a synthetic touch event, ignore it. A click event will be along shortly.
             if (ev.pointerType !== "mouse") {
                 return;
             }
             clearTooltipHoverTimeout(e);
         };
-        wrapper.onpointerleave = function(ev) {
+        wrapper.onpointerleave = ev => {
             // If this is a synthetic touch event, ignore it. A click event will be along shortly.
             if (ev.pointerType !== "mouse") {
                 return;
@@ -963,38 +952,38 @@ function preLoadCss(cssUrl) {
     }
 
     onEachLazy(document.getElementsByClassName("tooltip"), e => {
-        e.onclick = function() {
-            this.TOOLTIP_FORCE_VISIBLE = this.TOOLTIP_FORCE_VISIBLE ? false : true;
-            if (window.CURRENT_TOOLTIP_ELEMENT && !this.TOOLTIP_FORCE_VISIBLE) {
+        e.onclick = () => {
+            e.TOOLTIP_FORCE_VISIBLE = e.TOOLTIP_FORCE_VISIBLE ? false : true;
+            if (window.CURRENT_TOOLTIP_ELEMENT && !e.TOOLTIP_FORCE_VISIBLE) {
                 hideTooltip(true);
             } else {
-                showTooltip(this);
+                showTooltip(e);
                 window.CURRENT_TOOLTIP_ELEMENT.setAttribute("tabindex", "0");
                 window.CURRENT_TOOLTIP_ELEMENT.focus();
                 window.CURRENT_TOOLTIP_ELEMENT.onblur = tooltipBlurHandler;
             }
             return false;
         };
-        e.onpointerenter = function(ev) {
+        e.onpointerenter = ev => {
             // If this is a synthetic touch event, ignore it. A click event will be along shortly.
             if (ev.pointerType !== "mouse") {
                 return;
             }
-            setTooltipHoverTimeout(this, true);
+            setTooltipHoverTimeout(e, true);
         };
-        e.onpointermove = function(ev) {
+        e.onpointermove = ev => {
             // If this is a synthetic touch event, ignore it. A click event will be along shortly.
             if (ev.pointerType !== "mouse") {
                 return;
             }
-            setTooltipHoverTimeout(this, true);
+            setTooltipHoverTimeout(e, true);
         };
-        e.onpointerleave = function(ev) {
+        e.onpointerleave = ev => {
             // If this is a synthetic touch event, ignore it. A click event will be along shortly.
             if (ev.pointerType !== "mouse") {
                 return;
             }
-            if (!this.TOOLTIP_FORCE_VISIBLE &&
+            if (!e.TOOLTIP_FORCE_VISIBLE &&
                 !elemIsInParent(ev.relatedTarget, window.CURRENT_TOOLTIP_ELEMENT)) {
                 // Tooltip pointer leave gesture:
                 //
@@ -1139,7 +1128,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
      *
      * Pass "true" to reset focus for tooltip popovers.
      */
-    window.hideAllModals = function(switchFocus) {
+    window.hideAllModals = switchFocus => {
         hideSidebar();
         window.hidePopoverMenus();
         hideTooltip(switchFocus);
@@ -1148,7 +1137,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
     /**
      * Hide all the popover menus.
      */
-    window.hidePopoverMenus = function() {
+    window.hidePopoverMenus = () => {
         onEachLazy(document.querySelectorAll(".search-form .popover"), elem => {
             elem.style.display = "none";
         });
diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js
index 2cba32c1b50..63947789c54 100644
--- a/src/librustdoc/html/static/js/settings.js
+++ b/src/librustdoc/html/static/js/settings.js
@@ -59,8 +59,8 @@
             if (settingValue !== null) {
                 toggle.checked = settingValue === "true";
             }
-            toggle.onchange = function() {
-                changeSetting(this.id, this.checked);
+            toggle.onchange = () => {
+                changeSetting(toggle.id, toggle.checked);
             };
         });
         onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), elem => {
@@ -224,14 +224,14 @@
 
     if (isSettingsPage) {
         // We replace the existing "onclick" callback to do nothing if clicked.
-        getSettingsButton().onclick = function(event) {
+        getSettingsButton().onclick = event => {
             event.preventDefault();
         };
     } else {
         // We replace the existing "onclick" callback.
         const settingsButton = getSettingsButton();
         const settingsMenu = document.getElementById("settings");
-        settingsButton.onclick = function(event) {
+        settingsButton.onclick = event => {
             if (elemIsInParent(event.target, settingsMenu)) {
                 return;
             }
diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js
index af3ca42a6c0..c69641092ab 100644
--- a/src/librustdoc/html/static/js/storage.js
+++ b/src/librustdoc/html/static/js/storage.js
@@ -5,6 +5,7 @@
 // the page, so we don't see major layout changes during the load of the page.
 "use strict";
 
+const builtinThemes = ["light", "dark", "ayu"];
 const darkThemes = ["dark", "ayu"];
 window.currentTheme = document.getElementById("themeStyle");
 
@@ -119,19 +120,32 @@ function switchTheme(newThemeName, saveTheme) {
         updateLocalStorage("theme", newThemeName);
     }
 
-    let newHref;
+    document.documentElement.setAttribute("data-theme", newThemeName);
 
-    if (newThemeName === "light" || newThemeName === "dark" || newThemeName === "ayu") {
-        newHref = getVar("static-root-path") + getVar("theme-" + newThemeName + "-css");
+    if (builtinThemes.indexOf(newThemeName) !== -1) {
+        if (window.currentTheme) {
+            window.currentTheme.parentNode.removeChild(window.currentTheme);
+            window.currentTheme = null;
+        }
     } else {
-        newHref = getVar("root-path") + newThemeName + getVar("resource-suffix") + ".css";
-    }
-
-    if (!window.currentTheme) {
-        document.write(`<link rel="stylesheet" id="themeStyle" href="${newHref}">`);
-        window.currentTheme = document.getElementById("themeStyle");
-    } else if (newHref !== window.currentTheme.href) {
-        window.currentTheme.href = newHref;
+        const newHref = getVar("root-path") + newThemeName +
+            getVar("resource-suffix") + ".css";
+        if (!window.currentTheme) {
+            // If we're in the middle of loading, document.write blocks
+            // rendering, but if we are done, it would blank the page.
+            if (document.readyState === "loading") {
+                document.write(`<link rel="stylesheet" id="themeStyle" href="${newHref}">`);
+                window.currentTheme = document.getElementById("themeStyle");
+            } else {
+                window.currentTheme = document.createElement("link");
+                window.currentTheme.rel = "stylesheet";
+                window.currentTheme.id = "themeStyle";
+                window.currentTheme.href = newHref;
+                document.documentElement.appendChild(window.currentTheme);
+            }
+        } else if (newHref !== window.currentTheme.href) {
+            window.currentTheme.href = newHref;
+        }
     }
 }
 
diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs
index a27aa2b58d2..ca9a78f51b3 100644
--- a/src/librustdoc/html/static_files.rs
+++ b/src/librustdoc/html/static_files.rs
@@ -91,7 +91,6 @@ macro_rules! static_files {
 
 static_files! {
     rustdoc_css => "static/css/rustdoc.css",
-    settings_css => "static/css/settings.css",
     noscript_css => "static/css/noscript.css",
     normalize_css => "static/css/normalize.css",
     main_js => "static/js/main.js",
@@ -109,9 +108,6 @@ static_files! {
     rust_favicon_svg => "static/images/favicon.svg",
     rust_favicon_png_16 => "static/images/favicon-16x16.png",
     rust_favicon_png_32 => "static/images/favicon-32x32.png",
-    theme_light_css => "static/css/themes/light.css",
-    theme_dark_css => "static/css/themes/dark.css",
-    theme_ayu_css => "static/css/themes/ayu.css",
     fira_sans_regular => "static/fonts/FiraSans-Regular.woff2",
     fira_sans_medium => "static/fonts/FiraSans-Medium.woff2",
     fira_sans_license => "static/fonts/FiraSans-LICENSE.txt",
diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html
index 60ccfe4da44..b4b9c319165 100644
--- a/src/librustdoc/html/templates/page.html
+++ b/src/librustdoc/html/templates/page.html
@@ -15,8 +15,7 @@
     <link rel="stylesheet" {#+ #}
           href="{{static_root_path|safe}}{{files.normalize_css}}"> {# #}
     <link rel="stylesheet" {#+ #}
-          href="{{static_root_path|safe}}{{files.rustdoc_css}}" {#+ #}
-          id="mainThemeStyle"> {# #}
+          href="{{static_root_path|safe}}{{files.rustdoc_css}}"> {# #}
     {% if !layout.default_settings.is_empty() %}
     <script id="default-settings" {#+ #}
       {%~ for (k, v) in layout.default_settings ~%}
@@ -34,10 +33,6 @@
          data-channel="{{rust_channel}}" {#+ #}
          data-search-js="{{files.search_js}}" {#+ #}
          data-settings-js="{{files.settings_js}}" {#+ #}
-         data-settings-css="{{files.settings_css}}" {#+ #}
-         data-theme-light-css="{{files.theme_light_css}}" {#+ #}
-         data-theme-dark-css="{{files.theme_dark_css}}" {#+ #}
-         data-theme-ayu-css="{{files.theme_ayu_css}}" {#+ #}
     > {# #}
     <script src="{{static_root_path|safe}}{{files.storage_js}}"></script> {# #}
     {% if page.css_class.contains("crate") %}
@@ -54,12 +49,6 @@
     {% endif %}
     <noscript> {# #}
         <link rel="stylesheet" {#+ #}
-           media="(prefers-color-scheme:light)" {#+ #}
-           href="{{static_root_path|safe}}{{files.theme_light_css}}"> {# #}
-        <link rel="stylesheet" {#+ #}
-           media="(prefers-color-scheme:dark)" {#+ #}
-           href="{{static_root_path|safe}}{{files.theme_dark_css}}"> {# #}
-        <link rel="stylesheet" {#+ #}
            href="{{static_root_path|safe}}{{files.noscript_css}}"> {# #}
     </noscript> {# #}
     {% if layout.css_file_extension.is_some() %}
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 0c18f5687f5..08865015960 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -789,7 +789,7 @@ pub(crate) fn from_macro_kind(kind: rustc_span::hygiene::MacroKind) -> MacroKind
 
 impl FromWithTcx<Box<clean::TypeAlias>> for TypeAlias {
     fn from_tcx(type_alias: Box<clean::TypeAlias>, tcx: TyCtxt<'_>) -> Self {
-        let clean::TypeAlias { type_, generics, item_type: _ } = *type_alias;
+        let clean::TypeAlias { type_, generics, item_type: _, inner_type: _ } = *type_alias;
         TypeAlias { type_: type_.into_tcx(tcx), generics: generics.into_tcx(tcx) }
     }
 }
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 92e06f3ab0f..2f88f6dc6e0 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -430,8 +430,8 @@ fn opts() -> Vec<RustcOptGroup> {
             o.optopt(
                 "",
                 "resource-suffix",
-                "suffix to add to CSS and JavaScript files, e.g., \"light.css\" will become \
-                 \"light-suffix.css\"",
+                "suffix to add to CSS and JavaScript files, e.g., \"search-index.js\" will \
+                 become \"search-index-suffix.js\"",
                 "PATH",
             )
         }),
diff --git a/src/librustdoc/passes/check_custom_code_classes.rs b/src/librustdoc/passes/check_custom_code_classes.rs
new file mode 100644
index 00000000000..73e80372e4a
--- /dev/null
+++ b/src/librustdoc/passes/check_custom_code_classes.rs
@@ -0,0 +1,77 @@
+//! NIGHTLY & UNSTABLE CHECK: custom_code_classes_in_docs
+//!
+//! This pass will produce errors when finding custom classes outside of
+//! nightly + relevant feature active.
+
+use super::Pass;
+use crate::clean::{Crate, Item};
+use crate::core::DocContext;
+use crate::fold::DocFolder;
+use crate::html::markdown::{find_codes, ErrorCodes, LangString};
+
+use rustc_session::parse::feature_err;
+use rustc_span::symbol::sym;
+
+pub(crate) const CHECK_CUSTOM_CODE_CLASSES: Pass = Pass {
+    name: "check-custom-code-classes",
+    run: check_custom_code_classes,
+    description: "check for custom code classes without the feature-gate enabled",
+};
+
+pub(crate) fn check_custom_code_classes(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
+    let mut coll = CustomCodeClassLinter { cx };
+
+    coll.fold_crate(krate)
+}
+
+struct CustomCodeClassLinter<'a, 'tcx> {
+    cx: &'a DocContext<'tcx>,
+}
+
+impl<'a, 'tcx> DocFolder for CustomCodeClassLinter<'a, 'tcx> {
+    fn fold_item(&mut self, item: Item) -> Option<Item> {
+        look_for_custom_classes(&self.cx, &item);
+        Some(self.fold_item_recur(item))
+    }
+}
+
+#[derive(Debug)]
+struct TestsWithCustomClasses {
+    custom_classes_found: Vec<String>,
+}
+
+impl crate::doctest::Tester for TestsWithCustomClasses {
+    fn add_test(&mut self, _: String, config: LangString, _: usize) {
+        self.custom_classes_found.extend(config.added_classes.into_iter());
+    }
+}
+
+pub(crate) fn look_for_custom_classes<'tcx>(cx: &DocContext<'tcx>, item: &Item) {
+    if !item.item_id.is_local() {
+        // If non-local, no need to check anything.
+        return;
+    }
+
+    let mut tests = TestsWithCustomClasses { custom_classes_found: vec![] };
+
+    let dox = item.attrs.doc_value();
+    find_codes(&dox, &mut tests, ErrorCodes::No, false, None, true);
+
+    if !tests.custom_classes_found.is_empty() && !cx.tcx.features().custom_code_classes_in_docs {
+        feature_err(
+            &cx.tcx.sess.parse_sess,
+            sym::custom_code_classes_in_docs,
+            item.attr_span(cx.tcx),
+            "custom classes in code blocks are unstable",
+        )
+        .note(
+            // This will list the wrong items to make them more easily searchable.
+            // To ensure the most correct hits, it adds back the 'class:' that was stripped.
+            format!(
+                "found these custom classes: class={}",
+                tests.custom_classes_found.join(",class=")
+            ),
+        )
+        .emit();
+    }
+}
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 7b0a7a90d31..0db846bfc7e 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -16,7 +16,9 @@ use rustc_hir::Mutability;
 use rustc_middle::ty::{Ty, TyCtxt};
 use rustc_middle::{bug, span_bug, ty};
 use rustc_resolve::rustdoc::{has_primitive_or_keyword_docs, prepare_to_doc_link_resolution};
-use rustc_resolve::rustdoc::{strip_generics_from_path, MalformedGenerics};
+use rustc_resolve::rustdoc::{
+    source_span_for_markdown_range, strip_generics_from_path, MalformedGenerics,
+};
 use rustc_session::lint::Lint;
 use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{sym, Ident, Symbol};
@@ -1211,11 +1213,11 @@ impl LinkCollector<'_, '_> {
         ori_link: &MarkdownLinkRange,
         item: &Item,
     ) {
-        let span = super::source_span_for_markdown_range(
+        let span = source_span_for_markdown_range(
             self.cx.tcx,
             dox,
             ori_link.inner_range(),
-            &item.attrs,
+            &item.attrs.doc_strings,
         )
         .unwrap_or_else(|| item.attr_span(self.cx.tcx));
         rustc_session::parse::feature_err(
@@ -1702,26 +1704,27 @@ fn report_diagnostic(
         let (span, link_range) = match link_range {
             MarkdownLinkRange::Destination(md_range) => {
                 let mut md_range = md_range.clone();
-                let sp = super::source_span_for_markdown_range(tcx, dox, &md_range, &item.attrs)
-                    .map(|mut sp| {
-                        while dox.as_bytes().get(md_range.start) == Some(&b' ')
-                            || dox.as_bytes().get(md_range.start) == Some(&b'`')
-                        {
-                            md_range.start += 1;
-                            sp = sp.with_lo(sp.lo() + BytePos(1));
-                        }
-                        while dox.as_bytes().get(md_range.end - 1) == Some(&b' ')
-                            || dox.as_bytes().get(md_range.end - 1) == Some(&b'`')
-                        {
-                            md_range.end -= 1;
-                            sp = sp.with_hi(sp.hi() - BytePos(1));
-                        }
-                        sp
-                    });
+                let sp =
+                    source_span_for_markdown_range(tcx, dox, &md_range, &item.attrs.doc_strings)
+                        .map(|mut sp| {
+                            while dox.as_bytes().get(md_range.start) == Some(&b' ')
+                                || dox.as_bytes().get(md_range.start) == Some(&b'`')
+                            {
+                                md_range.start += 1;
+                                sp = sp.with_lo(sp.lo() + BytePos(1));
+                            }
+                            while dox.as_bytes().get(md_range.end - 1) == Some(&b' ')
+                                || dox.as_bytes().get(md_range.end - 1) == Some(&b'`')
+                            {
+                                md_range.end -= 1;
+                                sp = sp.with_hi(sp.hi() - BytePos(1));
+                            }
+                            sp
+                        });
                 (sp, MarkdownLinkRange::Destination(md_range))
             }
             MarkdownLinkRange::WholeLink(md_range) => (
-                super::source_span_for_markdown_range(tcx, dox, &md_range, &item.attrs),
+                source_span_for_markdown_range(tcx, dox, &md_range, &item.attrs.doc_strings),
                 link_range.clone(),
             ),
         };
diff --git a/src/librustdoc/passes/lint/bare_urls.rs b/src/librustdoc/passes/lint/bare_urls.rs
index 97078a5cb16..0c5cfffe1be 100644
--- a/src/librustdoc/passes/lint/bare_urls.rs
+++ b/src/librustdoc/passes/lint/bare_urls.rs
@@ -4,11 +4,11 @@
 use crate::clean::*;
 use crate::core::DocContext;
 use crate::html::markdown::main_body_opts;
-use crate::passes::source_span_for_markdown_range;
 use core::ops::Range;
 use pulldown_cmark::{Event, Parser, Tag};
 use regex::Regex;
 use rustc_errors::Applicability;
+use rustc_resolve::rustdoc::source_span_for_markdown_range;
 use std::mem;
 use std::sync::LazyLock;
 
@@ -21,8 +21,9 @@ pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item) {
     if !dox.is_empty() {
         let report_diag =
             |cx: &DocContext<'_>, msg: &'static str, url: &str, range: Range<usize>| {
-                let sp = source_span_for_markdown_range(cx.tcx, &dox, &range, &item.attrs)
-                    .unwrap_or_else(|| item.attr_span(cx.tcx));
+                let sp =
+                    source_span_for_markdown_range(cx.tcx, &dox, &range, &item.attrs.doc_strings)
+                        .unwrap_or_else(|| item.attr_span(cx.tcx));
                 cx.tcx.struct_span_lint_hir(crate::lint::BARE_URLS, hir_id, sp, msg, |lint| {
                     lint.note("bare URLs are not automatically turned into clickable links")
                         .span_suggestion(
diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs
index 37e28e1fbca..316b1a41c7d 100644
--- a/src/librustdoc/passes/lint/check_code_block_syntax.rs
+++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs
@@ -6,6 +6,7 @@ use rustc_errors::{
     Applicability, Diagnostic, Handler, LazyFallbackBundle,
 };
 use rustc_parse::parse_stream_from_source_str;
+use rustc_resolve::rustdoc::source_span_for_markdown_range;
 use rustc_session::parse::ParseSess;
 use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId};
 use rustc_span::source_map::{FilePathMapping, SourceMap};
@@ -14,7 +15,6 @@ use rustc_span::{FileName, InnerSpan, DUMMY_SP};
 use crate::clean;
 use crate::core::DocContext;
 use crate::html::markdown::{self, RustCodeBlock};
-use crate::passes::source_span_for_markdown_range;
 
 pub(crate) fn visit_item(cx: &DocContext<'_>, item: &clean::Item) {
     if let Some(dox) = &item.opt_doc_value() {
@@ -77,11 +77,15 @@ fn check_rust_syntax(
     let is_ignore = code_block.lang_string.ignore != markdown::Ignore::None;
 
     // The span and whether it is precise or not.
-    let (sp, precise_span) =
-        match source_span_for_markdown_range(cx.tcx, dox, &code_block.range, &item.attrs) {
-            Some(sp) => (sp, true),
-            None => (item.attr_span(cx.tcx), false),
-        };
+    let (sp, precise_span) = match source_span_for_markdown_range(
+        cx.tcx,
+        dox,
+        &code_block.range,
+        &item.attrs.doc_strings,
+    ) {
+        Some(sp) => (sp, true),
+        None => (item.attr_span(cx.tcx), false),
+    };
 
     let msg = if buffer.has_errors {
         "could not parse code block as Rust code"
diff --git a/src/librustdoc/passes/lint/html_tags.rs b/src/librustdoc/passes/lint/html_tags.rs
index c135c584cec..79fc599e176 100644
--- a/src/librustdoc/passes/lint/html_tags.rs
+++ b/src/librustdoc/passes/lint/html_tags.rs
@@ -2,9 +2,9 @@
 use crate::clean::*;
 use crate::core::DocContext;
 use crate::html::markdown::main_body_opts;
-use crate::passes::source_span_for_markdown_range;
 
 use pulldown_cmark::{BrokenLink, Event, LinkType, Parser, Tag};
+use rustc_resolve::rustdoc::source_span_for_markdown_range;
 
 use std::iter::Peekable;
 use std::ops::Range;
@@ -20,7 +20,8 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
     let dox = item.doc_value();
     if !dox.is_empty() {
         let report_diag = |msg: String, range: &Range<usize>, is_open_tag: bool| {
-            let sp = match source_span_for_markdown_range(tcx, &dox, range, &item.attrs) {
+            let sp = match source_span_for_markdown_range(tcx, &dox, range, &item.attrs.doc_strings)
+            {
                 Some(sp) => sp,
                 None => item.attr_span(tcx),
             };
@@ -60,7 +61,7 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
                             tcx,
                             &dox,
                             &(generics_start..generics_end),
-                            &item.attrs,
+                            &item.attrs.doc_strings,
                         ) {
                             Some(sp) => sp,
                             None => item.attr_span(tcx),
diff --git a/src/librustdoc/passes/lint/redundant_explicit_links.rs b/src/librustdoc/passes/lint/redundant_explicit_links.rs
index 67cd2cc9732..0c15bf5f764 100644
--- a/src/librustdoc/passes/lint/redundant_explicit_links.rs
+++ b/src/librustdoc/passes/lint/redundant_explicit_links.rs
@@ -6,6 +6,7 @@ use rustc_errors::SuggestionStyle;
 use rustc_hir::def::{DefKind, DocLinkResMap, Namespace, Res};
 use rustc_hir::HirId;
 use rustc_lint_defs::Applicability;
+use rustc_resolve::rustdoc::source_span_for_markdown_range;
 use rustc_span::Symbol;
 
 use crate::clean::utils::find_nearest_parent_module;
@@ -13,7 +14,6 @@ use crate::clean::utils::inherits_doc_hidden;
 use crate::clean::Item;
 use crate::core::DocContext;
 use crate::html::markdown::main_body_opts;
-use crate::passes::source_span_for_markdown_range;
 
 #[derive(Debug)]
 struct LinkData {
@@ -160,16 +160,21 @@ fn check_inline_or_reference_unknown_redundancy(
         (find_resolution(resolutions, &dest)?, find_resolution(resolutions, resolvable_link)?);
 
     if dest_res == display_res {
-        let link_span = source_span_for_markdown_range(cx.tcx, &doc, &link_range, &item.attrs)
-            .unwrap_or(item.attr_span(cx.tcx));
+        let link_span =
+            source_span_for_markdown_range(cx.tcx, &doc, &link_range, &item.attrs.doc_strings)
+                .unwrap_or(item.attr_span(cx.tcx));
         let explicit_span = source_span_for_markdown_range(
             cx.tcx,
             &doc,
             &offset_explicit_range(doc, link_range, open, close),
-            &item.attrs,
+            &item.attrs.doc_strings,
+        )?;
+        let display_span = source_span_for_markdown_range(
+            cx.tcx,
+            &doc,
+            &resolvable_link_range,
+            &item.attrs.doc_strings,
         )?;
-        let display_span =
-            source_span_for_markdown_range(cx.tcx, &doc, &resolvable_link_range, &item.attrs)?;
 
         cx.tcx.struct_span_lint_hir(crate::lint::REDUNDANT_EXPLICIT_LINKS, hir_id, explicit_span, "redundant explicit link target", |lint| {
             lint.span_label(explicit_span, "explicit target is redundant")
@@ -201,21 +206,26 @@ fn check_reference_redundancy(
         (find_resolution(resolutions, &dest)?, find_resolution(resolutions, resolvable_link)?);
 
     if dest_res == display_res {
-        let link_span = source_span_for_markdown_range(cx.tcx, &doc, &link_range, &item.attrs)
-            .unwrap_or(item.attr_span(cx.tcx));
+        let link_span =
+            source_span_for_markdown_range(cx.tcx, &doc, &link_range, &item.attrs.doc_strings)
+                .unwrap_or(item.attr_span(cx.tcx));
         let explicit_span = source_span_for_markdown_range(
             cx.tcx,
             &doc,
             &offset_explicit_range(doc, link_range.clone(), b'[', b']'),
-            &item.attrs,
+            &item.attrs.doc_strings,
+        )?;
+        let display_span = source_span_for_markdown_range(
+            cx.tcx,
+            &doc,
+            &resolvable_link_range,
+            &item.attrs.doc_strings,
         )?;
-        let display_span =
-            source_span_for_markdown_range(cx.tcx, &doc, &resolvable_link_range, &item.attrs)?;
         let def_span = source_span_for_markdown_range(
             cx.tcx,
             &doc,
             &offset_reference_def_range(doc, dest, link_range),
-            &item.attrs,
+            &item.attrs.doc_strings,
         )?;
 
         cx.tcx.struct_span_lint_hir(crate::lint::REDUNDANT_EXPLICIT_LINKS, hir_id, explicit_span, "redundant explicit link target", |lint| {
diff --git a/src/librustdoc/passes/lint/unescaped_backticks.rs b/src/librustdoc/passes/lint/unescaped_backticks.rs
index 256958d7160..8b7fdd6ab1f 100644
--- a/src/librustdoc/passes/lint/unescaped_backticks.rs
+++ b/src/librustdoc/passes/lint/unescaped_backticks.rs
@@ -3,10 +3,10 @@
 use crate::clean::Item;
 use crate::core::DocContext;
 use crate::html::markdown::main_body_opts;
-use crate::passes::source_span_for_markdown_range;
 use pulldown_cmark::{BrokenLink, Event, Parser};
 use rustc_errors::DiagnosticBuilder;
 use rustc_lint_defs::Applicability;
+use rustc_resolve::rustdoc::source_span_for_markdown_range;
 use std::ops::Range;
 
 pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
@@ -52,7 +52,7 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
                     tcx,
                     &dox,
                     &(backtick_index..backtick_index + 1),
-                    &item.attrs,
+                    &item.attrs.doc_strings,
                 )
                 .unwrap_or_else(|| item.attr_span(tcx));
 
@@ -378,9 +378,12 @@ fn suggest_insertion(
     /// Maximum bytes of context to show around the insertion.
     const CONTEXT_MAX_LEN: usize = 80;
 
-    if let Some(span) =
-        source_span_for_markdown_range(cx.tcx, &dox, &(insert_index..insert_index), &item.attrs)
-    {
+    if let Some(span) = source_span_for_markdown_range(
+        cx.tcx,
+        &dox,
+        &(insert_index..insert_index),
+        &item.attrs.doc_strings,
+    ) {
         lint.span_suggestion(span, message, suggestion, Applicability::MaybeIncorrect);
     } else {
         let line_start = dox[..insert_index].rfind('\n').map_or(0, |idx| idx + 1);
diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs
index 4b1ff68df50..4eeaaa2bb70 100644
--- a/src/librustdoc/passes/mod.rs
+++ b/src/librustdoc/passes/mod.rs
@@ -1,11 +1,6 @@
 //! Contains information about "passes", used to modify crate information during the documentation
 //! process.
 
-use rustc_middle::ty::TyCtxt;
-use rustc_resolve::rustdoc::DocFragmentKind;
-use rustc_span::{InnerSpan, Span, DUMMY_SP};
-use std::ops::Range;
-
 use self::Condition::*;
 use crate::clean;
 use crate::core::DocContext;
@@ -40,6 +35,9 @@ pub(crate) use self::calculate_doc_coverage::CALCULATE_DOC_COVERAGE;
 mod lint;
 pub(crate) use self::lint::RUN_LINTS;
 
+mod check_custom_code_classes;
+pub(crate) use self::check_custom_code_classes::CHECK_CUSTOM_CODE_CLASSES;
+
 /// A single pass over the cleaned documentation.
 ///
 /// Runs in the compiler context, so it has access to types and traits and the like.
@@ -71,6 +69,7 @@ pub(crate) enum Condition {
 
 /// The full list of passes.
 pub(crate) const PASSES: &[Pass] = &[
+    CHECK_CUSTOM_CODE_CLASSES,
     CHECK_DOC_TEST_VISIBILITY,
     STRIP_HIDDEN,
     STRIP_PRIVATE,
@@ -84,6 +83,7 @@ pub(crate) const PASSES: &[Pass] = &[
 
 /// The list of passes run by default.
 pub(crate) const DEFAULT_PASSES: &[ConditionalPass] = &[
+    ConditionalPass::always(CHECK_CUSTOM_CODE_CLASSES),
     ConditionalPass::always(COLLECT_TRAIT_IMPLS),
     ConditionalPass::always(CHECK_DOC_TEST_VISIBILITY),
     ConditionalPass::new(STRIP_HIDDEN, WhenNotDocumentHidden),
@@ -115,89 +115,3 @@ impl ConditionalPass {
 pub(crate) fn defaults(show_coverage: bool) -> &'static [ConditionalPass] {
     if show_coverage { COVERAGE_PASSES } else { DEFAULT_PASSES }
 }
-
-/// Returns a span encompassing all the given attributes.
-pub(crate) fn span_of_attrs(attrs: &clean::Attributes) -> Option<Span> {
-    if attrs.doc_strings.is_empty() {
-        return None;
-    }
-    let start = attrs.doc_strings[0].span;
-    if start == DUMMY_SP {
-        return None;
-    }
-    let end = attrs.doc_strings.last().expect("no doc strings provided").span;
-    Some(start.to(end))
-}
-
-/// Attempts to match a range of bytes from parsed markdown to a `Span` in the source code.
-///
-/// This method will return `None` if we cannot construct a span from the source map or if the
-/// attributes are not all sugared doc comments. It's difficult to calculate the correct span in
-/// that case due to escaping and other source features.
-pub(crate) fn source_span_for_markdown_range(
-    tcx: TyCtxt<'_>,
-    markdown: &str,
-    md_range: &Range<usize>,
-    attrs: &clean::Attributes,
-) -> Option<Span> {
-    let is_all_sugared_doc =
-        attrs.doc_strings.iter().all(|frag| frag.kind == DocFragmentKind::SugaredDoc);
-
-    if !is_all_sugared_doc {
-        return None;
-    }
-
-    let snippet = tcx.sess.source_map().span_to_snippet(span_of_attrs(attrs)?).ok()?;
-
-    let starting_line = markdown[..md_range.start].matches('\n').count();
-    let ending_line = starting_line + markdown[md_range.start..md_range.end].matches('\n').count();
-
-    // We use `split_terminator('\n')` instead of `lines()` when counting bytes so that we treat
-    // CRLF and LF line endings the same way.
-    let mut src_lines = snippet.split_terminator('\n');
-    let md_lines = markdown.split_terminator('\n');
-
-    // The number of bytes from the source span to the markdown span that are not part
-    // of the markdown, like comment markers.
-    let mut start_bytes = 0;
-    let mut end_bytes = 0;
-
-    'outer: for (line_no, md_line) in md_lines.enumerate() {
-        loop {
-            let source_line = src_lines.next()?;
-            match source_line.find(md_line) {
-                Some(offset) => {
-                    if line_no == starting_line {
-                        start_bytes += offset;
-
-                        if starting_line == ending_line {
-                            break 'outer;
-                        }
-                    } else if line_no == ending_line {
-                        end_bytes += offset;
-                        break 'outer;
-                    } else if line_no < starting_line {
-                        start_bytes += source_line.len() - md_line.len();
-                    } else {
-                        end_bytes += source_line.len() - md_line.len();
-                    }
-                    break;
-                }
-                None => {
-                    // Since this is a source line that doesn't include a markdown line,
-                    // we have to count the newline that we split from earlier.
-                    if line_no <= starting_line {
-                        start_bytes += source_line.len() + 1;
-                    } else {
-                        end_bytes += source_line.len() + 1;
-                    }
-                }
-            }
-        }
-    }
-
-    Some(span_of_attrs(attrs)?.from_inner(InnerSpan::new(
-        md_range.start + start_bytes,
-        md_range.end + start_bytes + end_bytes,
-    )))
-}
diff --git a/src/librustdoc/theme.rs b/src/librustdoc/theme.rs
index 722e01cd1fc..8c1acbd7347 100644
--- a/src/librustdoc/theme.rs
+++ b/src/librustdoc/theme.rs
@@ -185,6 +185,9 @@ pub(crate) fn parse_selectors(
     while let Some(c) = iter.next() {
         match c {
             '{' => {
+                if selector.trim().starts_with(":root[data-theme") {
+                    selector = String::from(":root");
+                }
                 let s = minifier::css::minify(selector.trim()).map(|s| s.to_string())?;
                 parse_rules(content, s, iter, paths)?;
                 selector.clear();
diff --git a/src/librustdoc/theme/tests.rs b/src/librustdoc/theme/tests.rs
index 2a28c19c3fe..56f40b8046d 100644
--- a/src/librustdoc/theme/tests.rs
+++ b/src/librustdoc/theme/tests.rs
@@ -100,7 +100,7 @@ fn check_invalid_css() {
 
 #[test]
 fn test_with_minification() {
-    let text = include_str!("../html/static/css/themes/dark.css");
+    let text = include_str!("../html/static/css/noscript.css");
     let minified = minifier::css::minify(&text).expect("CSS minification failed").to_string();
 
     let against = load_css_paths(text).unwrap();
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject d14c85f4e6e7671673b1a1bc87231ff7164761e
+Subproject d5336f813df39d476e61fc46daabb1446350660
diff --git a/src/tools/clippy/.github/driver.sh b/src/tools/clippy/.github/driver.sh
index 798782340ee..c05c6ecc115 100644
--- a/src/tools/clippy/.github/driver.sh
+++ b/src/tools/clippy/.github/driver.sh
@@ -30,7 +30,7 @@ unset CARGO_MANIFEST_DIR
 # Run a lint and make sure it produces the expected output. It's also expected to exit with code 1
 # FIXME: How to match the clippy invocation in compile-test.rs?
 ./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/double_neg.rs 2>double_neg.stderr && exit 1
-sed -e "s,tests/ui,\$DIR," -e "/= help/d" double_neg.stderr >normalized.stderr
+sed -e "s,tests/ui,\$DIR," -e "/= help: for/d" double_neg.stderr > normalized.stderr
 diff -u normalized.stderr tests/ui/double_neg.stderr
 
 # make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same
diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml
index 5c69714bc1e..9b96f8dc253 100644
--- a/src/tools/clippy/.github/workflows/clippy_bors.yml
+++ b/src/tools/clippy/.github/workflows/clippy_bors.yml
@@ -52,24 +52,14 @@ jobs:
     needs: changelog
     strategy:
       matrix:
-        os: [ubuntu-latest, windows-latest, macos-latest]
-        host: [x86_64-unknown-linux-gnu, i686-unknown-linux-gnu, x86_64-apple-darwin, x86_64-pc-windows-msvc]
-        exclude:
+        include:
         - os: ubuntu-latest
-          host: x86_64-apple-darwin
-        - os: ubuntu-latest
-          host: x86_64-pc-windows-msvc
-        - os: macos-latest
-          host: x86_64-unknown-linux-gnu
-        - os: macos-latest
-          host: i686-unknown-linux-gnu
-        - os: macos-latest
-          host: x86_64-pc-windows-msvc
-        - os: windows-latest
           host: x86_64-unknown-linux-gnu
-        - os: windows-latest
+        - os: ubuntu-latest
           host: i686-unknown-linux-gnu
         - os: windows-latest
+          host: x86_64-pc-windows-msvc
+        - os: macos-latest
           host: x86_64-apple-darwin
 
     runs-on: ${{ matrix.os }}
@@ -84,8 +74,17 @@ jobs:
     - name: Checkout
       uses: actions/checkout@v3
 
+    - name: Install i686 dependencies
+      if: matrix.host == 'i686-unknown-linux-gnu'
+      run: |
+        sudo dpkg --add-architecture i386
+        sudo apt-get update
+        sudo apt-get install gcc-multilib zlib1g-dev:i386
+
     - name: Install toolchain
-      run: rustup show active-toolchain
+      run: |
+        rustup set default-host ${{ matrix.host }}
+        rustup show active-toolchain
 
     # Run
     - name: Set LD_LIBRARY_PATH (Linux)
@@ -109,11 +108,11 @@ jobs:
       run: cargo build --tests --features deny-warnings,internal
 
     - name: Test
-      if: runner.os == 'Linux'
+      if: matrix.host == 'x86_64-unknown-linux-gnu'
       run: cargo test --features deny-warnings,internal
 
     - name: Test
-      if: runner.os != 'Linux'
+      if: matrix.host != 'x86_64-unknown-linux-gnu'
       run: cargo test --features deny-warnings,internal -- --skip dogfood
 
     - name: Test clippy_lints
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 92cc19edb05..a7ae4a0ee2c 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -5031,6 +5031,7 @@ Released 2018-09-13
 [`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
 [`iter_on_empty_collections`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_empty_collections
 [`iter_on_single_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_single_items
+[`iter_out_of_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_out_of_bounds
 [`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned
 [`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
 [`iter_skip_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_zero
@@ -5131,6 +5132,7 @@ Released 2018-09-13
 [`misnamed_getters`]: https://rust-lang.github.io/rust-clippy/master/index.html#misnamed_getters
 [`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
 [`missing_assert_message`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_assert_message
+[`missing_asserts_for_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_asserts_for_indexing
 [`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
 [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
 [`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
@@ -5204,6 +5206,8 @@ Released 2018-09-13
 [`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding
 [`no_mangle_with_rust_abi`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_mangle_with_rust_abi
 [`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal
+[`non_canonical_clone_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_canonical_clone_impl
+[`non_canonical_partial_ord_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_canonical_partial_ord_impl
 [`non_minimal_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_minimal_cfg
 [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
 [`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty
@@ -5570,4 +5574,5 @@ Released 2018-09-13
 [`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings
 [`absolute-paths-max-segments`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-max-segments
 [`absolute-paths-allowed-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-allowed-crates
+[`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow
 <!-- end autogenerated links to configuration documentation -->
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index 7c78beb5dde..2d8b590dbe3 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -27,7 +27,7 @@ tempfile = { version = "3.2", optional = true }
 termize = "0.1"
 
 [dev-dependencies]
-ui_test = "0.18.1"
+ui_test = "0.20"
 tester = "0.9"
 regex = "1.5"
 toml = "0.7.3"
diff --git a/src/tools/clippy/book/src/SUMMARY.md b/src/tools/clippy/book/src/SUMMARY.md
index daaefd06a97..b02457307d7 100644
--- a/src/tools/clippy/book/src/SUMMARY.md
+++ b/src/tools/clippy/book/src/SUMMARY.md
@@ -14,8 +14,11 @@
     - [Basics](development/basics.md)
     - [Adding Lints](development/adding_lints.md)
     - [Defining Lints](development/defining_lints.md)
+    - [Writing tests](development/writing_tests.md)
     - [Lint Passes](development/lint_passes.md)
+    - [Emitting lints](development/emitting_lints.md)
     - [Type Checking](development/type_checking.md)
+    - [Trait Checking](development/trait_checking.md)
     - [Method Checking](development/method_checking.md)
     - [Macro Expansions](development/macro_expansions.md)
     - [Common Tools](development/common_tools_writing_lints.md)
diff --git a/src/tools/clippy/book/src/development/emitting_lints.md b/src/tools/clippy/book/src/development/emitting_lints.md
new file mode 100644
index 00000000000..a12f6aa91b3
--- /dev/null
+++ b/src/tools/clippy/book/src/development/emitting_lints.md
@@ -0,0 +1,217 @@
+# Emitting a lint
+
+Once we have [defined a lint](defining_lints.md), written [UI
+tests](writing_tests.md) and chosen [the lint pass](lint_passes.md) for the lint,
+we can begin the implementation of the lint logic so that we can emit it and
+gradually work towards a lint that behaves as expected.
+
+Note that we will not go into concrete implementation of a lint logic in this
+chapter. We will go into details in later chapters as well as in two examples of
+real Clippy lints.
+
+To emit a lint, we must implement a pass (see [Lint Passes](lint_passes.md)) for
+the lint that we have declared. In this example we'll implement a "late" lint,
+so take a look at the [LateLintPass][late_lint_pass] documentation, which
+provides an abundance of methods that we can implement for our lint.
+
+```rust
+pub trait LateLintPass<'tcx>: LintPass {
+    // Trait methods
+}
+```
+
+By far the most common method used for Clippy lints is [`check_expr`
+method][late_check_expr], this is because Rust is an expression language and,
+more often than not, the lint we want to work on must examine expressions.
+
+> _Note:_ If you don't fully understand what expressions are in Rust, take a
+> look at the official documentation on [expressions][rust_expressions]
+
+Other common ones include the [`check_fn` method][late_check_fn] and the
+[`check_item` method][late_check_item].
+
+### Emitting a lint
+
+Inside the trait method that we implement, we can write down the lint logic and
+emit the lint with suggestions.
+
+Clippy's [diagnostics] provides quite a few diagnostic functions that we can use
+to emit lints. Take a look at the documentation to pick one that suits your
+lint's needs the best. Some common ones you will encounter in the Clippy
+repository includes:
+
+- [`span_lint`]: Emits a lint without providing any other information
+- [`span_lint_and_note`]: Emits a lint and adds a note
+- [`span_lint_and_help`]: Emits a lint and provides a helpful message
+- [`span_lint_and_sugg`]: Emits a lint and provides a suggestion to fix the code
+- [`span_lint_and_then`]: Like `span_lint`, but allows for a lot of output
+  customization.
+
+```rust
+impl<'tcx> LateLintPass<'tcx> for LintName {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>)  {
+        // Imagine that `some_lint_expr_logic` checks for requirements for emitting the lint
+        if some_lint_expr_logic(expr) {
+            span_lint_and_help(
+                cx, // < The context
+                LINT_NAME, // < The name of the lint in ALL CAPS
+                expr.span, // < The span to lint
+                "message on why the lint is emitted",
+                None, // < An optional help span (to highlight something in the lint)
+                "message that provides a helpful suggestion",
+            );
+        }
+    }
+}
+```
+
+> Note: The message should be matter of fact and avoid capitalization and
+> punctuation. If multiple sentences are needed, the messages should probably be
+> split up into an error + a help / note / suggestion message.
+
+## Suggestions: Automatic fixes
+
+Some lints know what to change in order to fix the code. For example, the lint
+[`range_plus_one`][range_plus_one] warns for ranges where the user wrote `x..y +
+1` instead of using an [inclusive range][inclusive_range] (`x..=y`). The fix to
+this code would be changing the `x..y + 1` expression to `x..=y`. **This is
+where suggestions come in**.
+
+A suggestion is a change that the lint provides to fix the issue it is linting.
+The output looks something like this (from the example earlier):
+
+```text
+error: an inclusive range would be more readable
+  --> $DIR/range_plus_minus_one.rs:37:14
+   |
+LL |     for _ in 1..1 + 1 {}
+   |              ^^^^^^^^ help: use: `1..=1`
+```
+
+**Not all suggestions are always right**, some of them require human
+supervision, that's why we have [Applicability][applicability].
+
+Applicability indicates confidence in the correctness of the suggestion, some
+are always right (`Applicability::MachineApplicable`), but we use
+`Applicability::MaybeIncorrect` and others when talking about a suggestion that
+may be incorrect.
+
+### Example
+
+The same lint `LINT_NAME` but that emits a suggestion would look something like this:
+
+```rust
+impl<'tcx> LateLintPass<'tcx> for LintName {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>)  {
+        // Imagine that `some_lint_expr_logic` checks for requirements for emitting the lint
+        if some_lint_expr_logic(expr) {
+            span_lint_and_sugg( // < Note this change
+                cx,
+                LINT_NAME,
+                span,
+                "message on why the lint is emitted",
+                "use",
+                format!("foo + {} * bar", snippet(cx, expr.span, "<default>")), // < Suggestion
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
+```
+
+Suggestions generally use the [`format!`][format_macro] macro to interpolate the
+old values with the new ones. To get code snippets, use one of the `snippet*`
+functions from `clippy_utils::source`.
+
+## How to choose between notes, help messages and suggestions
+
+Notes are presented separately from the main lint message, they provide useful
+information that the user needs to understand why the lint was activated. They
+are the most helpful when attached to a span.
+
+Examples:
+
+### Notes
+
+```text
+error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
+  --> $DIR/drop_forget_ref.rs:10:5
+   |
+10 |     forget(&SomeStruct);
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::forget-ref` implied by `-D warnings`
+note: argument has type &SomeStruct
+  --> $DIR/drop_forget_ref.rs:10:12
+   |
+10 |     forget(&SomeStruct);
+   |            ^^^^^^^^^^^
+```
+
+### Help Messages
+
+Help messages are specifically to help the user. These are used in situation
+where you can't provide a specific machine applicable suggestion. They can also
+be attached to a span.
+
+Example:
+
+```text
+error: constant division of 0.0 with 0.0 will always result in NaN
+  --> $DIR/zero_div_zero.rs:6:25
+   |
+6  |     let other_f64_nan = 0.0f64 / 0.0;
+   |                         ^^^^^^^^^^^^
+   |
+   = help: consider using `f64::NAN` if you would like a constant representing NaN
+```
+
+### Suggestions
+
+Suggestions are the most helpful, they are changes to the source code to fix the
+error. The magic in suggestions is that tools like `rustfix` can detect them and
+automatically fix your code.
+
+Example:
+
+```text
+error: This `.fold` can be more succinctly expressed as `.any`
+--> $DIR/methods.rs:390:13
+    |
+390 |     let _ = (0..3).fold(false, |acc, x| acc || x > 2);
+    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)`
+    |
+```
+
+### Snippets
+
+Snippets are pieces of the source code (as a string), they are extracted
+generally using the [`snippet`][snippet_fn] function.
+
+For example, if you want to know how an item looks (and you know the item's
+span), you could use `snippet(cx, span, "..")`.
+
+## Final: Run UI Tests to Emit the Lint
+
+Now, if we run our [UI test](writing_tests.md), we should see that Clippy now
+produces output that contains the lint message we designed.
+
+The next step is to implement the logic properly, which is a detail that we will
+cover in the next chapters.
+
+[diagnostics]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/diagnostics/index.html
+[late_check_expr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_expr
+[late_check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_fn
+[late_check_item]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_item
+[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
+[rust_expressions]: https://doc.rust-lang.org/reference/expressions.html
+[`span_lint`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint.html
+[`span_lint_and_note`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_note.html
+[`span_lint_and_help`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_help.html
+[`span_lint_and_sugg`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_sugg.html
+[`span_lint_and_then`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_then.html
+[range_plus_one]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
+[inclusive_range]: https://doc.rust-lang.org/std/ops/struct.RangeInclusive.html
+[applicability]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_errors/enum.Applicability.html
+[snippet_fn]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/source/fn.snippet.html
+[format_macro]: https://doc.rust-lang.org/std/macro.format.html
diff --git a/src/tools/clippy/book/src/development/trait_checking.md b/src/tools/clippy/book/src/development/trait_checking.md
new file mode 100644
index 00000000000..fb263922cf8
--- /dev/null
+++ b/src/tools/clippy/book/src/development/trait_checking.md
@@ -0,0 +1,105 @@
+# Trait Checking
+
+Besides [type checking](type_checking.md), we might want to examine if
+a specific type `Ty` implements certain trait when implementing a lint.
+There are three approaches to achieve this, depending on if the target trait
+that we want to examine has a [diagnostic item][diagnostic_items],
+[lang item][lang_items], or neither.
+
+## Using Diagnostic Items
+
+As explained in the [Rust Compiler Development Guide][rustc_dev_guide], diagnostic items
+are introduced for identifying types via [Symbols][symbol].
+
+For instance, if we want to examine whether an expression implements
+the `Iterator` trait, we could simply write the following code,
+providing the `LateContext` (`cx`), our expression at hand, and
+the symbol of the trait in question:
+
+```rust
+use clippy_utils::is_trait_method;
+use rustc_hir::Expr;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_span::symbol::sym;
+
+impl LateLintPass<'_> for CheckIteratorTraitLint {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+		let implements_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| {
+    		implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[])
+		});
+		if implements_iterator {
+			// [...]
+		}
+
+    }
+}
+```
+
+> **Note**: Refer to [this index][symbol_index] for all the defined `Symbol`s.
+
+## Using Lang Items
+
+Besides diagnostic items, we can also use [`lang_items`][lang_items].
+Take a look at the documentation to find that `LanguageItems` contains
+all language items defined in the compiler.
+
+Using one of its `*_trait` method, we could obtain the [DefId] of any
+specific item, such as `Clone`, `Copy`, `Drop`, `Eq`, which are familiar
+to many Rustaceans.
+
+For instance, if we want to examine whether an expression `expr` implements
+`Drop` trait, we could access `LanguageItems` via our `LateContext`'s
+[TyCtxt], which provides a `lang_items` method that will return the id of
+`Drop` trait to us. Then, by calling Clippy utils function `implements_trait`
+we can check that the `Ty` of the `expr` implements the trait:
+
+```rust
+use clippy_utils::implements_trait;
+use rustc_hir::Expr;
+use rustc_lint::{LateContext, LateLintPass};
+
+impl LateLintPass<'_> for CheckDropTraitLint {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        let ty = cx.typeck_results().expr_ty(expr);
+        if cx.tcx.lang_items()
+            .drop_trait()
+            .map_or(false, |id| implements_trait(cx, ty, id, &[])) {
+                println!("`expr` implements `Drop` trait!");
+            }
+    }
+}
+```
+
+## Using Type Path
+
+If neither diagnostic item nor a language item is available, we can use
+[`clippy_utils::paths`][paths] with the `match_trait_method` to determine trait
+implementation.
+
+> **Note**: This approach should be avoided if possible, the best thing to do would be to make a PR to [`rust-lang/rust`][rust] adding a diagnostic item.
+
+Below, we check if the given `expr` implements the `Iterator`'s trait method `cloned` :
+
+```rust
+use clippy_utils::{match_trait_method, paths};
+use rustc_hir::Expr;
+use rustc_lint::{LateContext, LateLintPass};
+
+impl LateLintPass<'_> for CheckTokioAsyncReadExtTrait {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        if match_trait_method(cx, expr, &paths::CORE_ITER_CLONED) {
+            println!("`expr` implements `CORE_ITER_CLONED` trait!");
+        }
+    }
+}
+```
+
+[DefId]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html
+[diagnostic_items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
+[lang_items]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/lang_items/struct.LanguageItems.html
+[paths]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/paths.rs
+[rustc_dev_guide]: https://rustc-dev-guide.rust-lang.org/
+[symbol]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Symbol.html
+[symbol_index]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_span/symbol/sym/index.html
+[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html
+[rust]: https://github.com/rust-lang/rust
diff --git a/src/tools/clippy/book/src/development/writing_tests.md b/src/tools/clippy/book/src/development/writing_tests.md
new file mode 100644
index 00000000000..8937e0d8e94
--- /dev/null
+++ b/src/tools/clippy/book/src/development/writing_tests.md
@@ -0,0 +1,218 @@
+# Testing
+
+Developing lints for Clippy is a Test-Driven Development (TDD) process because
+our first task before implementing any logic for a new lint is to write some test cases.
+
+## Develop Lints with Tests
+
+When we develop Clippy, we enter a complex and chaotic realm full of
+programmatic issues, stylistic errors, illogical code and non-adherence to convention.
+Tests are the first layer of order we can leverage to define when and where
+we want a new lint to trigger or not.
+
+Moreover, writing tests first help Clippy developers to find a balance for
+the first iteration of and further enhancements for a lint.
+With test cases on our side, we will not have to worry about over-engineering
+a lint on its first version nor missing out some obvious edge cases of the lint.
+This approach empowers us to iteratively enhance each lint.
+
+## Clippy UI Tests
+
+We use **UI tests** for testing in Clippy. These UI tests check that the output
+of Clippy is exactly as we expect it to be. Each test is just a plain Rust file
+that contains the code we want to check.
+
+The output of Clippy is compared against a `.stderr` file. Note that you don't
+have to create this file yourself. We'll get to generating the `.stderr` files
+with the command [`cargo bless`](#cargo-bless) (seen later on).
+
+### Write Test Cases
+
+Let us now think about some tests for our imaginary `foo_functions` lint. We
+start by opening the test file `tests/ui/foo_functions.rs` that was created by
+`cargo dev new_lint`.
+
+Update the file with some examples to get started:
+
+```rust
+#![warn(clippy::foo_functions)] // < Add this, so the lint is guaranteed to be enabled in this file
+
+// Impl methods
+struct A;
+impl A {
+    pub fn fo(&self) {}
+    pub fn foo(&self) {} //~ ERROR: function called "foo"
+    pub fn food(&self) {}
+}
+
+// Default trait methods
+trait B {
+    fn fo(&self) {}
+    fn foo(&self) {} //~ ERROR: function called "foo"
+    fn food(&self) {}
+}
+
+// Plain functions
+fn fo() {}
+fn foo() {} //~ ERROR: function called "foo"
+fn food() {}
+
+fn main() {
+    // We also don't want to lint method calls
+    foo();
+    let a = A;
+    a.foo();
+}
+```
+
+Without actual lint logic to emit the lint when we see a `foo` function name,
+this test will just pass, because no lint will be emitted. However, we can now
+run the test with the following command:
+
+```sh
+$ TESTNAME=foo_functions cargo uitest
+```
+
+Clippy will compile and it will conclude with an `ok` for the tests:
+
+```
+...Clippy warnings and test outputs...
+test compile_test ... ok
+test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.48s
+```
+
+This is normal. After all, we wrote a bunch of Rust code but we haven't really
+implemented any logic for Clippy to detect `foo` functions and emit a lint.
+
+As we gradually implement our lint logic, we will keep running this UI test command.
+Clippy will begin outputting information that allows us to check if the output is
+turning into what we want it to be.
+
+### Example output
+
+As our `foo_functions` lint is tested, the output would look something like this:
+
+```
+failures:
+---- compile_test stdout ----
+normalized stderr:
+error: function called "foo"
+  --> $DIR/foo_functions.rs:6:12
+   |
+LL |     pub fn foo(&self) {}
+   |            ^^^
+   |
+   = note: `-D clippy::foo-functions` implied by `-D warnings`
+error: function called "foo"
+  --> $DIR/foo_functions.rs:13:8
+   |
+LL |     fn foo(&self) {}
+   |        ^^^
+error: function called "foo"
+  --> $DIR/foo_functions.rs:19:4
+   |
+LL | fn foo() {}
+   |    ^^^
+error: aborting due to 3 previous errors
+```
+
+Note the *failures* label at the top of the fragment, we'll get rid of it
+(saving this output) in the next section.
+
+> _Note:_ You can run multiple test files by specifying a comma separated list:
+> `TESTNAME=foo_functions,bar_methods,baz_structs`.
+
+### `cargo bless`
+
+Once we are satisfied with the output, we need to run this command to
+generate or update the `.stderr` file for our lint:
+
+```sh
+$ TESTNAME=foo_functions cargo uibless
+```
+
+This writes the emitted lint suggestions and fixes to the `.stderr` file, with
+the reason for the lint, suggested fixes, and line numbers, etc.
+
+Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit
+our lint, we need to commit the generated `.stderr` files, too.
+
+In general, you should only commit files changed by `cargo bless` for the
+specific lint you are creating/editing.
+
+> _Note:_ If the generated `.stderr`, and `.fixed` files are empty,
+> they should be removed.
+
+## `toml` Tests
+
+Some lints can be configured through a `clippy.toml` file. Those configuration
+values are tested in `tests/ui-toml`.
+
+To add a new test there, create a new directory and add the files:
+
+- `clippy.toml`: Put here the configuration value you want to test.
+- `lint_name.rs`: A test file where you put the testing code, that should see a
+  different lint behavior according to the configuration set in the
+  `clippy.toml` file.
+
+The potential `.stderr` and `.fixed` files can again be generated with `cargo
+bless`.
+
+## Cargo Lints
+
+The process of testing is different for Cargo lints in that now we are
+interested in the `Cargo.toml` manifest file. In this case, we also need a
+minimal crate associated with that manifest. Those tests are generated in
+`tests/ui-cargo`.
+
+Imagine we have a new example lint that is named `foo_categories`, we can run:
+
+```sh
+$ cargo dev new_lint --name=foo_categories --pass=late --category=cargo
+```
+
+After running `cargo dev new_lint` we will find by default two new crates,
+each with its manifest file:
+
+* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the
+  new lint to raise an error.
+* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger
+  the lint.
+
+If you need more cases, you can copy one of those crates (under
+`foo_categories`) and rename it.
+
+The process of generating the `.stderr` file is the same as for other lints
+and prepending the `TESTNAME` variable to `cargo uitest` works for Cargo lints too.
+
+## Rustfix Tests
+
+If the lint you are working on is making use of structured suggestions,
+[`rustfix`] will apply the suggestions from the lint to the test file code and
+compare that to the contents of a `.fixed` file.
+
+Structured suggestions tell a user how to fix or re-write certain code that has
+been linted with [`span_lint_and_sugg`].
+
+Should `span_lint_and_sugg` be used to generate a suggestion, but not all
+suggestions lead to valid code, you can use the `//@no-rustfix` comment on top
+of the test file, to not run `rustfix` on that file.
+
+We'll talk about suggestions more in depth in a [later chapter](emitting_lints.md).
+
+Use `cargo bless` to automatically generate the `.fixed` file after running
+the tests.
+
+[`rustfix`]: https://github.com/rust-lang/rustfix
+[`span_lint_and_sugg`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_sugg.html
+
+## Testing Manually
+
+Manually testing against an example file can be useful if you have added some
+`println!`s and the test suite output becomes unreadable.
+
+To try Clippy with your local modifications, run from the working copy root.
+
+```sh
+$ cargo dev lint input.rs
+```
diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md
index caaad6d1173..52c795e04fe 100644
--- a/src/tools/clippy/book/src/lint_configuration.md
+++ b/src/tools/clippy/book/src/lint_configuration.md
@@ -751,3 +751,27 @@ Which crates to allow absolute paths from
 * [`absolute_paths`](https://rust-lang.github.io/rust-clippy/master/index.html#absolute_paths)
 
 
+## `enforce-iter-loop-reborrow`
+#### Example
+```
+let mut vec = vec![1, 2, 3];
+let rmvec = &mut vec;
+for _ in rmvec.iter() {}
+for _ in rmvec.iter_mut() {}
+```
+
+Use instead:
+```
+let mut vec = vec![1, 2, 3];
+let rmvec = &mut vec;
+for _ in &*rmvec {}
+for _ in &mut *rmvec {}
+```
+
+**Default Value:** `false` (`bool`)
+
+---
+**Affected lints:**
+* [`explicit_iter_loop`](https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop)
+
+
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
index c0a9f466e7b..dcd9a4adcbd 100644
--- a/src/tools/clippy/clippy_lints/Cargo.toml
+++ b/src/tools/clippy/clippy_lints/Cargo.toml
@@ -15,7 +15,6 @@ clippy_utils = { path = "../clippy_utils" }
 declare_clippy_lint = { path = "../declare_clippy_lint" }
 if_chain = "1.0"
 itertools = "0.10.1"
-pulldown-cmark = { version = "0.9", default-features = false }
 quine-mc_cluskey = "0.2"
 regex-syntax = "0.7"
 serde = { version = "1.0", features = ["derive"] }
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index f73f1ed18f8..a4d40df52e7 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -211,8 +211,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO,
     crate::implied_bounds_in_impls::IMPLIED_BOUNDS_IN_IMPLS_INFO,
     crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO,
-    crate::incorrect_impls::INCORRECT_CLONE_IMPL_ON_COPY_TYPE_INFO,
-    crate::incorrect_impls::INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE_INFO,
     crate::index_refutable_slice::INDEX_REFUTABLE_SLICE_INFO,
     crate::indexing_slicing::INDEXING_SLICING_INFO,
     crate::indexing_slicing::OUT_OF_BOUNDS_INDEXING_INFO,
@@ -365,6 +363,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::methods::ITER_NTH_ZERO_INFO,
     crate::methods::ITER_ON_EMPTY_COLLECTIONS_INFO,
     crate::methods::ITER_ON_SINGLE_ITEMS_INFO,
+    crate::methods::ITER_OUT_OF_BOUNDS_INFO,
     crate::methods::ITER_OVEREAGER_CLONED_INFO,
     crate::methods::ITER_SKIP_NEXT_INFO,
     crate::methods::ITER_SKIP_ZERO_INFO,
@@ -456,6 +455,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::misc_early::ZERO_PREFIXED_LITERAL_INFO,
     crate::mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER_INFO,
     crate::missing_assert_message::MISSING_ASSERT_MESSAGE_INFO,
+    crate::missing_asserts_for_indexing::MISSING_ASSERTS_FOR_INDEXING_INFO,
     crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO,
     crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO,
     crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO,
@@ -496,6 +496,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::no_effect::NO_EFFECT_UNDERSCORE_BINDING_INFO,
     crate::no_effect::UNNECESSARY_OPERATION_INFO,
     crate::no_mangle_with_rust_abi::NO_MANGLE_WITH_RUST_ABI_INFO,
+    crate::non_canonical_impls::NON_CANONICAL_CLONE_IMPL_INFO,
+    crate::non_canonical_impls::NON_CANONICAL_PARTIAL_ORD_IMPL_INFO,
     crate::non_copy_const::BORROW_INTERIOR_MUTABLE_CONST_INFO,
     crate::non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST_INFO,
     crate::non_expressive_names::JUST_UNDERSCORES_AND_DIGITS_INFO,
diff --git a/src/tools/clippy/clippy_lints/src/default_union_representation.rs b/src/tools/clippy/clippy_lints/src/default_union_representation.rs
index 03b5a2d6d08..bbce6e1dd8f 100644
--- a/src/tools/clippy/clippy_lints/src/default_union_representation.rs
+++ b/src/tools/clippy/clippy_lints/src/default_union_representation.rs
@@ -69,6 +69,9 @@ impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation {
 }
 
 /// Returns true if the given item is a union with at least two non-ZST fields.
+/// (ZST fields having an arbitrary offset is completely inconsequential, and
+/// if there is only one field left after ignoring ZST fields then the offset
+/// of that field does not matter either.)
 fn is_union_with_two_non_zst_fields(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
     if let ItemKind::Union(data, _) = &item.kind {
         data.fields().iter().filter(|f| !is_zst(cx, f.ty)).count() >= 2
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index 58c27855000..8090f821d1f 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -3,7 +3,7 @@ use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exact
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::sugg::has_enclosing_paren;
-use clippy_utils::ty::{is_copy, peel_mid_ty_refs};
+use clippy_utils::ty::{implements_trait, is_copy, peel_mid_ty_refs};
 use clippy_utils::{
     expr_use_ctxt, get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode,
 };
@@ -33,7 +33,6 @@ use rustc_middle::ty::{
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol::sym;
 use rustc_span::{Span, Symbol};
-use rustc_trait_selection::infer::InferCtxtExt as _;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
 use rustc_trait_selection::traits::{Obligation, ObligationCause};
 use std::collections::VecDeque;
@@ -452,13 +451,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
                                         // Trait methods taking `self`
                                         arg_ty
                                     } && impl_ty.is_ref()
-                                    && cx.tcx.infer_ctxt().build()
-                                        .type_implements_trait(
-                                            trait_id,
-                                            [impl_ty.into()].into_iter().chain(args.iter().copied()),
-                                            cx.param_env,
-                                        )
-                                        .must_apply_modulo_regions()
+                                    && implements_trait(
+                                        cx,
+                                        impl_ty,
+                                        trait_id,
+                                        &args[..cx.tcx.generics_of(trait_id).params.len() - 1],
+                                    )
                                 {
                                     false
                                 } else {
@@ -609,12 +607,14 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
                             adjusted_ty,
                         },
                     ));
-                } else if stability.is_deref_stable() {
+                } else if stability.is_deref_stable()
+                    && let Some(parent) = get_parent_expr(cx, expr)
+                {
                     self.state = Some((
                         State::ExplicitDeref { mutability: None },
                         StateData {
-                            span: expr.span,
-                            hir_id: expr.hir_id,
+                            span: parent.span,
+                            hir_id: parent.hir_id,
                             adjusted_ty,
                         },
                     ));
@@ -1399,6 +1399,13 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
                 return;
             }
 
+            if let ExprKind::Field(parent_expr, _) = expr.kind
+                && let ty::Adt(adt, _) = cx.typeck_results().expr_ty(parent_expr).kind()
+                && adt.is_union()
+            {
+                // Auto deref does not apply on union field
+                return;
+            }
             span_lint_hir_and_then(
                 cx,
                 EXPLICIT_AUTO_DEREF,
diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
index 9a85cc4ce2d..d2bfc4f8e27 100644
--- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
@@ -217,8 +217,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
             if let &Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind();
             if let attrs = cx.tcx.hir().attrs(item.hir_id());
             if !attrs.iter().any(|attr| attr.doc_str().is_some());
-            if let child_attrs = cx.tcx.hir().attrs(impl_item_hir);
-            if !child_attrs.iter().any(|attr| attr.doc_str().is_some());
+            if cx.tcx.hir().attrs(impl_item_hir).is_empty();
 
             then {
                 if adt_def.is_struct() {
diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs
index d3311792cfa..2bdac1352dc 100644
--- a/src/tools/clippy/clippy_lints/src/derive.rs
+++ b/src/tools/clippy/clippy_lints/src/derive.rs
@@ -343,7 +343,7 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h
     // If the current self type doesn't implement Copy (due to generic constraints), search to see if
     // there's a Copy impl for any instance of the adt.
     if !is_copy(cx, ty) {
-        if ty_subs.non_erasable_generics().next().is_some() {
+        if ty_subs.non_erasable_generics(cx.tcx, ty_adt.did()).next().is_some() {
             let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(&copy_id).map_or(false, |impls| {
                 impls.iter().any(|&id| {
                     matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _)
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
index 1971cab64ef..7469f813ef8 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
@@ -1,8 +1,9 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::macro_backtrace;
+use rustc_ast::Attribute;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def_id::DefIdMap;
-use rustc_hir::{Expr, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty};
+use rustc_hir::{Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{ExpnId, Span};
@@ -111,6 +112,10 @@ impl LateLintPass<'_> for DisallowedMacros {
 
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
         self.check(cx, expr.span);
+        // `$t + $t` can have the context of $t, check also the span of the binary operator
+        if let ExprKind::Binary(op, ..) = expr.kind {
+            self.check(cx, op.span);
+        }
     }
 
     fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) {
@@ -147,4 +152,8 @@ impl LateLintPass<'_> for DisallowedMacros {
     fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, _: HirId) {
         self.check(cx, path.span);
     }
+
+    fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) {
+        self.check(cx, attr.span);
+    }
 }
diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs
index e29ab634c97..bf2add6aa64 100644
--- a/src/tools/clippy/clippy_lints/src/doc.rs
+++ b/src/tools/clippy/clippy_lints/src/doc.rs
@@ -1,18 +1,16 @@
 use clippy_utils::attrs::is_doc_hidden;
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_then};
 use clippy_utils::macros::{is_panic, root_macro_call_first_node};
-use clippy_utils::source::{first_line_of_span, snippet_with_applicability};
+use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty};
 use if_chain::if_chain;
-use itertools::Itertools;
 use pulldown_cmark::Event::{
     Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
 };
 use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph};
 use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options};
-use rustc_ast::ast::{Async, AttrKind, Attribute, Fn, FnRetTy, ItemKind};
-use rustc_ast::token::CommentKind;
+use rustc_ast::ast::{Async, Attribute, Fn, FnRetTy, ItemKind};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::emitter::EmitterWriter;
@@ -26,6 +24,9 @@ use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty;
 use rustc_parse::maybe_new_parser_from_source_str;
 use rustc_parse::parser::ForceCollect;
+use rustc_resolve::rustdoc::{
+    add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range, DocFragment,
+};
 use rustc_session::parse::ParseSess;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::edition::Edition;
@@ -450,53 +451,16 @@ fn lint_for_missing_headers(
     }
 }
 
-/// Cleanup documentation decoration.
-///
-/// We can't use `rustc_ast::attr::AttributeMethods::with_desugared_doc` or
-/// `rustc_ast::parse::lexer::comments::strip_doc_comment_decoration` because we
-/// need to keep track of
-/// the spans but this function is inspired from the later.
-#[expect(clippy::cast_possible_truncation)]
-#[must_use]
-pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: Span) -> (String, Vec<(usize, Span)>) {
-    // one-line comments lose their prefix
-    if comment_kind == CommentKind::Line {
-        let mut doc = doc.to_owned();
-        doc.push('\n');
-        let len = doc.len();
-        // +3 skips the opening delimiter
-        return (doc, vec![(len, span.with_lo(span.lo() + BytePos(3)))]);
-    }
+#[derive(Copy, Clone)]
+struct Fragments<'a> {
+    doc: &'a str,
+    fragments: &'a [DocFragment],
+}
 
-    let mut sizes = vec![];
-    let mut contains_initial_stars = false;
-    for line in doc.lines() {
-        let offset = line.as_ptr() as usize - doc.as_ptr() as usize;
-        debug_assert_eq!(offset as u32 as usize, offset);
-        contains_initial_stars |= line.trim_start().starts_with('*');
-        // +1 adds the newline, +3 skips the opening delimiter
-        sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(3 + offset as u32))));
-    }
-    if !contains_initial_stars {
-        return (doc.to_string(), sizes);
-    }
-    // remove the initial '*'s if any
-    let mut no_stars = String::with_capacity(doc.len());
-    for line in doc.lines() {
-        let mut chars = line.chars();
-        for c in &mut chars {
-            if c.is_whitespace() {
-                no_stars.push(c);
-            } else {
-                no_stars.push(if c == '*' { ' ' } else { c });
-                break;
-            }
-        }
-        no_stars.push_str(chars.as_str());
-        no_stars.push('\n');
+impl Fragments<'_> {
+    fn span(self, cx: &LateContext<'_>, range: Range<usize>) -> Option<Span> {
+        source_span_for_markdown_range(cx.tcx, &self.doc, &range, &self.fragments)
     }
-
-    (no_stars, sizes)
 }
 
 #[derive(Copy, Clone, Default)]
@@ -508,34 +472,23 @@ struct DocHeaders {
 
 fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[Attribute]) -> Option<DocHeaders> {
     /// We don't want the parser to choke on intra doc links. Since we don't
-    /// actually care about rendering them, just pretend that all broken links are
+    /// actually care about rendering them, just pretend that all broken links
     /// point to a fake address.
     #[expect(clippy::unnecessary_wraps)] // we're following a type signature
     fn fake_broken_link_callback<'a>(_: BrokenLink<'_>) -> Option<(CowStr<'a>, CowStr<'a>)> {
         Some(("fake".into(), "fake".into()))
     }
 
-    let mut doc = String::new();
-    let mut spans = vec![];
-
-    for attr in attrs {
-        if let AttrKind::DocComment(comment_kind, comment) = attr.kind {
-            let (comment, current_spans) = strip_doc_comment_decoration(comment.as_str(), comment_kind, attr.span);
-            spans.extend_from_slice(&current_spans);
-            doc.push_str(&comment);
-        } else if attr.has_name(sym::doc) {
-            // ignore mix of sugared and non-sugared doc
-            // don't trigger the safety or errors check
-            return None;
-        }
+    if is_doc_hidden(attrs) {
+        return None;
     }
 
-    let mut current = 0;
-    for &mut (ref mut offset, _) in &mut spans {
-        let offset_copy = *offset;
-        *offset = current;
-        current += offset_copy;
+    let (fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true);
+    let mut doc = String::new();
+    for fragment in &fragments {
+        add_doc_fragment(&mut doc, fragment);
     }
+    doc.pop();
 
     if doc.is_empty() {
         return Some(DocHeaders::default());
@@ -543,23 +496,19 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
 
     let mut cb = fake_broken_link_callback;
 
-    let parser =
-        pulldown_cmark::Parser::new_with_broken_link_callback(&doc, Options::empty(), Some(&mut cb)).into_offset_iter();
-    // Iterate over all `Events` and combine consecutive events into one
-    let events = parser.coalesce(|previous, current| {
-        let previous_range = previous.1;
-        let current_range = current.1;
-
-        match (previous.0, current.0) {
-            (Text(previous), Text(current)) => {
-                let mut previous = previous.to_string();
-                previous.push_str(&current);
-                Ok((Text(previous.into()), previous_range))
-            },
-            (previous, current) => Err(((previous, previous_range), (current, current_range))),
-        }
-    });
-    Some(check_doc(cx, valid_idents, events, &spans))
+    // disable smart punctuation to pick up ['link'] more easily
+    let opts = main_body_opts() - Options::ENABLE_SMART_PUNCTUATION;
+    let parser = pulldown_cmark::Parser::new_with_broken_link_callback(&doc, opts, Some(&mut cb));
+
+    Some(check_doc(
+        cx,
+        valid_idents,
+        parser.into_offset_iter(),
+        Fragments {
+            fragments: &fragments,
+            doc: &doc,
+        },
+    ))
 }
 
 const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"];
@@ -568,7 +517,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
     cx: &LateContext<'_>,
     valid_idents: &FxHashSet<String>,
     events: Events,
-    spans: &[(usize, Span)],
+    fragments: Fragments<'_>,
 ) -> DocHeaders {
     // true if a safety header was found
     let mut headers = DocHeaders::default();
@@ -579,8 +528,8 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
     let mut no_test = false;
     let mut edition = None;
     let mut ticks_unbalanced = false;
-    let mut text_to_check: Vec<(CowStr<'_>, Span)> = Vec::new();
-    let mut paragraph_span = spans.get(0).expect("function isn't called if doc comment is empty").1;
+    let mut text_to_check: Vec<(CowStr<'_>, Range<usize>)> = Vec::new();
+    let mut paragraph_range = 0..0;
     for (event, range) in events {
         match event {
             Start(CodeBlock(ref kind)) => {
@@ -613,25 +562,28 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
                     in_heading = true;
                 }
                 ticks_unbalanced = false;
-                let (_, span) = get_current_span(spans, range.start);
-                paragraph_span = first_line_of_span(cx, span);
+                paragraph_range = range;
             },
             End(Heading(_, _, _) | Paragraph | Item) => {
                 if let End(Heading(_, _, _)) = event {
                     in_heading = false;
                 }
-                if ticks_unbalanced {
+                if ticks_unbalanced
+                    && let Some(span) = fragments.span(cx, paragraph_range.clone())
+                {
                     span_lint_and_help(
                         cx,
                         DOC_MARKDOWN,
-                        paragraph_span,
+                        span,
                         "backticks are unbalanced",
                         None,
                         "a backtick may be missing a pair",
                     );
                 } else {
-                    for (text, span) in text_to_check {
-                        check_text(cx, valid_idents, &text, span);
+                    for (text, range) in text_to_check {
+                        if let Some(span) = fragments.span(cx, range) {
+                            check_text(cx, valid_idents, &text, span);
+                        }
                     }
                 }
                 text_to_check = Vec::new();
@@ -640,8 +592,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
             Html(_html) => (),             // HTML is weird, just ignore it
             SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (),
             FootnoteReference(text) | Text(text) => {
-                let (begin, span) = get_current_span(spans, range.start);
-                paragraph_span = paragraph_span.with_hi(span.hi());
+                paragraph_range.end = range.end;
                 ticks_unbalanced |= text.contains('`') && !in_code;
                 if Some(&text) == in_link.as_ref() || ticks_unbalanced {
                     // Probably a link of the form `<http://example.com>`
@@ -658,19 +609,19 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
                 if in_code {
                     if is_rust && !no_test {
                         let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition());
-                        check_code(cx, &text, edition, span);
+                        check_code(cx, &text, edition, range.clone(), fragments);
                     }
                 } else {
-                    check_link_quotes(cx, in_link.is_some(), trimmed_text, span, &range, begin, text.len());
-                    // Adjust for the beginning of the current `Event`
-                    let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
+                    if in_link.is_some() {
+                        check_link_quotes(cx, trimmed_text, range.clone(), fragments);
+                    }
                     if let Some(link) = in_link.as_ref()
                       && let Ok(url) = Url::parse(link)
                       && (url.scheme() == "https" || url.scheme() == "http") {
                         // Don't check the text associated with external URLs
                         continue;
                     }
-                    text_to_check.push((text, span));
+                    text_to_check.push((text, range));
                 }
             },
         }
@@ -678,36 +629,21 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
     headers
 }
 
-fn check_link_quotes(
-    cx: &LateContext<'_>,
-    in_link: bool,
-    trimmed_text: &str,
-    span: Span,
-    range: &Range<usize>,
-    begin: usize,
-    text_len: usize,
-) {
-    if in_link && trimmed_text.starts_with('\'') && trimmed_text.ends_with('\'') {
-        // fix the span to only point at the text within the link
-        let lo = span.lo() + BytePos::from_usize(range.start - begin);
+fn check_link_quotes(cx: &LateContext<'_>, trimmed_text: &str, range: Range<usize>, fragments: Fragments<'_>) {
+    if trimmed_text.starts_with('\'')
+        && trimmed_text.ends_with('\'')
+        && let Some(span) = fragments.span(cx, range)
+    {
         span_lint(
             cx,
             DOC_LINK_WITH_QUOTES,
-            span.with_lo(lo).with_hi(lo + BytePos::from_usize(text_len)),
+            span,
             "possible intra-doc link using quotes instead of backticks",
         );
     }
 }
 
-fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) {
-    let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) {
-        Ok(o) => o,
-        Err(e) => e - 1,
-    };
-    spans[index]
-}
-
-fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
+fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, range: Range<usize>, fragments: Fragments<'_>) {
     fn has_needless_main(code: String, edition: Edition) -> bool {
         rustc_driver::catch_fatal_errors(|| {
             rustc_span::create_session_globals_then(edition, || {
@@ -774,12 +710,13 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
         .unwrap_or_default()
     }
 
+    let trailing_whitespace = text.len() - text.trim_end().len();
+
     // Because of the global session, we need to create a new session in a different thread with
     // the edition we need.
     let text = text.to_owned();
-    if thread::spawn(move || has_needless_main(text, edition))
-        .join()
-        .expect("thread::spawn failed")
+    if thread::spawn(move || has_needless_main(text, edition)).join().expect("thread::spawn failed")
+        && let Some(span) = fragments.span(cx, range.start..range.end - trailing_whitespace)
     {
         span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
     }
diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
index ab6ad3f3b3a..e2d19e24557 100644
--- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
+++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
@@ -6,7 +6,7 @@ use clippy_utils::sugg::Sugg;
 use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks};
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
-use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
+use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
             && then_expr.span.ctxt() == ctxt
             && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome)
             && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone)
-            && !stmts_contains_early_return(then_block.stmts)
+            && !contains_return(then_block.stmts)
         {
             let mut app = Applicability::Unspecified;
             let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app).maybe_par().to_string();
@@ -116,17 +116,3 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
 
     extract_msrv_attr!(LateContext);
 }
-
-fn stmts_contains_early_return(stmts: &[Stmt<'_>]) -> bool {
-    stmts.iter().any(|stmt| {
-        let Stmt {
-            kind: StmtKind::Semi(e),
-            ..
-        } = stmt
-        else {
-            return false;
-        };
-
-        contains_return(e)
-    })
-}
diff --git a/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs
index c635120b882..d8ead1c9d9f 100644
--- a/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use hir::PatKind;
+use hir::{Node, PatKind};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
@@ -37,6 +37,17 @@ declare_lint_pass!(IgnoredUnitPatterns => [IGNORED_UNIT_PATTERNS]);
 
 impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns {
     fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx hir::Pat<'tcx>) {
+        match cx.tcx.hir().get_parent(pat.hir_id) {
+            Node::Param(param) if matches!(cx.tcx.hir().get_parent(param.hir_id), Node::Item(_)) => {
+                // Ignore function parameters
+                return;
+            },
+            Node::Local(local) if local.ty.is_some() => {
+                // Ignore let bindings with explicit type
+                return;
+            },
+            _ => {},
+        }
         if matches!(pat.kind, PatKind::Wild) && cx.typeck_results().pat_ty(pat).is_unit() {
             span_lint_and_sugg(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
index c6d1acad3a0..ec9044bba5c 100644
--- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
 use rustc_errors::{Applicability, SuggestionStyle};
-use rustc_hir::def_id::LocalDefId;
+use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
     Body, FnDecl, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, ItemKind, TraitBoundModifier, TraitItem,
@@ -9,7 +9,7 @@ use rustc_hir::{
 };
 use rustc_hir_analysis::hir_ty_to_ty;
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{self, ClauseKind, TyCtxt};
+use rustc_middle::ty::{self, ClauseKind, Generics, Ty, TyCtxt};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::Span;
 
@@ -45,52 +45,169 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "1.73.0"]
     pub IMPLIED_BOUNDS_IN_IMPLS,
-    complexity,
+    nursery,
     "specifying bounds that are implied by other bounds in `impl Trait` type"
 }
 declare_lint_pass!(ImpliedBoundsInImpls => [IMPLIED_BOUNDS_IN_IMPLS]);
 
-/// This function tries to, for all type parameters in a supertype predicate `GenericTrait<U>`,
-/// check if the substituted type in the implied-by bound matches with what's subtituted in the
-/// implied type.
+#[allow(clippy::too_many_arguments)]
+fn emit_lint(
+    cx: &LateContext<'_>,
+    poly_trait: &rustc_hir::PolyTraitRef<'_>,
+    opaque_ty: &rustc_hir::OpaqueTy<'_>,
+    index: usize,
+    // The bindings that were implied
+    implied_bindings: &[rustc_hir::TypeBinding<'_>],
+    // The original bindings that `implied_bindings` are implied from
+    implied_by_bindings: &[rustc_hir::TypeBinding<'_>],
+    implied_by_args: &[GenericArg<'_>],
+    implied_by_span: Span,
+) {
+    let implied_by = snippet(cx, implied_by_span, "..");
+
+    span_lint_and_then(
+        cx,
+        IMPLIED_BOUNDS_IN_IMPLS,
+        poly_trait.span,
+        &format!("this bound is already specified as the supertrait of `{implied_by}`"),
+        |diag| {
+            // If we suggest removing a bound, we may also need to extend the span
+            // to include the `+` token that is ahead or behind,
+            // so we don't end up with something like `impl + B` or `impl A + `
+
+            let implied_span_extended = if let Some(next_bound) = opaque_ty.bounds.get(index + 1) {
+                poly_trait.span.to(next_bound.span().shrink_to_lo())
+            } else if index > 0
+                && let Some(prev_bound) = opaque_ty.bounds.get(index - 1)
+            {
+                prev_bound.span().shrink_to_hi().to(poly_trait.span.shrink_to_hi())
+            } else {
+                poly_trait.span
+            };
+
+            let mut sugg = vec![(implied_span_extended, String::new())];
+
+            // We also might need to include associated type binding that were specified in the implied bound,
+            // but omitted in the implied-by bound:
+            // `fn f() -> impl Deref<Target = u8> + DerefMut`
+            // If we're going to suggest removing `Deref<..>`, we'll need to put `<Target = u8>` on `DerefMut`
+            let omitted_assoc_tys: Vec<_> = implied_bindings
+                .iter()
+                .filter(|binding| !implied_by_bindings.iter().any(|b| b.ident == binding.ident))
+                .collect();
+
+            if !omitted_assoc_tys.is_empty() {
+                // `<>` needs to be added if there aren't yet any generic arguments or bindings
+                let needs_angle_brackets = implied_by_args.is_empty() && implied_by_bindings.is_empty();
+                let insert_span = match (implied_by_args, implied_by_bindings) {
+                    ([.., arg], [.., binding]) => arg.span().max(binding.span).shrink_to_hi(),
+                    ([.., arg], []) => arg.span().shrink_to_hi(),
+                    ([], [.., binding]) => binding.span.shrink_to_hi(),
+                    ([], []) => implied_by_span.shrink_to_hi(),
+                };
+
+                let mut associated_tys_sugg = if needs_angle_brackets {
+                    "<".to_owned()
+                } else {
+                    // If angle brackets aren't needed (i.e., there are already generic arguments or bindings),
+                    // we need to add a comma:
+                    // `impl A<B, C >`
+                    //             ^ if we insert `Assoc=i32` without a comma here, that'd be invalid syntax:
+                    // `impl A<B, C Assoc=i32>`
+                    ", ".to_owned()
+                };
+
+                for (index, binding) in omitted_assoc_tys.into_iter().enumerate() {
+                    if index > 0 {
+                        associated_tys_sugg += ", ";
+                    }
+                    associated_tys_sugg += &snippet(cx, binding.span, "..");
+                }
+                if needs_angle_brackets {
+                    associated_tys_sugg += ">";
+                }
+                sugg.push((insert_span, associated_tys_sugg));
+            }
+
+            diag.multipart_suggestion_with_style(
+                "try removing this bound",
+                sugg,
+                Applicability::MachineApplicable,
+                SuggestionStyle::ShowAlways,
+            );
+        },
+    );
+}
+
+/// Tries to "resolve" a type.
+/// The index passed to this function must start with `Self=0`, i.e. it must be a valid
+/// type parameter index.
+/// If the index is out of bounds, it means that the generic parameter has a default type.
+fn try_resolve_type<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    args: &'tcx [GenericArg<'tcx>],
+    generics: &'tcx Generics,
+    index: usize,
+) -> Option<Ty<'tcx>> {
+    match args.get(index - 1) {
+        Some(GenericArg::Type(ty)) => Some(hir_ty_to_ty(tcx, ty)),
+        Some(_) => None,
+        None => Some(tcx.type_of(generics.params[index].def_id).skip_binder()),
+    }
+}
+
+/// This function tries to, for all generic type parameters in a supertrait predicate `trait ...<U>:
+/// GenericTrait<U>`, check if the substituted type in the implied-by bound matches with what's
+/// subtituted in the implied bound.
 ///
 /// Consider this example.
 /// ```rust,ignore
 /// trait GenericTrait<T> {}
 /// trait GenericSubTrait<T, U, V>: GenericTrait<U> {}
-///                                              ^ trait_predicate_args: [Self#0, U#2]
+///                                 ^^^^^^^^^^^^^^^ trait_predicate_args: [Self#0, U#2]
+///                                                 (the Self#0 is implicit: `<Self as GenericTrait<U>>`)
 /// impl GenericTrait<i32> for () {}
 /// impl GenericSubTrait<(), i32, ()> for () {}
-/// impl GenericSubTrait<(), [u8; 8], ()> for () {}
+/// impl GenericSubTrait<(), i64, ()> for () {}
 ///
-/// fn f() -> impl GenericTrait<i32> + GenericSubTrait<(), [u8; 8], ()> {
-///                             ^^^ implied_args       ^^^^^^^^^^^^^^^ implied_by_args
-///                                                                    (we are interested in `[u8; 8]` specifically, as that
-///                                                                     is what `U` in `GenericTrait<U>` is substituted with)
-///     ()
+/// fn f() -> impl GenericTrait<i32> + GenericSubTrait<(), i64, ()> {
+///                             ^^^ implied_args       ^^^^^^^^^^^ implied_by_args
+///                                                                (we are interested in `i64` specifically, as that
+///                                                                 is what `U` in `GenericTrait<U>` is substituted with)
 /// }
 /// ```
-/// Here i32 != [u8; 8], so this will return false.
-fn is_same_generics(
-    tcx: TyCtxt<'_>,
-    trait_predicate_args: &[ty::GenericArg<'_>],
-    implied_by_args: &[GenericArg<'_>],
-    implied_args: &[GenericArg<'_>],
+/// Here i32 != i64, so this will return false.
+fn is_same_generics<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    trait_predicate_args: &'tcx [ty::GenericArg<'tcx>],
+    implied_by_args: &'tcx [GenericArg<'tcx>],
+    implied_args: &'tcx [GenericArg<'tcx>],
+    implied_by_def_id: DefId,
+    implied_def_id: DefId,
 ) -> bool {
+    // Get the generics of the two traits to be able to get default generic parameter.
+    let implied_by_generics = tcx.generics_of(implied_by_def_id);
+    let implied_generics = tcx.generics_of(implied_def_id);
+
     trait_predicate_args
         .iter()
         .enumerate()
         .skip(1) // skip `Self` implicit arg
         .all(|(arg_index, arg)| {
-            if let Some(ty) = arg.as_type()
-                && let &ty::Param(ty::ParamTy{ index, .. }) = ty.kind()
-                // Since `trait_predicate_args` and type params in traits start with `Self=0`
-                // and generic argument lists `GenericTrait<i32>` don't have `Self`,
-                // we need to subtract 1 from the index.
-                && let GenericArg::Type(ty_a) = implied_by_args[index as usize - 1]
-                && let GenericArg::Type(ty_b) = implied_args[arg_index - 1]
-            {
-                hir_ty_to_ty(tcx, ty_a) == hir_ty_to_ty(tcx, ty_b)
+            if let Some(ty) = arg.as_type() {
+                if let &ty::Param(ty::ParamTy { index, .. }) = ty.kind()
+                    // `index == 0` means that it's referring to `Self`,
+                    // in which case we don't try to substitute it
+                    && index != 0
+                    && let Some(ty_a) = try_resolve_type(tcx, implied_by_args, implied_by_generics, index as usize)
+                    && let Some(ty_b) = try_resolve_type(tcx, implied_args, implied_generics, arg_index)
+                {
+                    ty_a == ty_b
+                } else if let Some(ty_b) = try_resolve_type(tcx, implied_args, implied_generics, arg_index) {
+                    ty == ty_b
+                } else {
+                    false
+                }
             } else {
                 false
             }
@@ -121,7 +238,7 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
                 && let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates
                 && !predicates.is_empty() // If the trait has no supertrait, there is nothing to add.
             {
-                Some((bound.span(), path.args.map_or([].as_slice(), |a| a.args), predicates))
+                Some((bound.span(), path, predicates, trait_def_id))
             } else {
                 None
             }
@@ -134,43 +251,42 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
             if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound
                 && let [.., path] = poly_trait.trait_ref.path.segments
                 && let implied_args = path.args.map_or([].as_slice(), |a| a.args)
+                && let implied_bindings = path.args.map_or([].as_slice(), |a| a.bindings)
                 && let Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id()
-                && let Some(implied_by_span) = implied_bounds.iter().find_map(|&(span, implied_by_args, preds)| {
-                    preds.iter().find_map(|(clause, _)| {
-                        if let ClauseKind::Trait(tr) = clause.kind().skip_binder()
-                            && tr.def_id() == def_id
-                            && is_same_generics(cx.tcx, tr.trait_ref.args, implied_by_args, implied_args)
-                        {
-                            Some(span)
-                        } else {
-                            None
-                        }
+                && let Some((implied_by_span, implied_by_args, implied_by_bindings)) = implied_bounds
+                    .iter()
+                    .find_map(|&(span, implied_by_path, preds, implied_by_def_id)| {
+                        let implied_by_args = implied_by_path.args.map_or([].as_slice(), |a| a.args);
+                        let implied_by_bindings = implied_by_path.args.map_or([].as_slice(), |a| a.bindings);
+
+                        preds.iter().find_map(|(clause, _)| {
+                            if let ClauseKind::Trait(tr) = clause.kind().skip_binder()
+                                && tr.def_id() == def_id
+                                && is_same_generics(
+                                    cx.tcx,
+                                    tr.trait_ref.args,
+                                    implied_by_args,
+                                    implied_args,
+                                    implied_by_def_id,
+                                    def_id,
+                                )
+                            {
+                                Some((span, implied_by_args, implied_by_bindings))
+                            } else {
+                                None
+                            }
+                        })
                     })
-                })
             {
-                let implied_by = snippet(cx, implied_by_span, "..");
-                span_lint_and_then(
-                    cx, IMPLIED_BOUNDS_IN_IMPLS,
-                    poly_trait.span,
-                    &format!("this bound is already specified as the supertrait of `{implied_by}`"),
-                    |diag| {
-                        // If we suggest removing a bound, we may also need extend the span
-                        // to include the `+` token, so we don't end up with something like `impl + B`
-
-                        let implied_span_extended = if let Some(next_bound) = opaque_ty.bounds.get(index + 1) {
-                            poly_trait.span.to(next_bound.span().shrink_to_lo())
-                        } else {
-                            poly_trait.span
-                        };
-
-                        diag.span_suggestion_with_style(
-                            implied_span_extended,
-                            "try removing this bound",
-                            "",
-                            Applicability::MachineApplicable,
-                            SuggestionStyle::ShowAlways
-                        );
-                    }
+                emit_lint(
+                    cx,
+                    poly_trait,
+                    opaque_ty,
+                    index,
+                    implied_bindings,
+                    implied_by_bindings,
+                    implied_by_args,
+                    implied_by_span
                 );
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index ab71bfbdc58..f52614b6208 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -21,6 +21,7 @@
 
 // FIXME: switch to something more ergonomic here, once available.
 // (Currently there is no way to opt into sysroot crates without `extern crate`.)
+extern crate pulldown_cmark;
 extern crate rustc_arena;
 extern crate rustc_ast;
 extern crate rustc_ast_pretty;
@@ -37,6 +38,7 @@ extern crate rustc_lexer;
 extern crate rustc_lint;
 extern crate rustc_middle;
 extern crate rustc_parse;
+extern crate rustc_resolve;
 extern crate rustc_session;
 extern crate rustc_span;
 extern crate rustc_target;
@@ -154,7 +156,6 @@ mod implicit_saturating_add;
 mod implicit_saturating_sub;
 mod implied_bounds_in_impls;
 mod inconsistent_struct_constructor;
-mod incorrect_impls;
 mod index_refutable_slice;
 mod indexing_slicing;
 mod infinite_iter;
@@ -210,6 +211,7 @@ mod misc;
 mod misc_early;
 mod mismatching_type_param_order;
 mod missing_assert_message;
+mod missing_asserts_for_indexing;
 mod missing_const_for_fn;
 mod missing_doc;
 mod missing_enforced_import_rename;
@@ -243,6 +245,7 @@ mod neg_multiply;
 mod new_without_default;
 mod no_effect;
 mod no_mangle_with_rust_abi;
+mod non_canonical_impls;
 mod non_copy_const;
 mod non_expressive_names;
 mod non_octal_unix_permissions;
@@ -695,7 +698,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     });
     store.register_late_pass(|_| Box::<shadow::Shadow>::default());
     store.register_late_pass(|_| Box::new(unit_types::UnitTypes));
-    store.register_late_pass(move |_| Box::new(loops::Loops::new(msrv())));
+    let enforce_iter_loop_reborrow = conf.enforce_iter_loop_reborrow;
+    store.register_late_pass(move |_| Box::new(loops::Loops::new(msrv(), enforce_iter_loop_reborrow)));
     store.register_late_pass(|_| Box::<main_recursion::MainRecursion>::default());
     store.register_late_pass(|_| Box::new(lifetimes::Lifetimes));
     store.register_late_pass(|_| Box::new(entry::HashMapPass));
@@ -1068,7 +1072,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
             avoid_breaking_exported_api,
         ))
     });
-    store.register_late_pass(|_| Box::new(incorrect_impls::IncorrectImpls));
+    store.register_late_pass(|_| Box::new(non_canonical_impls::NonCanonicalImpls));
     store.register_late_pass(move |_| {
         Box::new(single_call_fn::SingleCallFn {
             avoid_breaking_exported_api,
@@ -1099,6 +1103,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns));
     store.register_late_pass(|_| Box::<reserve_after_initialization::ReserveAfterInitialization>::default());
     store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls));
+    store.register_late_pass(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
index 7b8c88235a9..6ab256ef001 100644
--- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
@@ -13,8 +13,14 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMut
 use rustc_middle::ty::{self, EarlyBinder, Ty, TypeAndMut};
 use rustc_span::sym;
 
-pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>, msrv: &Msrv) {
-    let Some((adjust, ty)) = is_ref_iterable(cx, self_arg, call_expr) else {
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    self_arg: &Expr<'_>,
+    call_expr: &Expr<'_>,
+    msrv: &Msrv,
+    enforce_iter_loop_reborrow: bool,
+) {
+    let Some((adjust, ty)) = is_ref_iterable(cx, self_arg, call_expr, enforce_iter_loop_reborrow) else {
         return;
     };
     if let ty::Array(_, count) = *ty.peel_refs().kind() {
@@ -102,6 +108,7 @@ fn is_ref_iterable<'tcx>(
     cx: &LateContext<'tcx>,
     self_arg: &Expr<'_>,
     call_expr: &Expr<'_>,
+    enforce_iter_loop_reborrow: bool,
 ) -> Option<(AdjustKind, Ty<'tcx>)> {
     let typeck = cx.typeck_results();
     if let Some(trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
@@ -142,7 +149,8 @@ fn is_ref_iterable<'tcx>(
                 {
                     return Some((AdjustKind::None, self_ty));
                 }
-            } else if let ty::Ref(region, ty, Mutability::Mut) = *self_ty.kind()
+            } else if enforce_iter_loop_reborrow
+                && let ty::Ref(region, ty, Mutability::Mut) = *self_ty.kind()
                 && let Some(mutbl) = mutbl
             {
                 // Attempt to reborrow the mutable reference
@@ -186,7 +194,8 @@ fn is_ref_iterable<'tcx>(
                 },
                 ..
             ] => {
-                if target != self_ty
+                if enforce_iter_loop_reborrow
+                    && target != self_ty
                     && implements_trait(cx, target, trait_id, &[])
                     && let Some(ty) =
                         make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target])
diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs
index ffd29ab7630..1fb16adad7a 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs
@@ -609,10 +609,14 @@ declare_clippy_lint! {
 
 pub struct Loops {
     msrv: Msrv,
+    enforce_iter_loop_reborrow: bool,
 }
 impl Loops {
-    pub fn new(msrv: Msrv) -> Self {
-        Self { msrv }
+    pub fn new(msrv: Msrv, enforce_iter_loop_reborrow: bool) -> Self {
+        Self {
+            msrv,
+            enforce_iter_loop_reborrow,
+        }
     }
 }
 impl_lint_pass!(Loops => [
@@ -719,7 +723,7 @@ impl Loops {
         if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind {
             match method.ident.as_str() {
                 "iter" | "iter_mut" => {
-                    explicit_iter_loop::check(cx, self_arg, arg, &self.msrv);
+                    explicit_iter_loop::check(cx, self_arg, arg, &self.msrv, self.enforce_iter_loop_reborrow);
                 },
                 "into_iter" => {
                     explicit_into_iter_loop::check(cx, self_arg, arg);
diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
index cc19ac55e5e..3d8a4cd948a 100644
--- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
@@ -1,13 +1,13 @@
 use super::utils::make_iterator_snippet;
 use super::NEVER_LOOP;
-use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::ForLoop;
+use clippy_utils::macros::root_macro_call_first_node;
 use clippy_utils::source::snippet;
 use rustc_errors::Applicability;
 use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind};
 use rustc_lint::LateContext;
-use rustc_span::Span;
+use rustc_span::{sym, Span};
 use std::iter::{once, Iterator};
 
 pub(super) fn check<'tcx>(
@@ -18,7 +18,7 @@ pub(super) fn check<'tcx>(
     for_loop: Option<&ForLoop<'_>>,
 ) {
     match never_loop_block(cx, block, &mut Vec::new(), loop_id) {
-        NeverLoopResult::AlwaysBreak => {
+        NeverLoopResult::Diverging => {
             span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| {
                 if let Some(ForLoop {
                     arg: iterator,
@@ -39,67 +39,76 @@ pub(super) fn check<'tcx>(
                 }
             });
         },
-        NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (),
-        NeverLoopResult::IgnoreUntilEnd(_) => unreachable!(),
+        NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Normal => (),
     }
 }
 
+/// The `never_loop` analysis keeps track of three things:
+///
+/// * Has any (reachable) code path hit a `continue` of the main loop?
+/// * Is the current code path diverging (that is, the next expression is not reachable)
+/// * For each block label `'a` inside the main loop, has any (reachable) code path encountered a
+///   `break 'a`?
+///
+/// The first two bits of information are in this enum, and the last part is in the
+/// `local_labels` variable, which contains a list of `(block_id, reachable)` pairs ordered by
+/// scope.
 #[derive(Copy, Clone)]
 enum NeverLoopResult {
-    // A break/return always get triggered but not necessarily for the main loop.
-    AlwaysBreak,
-    // A continue may occur for the main loop.
+    /// A continue may occur for the main loop.
     MayContinueMainLoop,
-    // Ignore everything until the end of the block with this id
-    IgnoreUntilEnd(HirId),
-    Otherwise,
+    /// We have not encountered any main loop continue,
+    /// but we are diverging (subsequent control flow is not reachable)
+    Diverging,
+    /// We have not encountered any main loop continue,
+    /// and subsequent control flow is (possibly) reachable
+    Normal,
 }
 
 #[must_use]
 fn absorb_break(arg: NeverLoopResult) -> NeverLoopResult {
     match arg {
-        NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise,
+        NeverLoopResult::Diverging | NeverLoopResult::Normal => NeverLoopResult::Normal,
         NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop,
-        NeverLoopResult::IgnoreUntilEnd(id) => NeverLoopResult::IgnoreUntilEnd(id),
     }
 }
 
 // Combine two results for parts that are called in order.
 #[must_use]
-fn combine_seq(first: NeverLoopResult, second: NeverLoopResult) -> NeverLoopResult {
+fn combine_seq(first: NeverLoopResult, second: impl FnOnce() -> NeverLoopResult) -> NeverLoopResult {
     match first {
-        NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop | NeverLoopResult::IgnoreUntilEnd(_) => {
-            first
-        },
-        NeverLoopResult::Otherwise => second,
+        NeverLoopResult::Diverging | NeverLoopResult::MayContinueMainLoop => first,
+        NeverLoopResult::Normal => second(),
+    }
+}
+
+// Combine an iterator of results for parts that are called in order.
+#[must_use]
+fn combine_seq_many(iter: impl IntoIterator<Item = NeverLoopResult>) -> NeverLoopResult {
+    for e in iter {
+        if let NeverLoopResult::Diverging | NeverLoopResult::MayContinueMainLoop = e {
+            return e;
+        }
     }
+    NeverLoopResult::Normal
 }
 
 // Combine two results where only one of the part may have been executed.
 #[must_use]
-fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult, ignore_ids: &[HirId]) -> NeverLoopResult {
+fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult {
     match (b1, b2) {
-        (NeverLoopResult::IgnoreUntilEnd(a), NeverLoopResult::IgnoreUntilEnd(b)) => {
-            if ignore_ids.iter().find(|&e| e == &a || e == &b).unwrap() == &a {
-                NeverLoopResult::IgnoreUntilEnd(b)
-            } else {
-                NeverLoopResult::IgnoreUntilEnd(a)
-            }
-        },
-        (i @ NeverLoopResult::IgnoreUntilEnd(_), NeverLoopResult::AlwaysBreak)
-        | (NeverLoopResult::AlwaysBreak, i @ NeverLoopResult::IgnoreUntilEnd(_)) => i,
-        (NeverLoopResult::AlwaysBreak, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
         (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
             NeverLoopResult::MayContinueMainLoop
         },
-        (NeverLoopResult::Otherwise, _) | (_, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise,
+        (NeverLoopResult::Normal, _) | (_, NeverLoopResult::Normal) => NeverLoopResult::Normal,
+        (NeverLoopResult::Diverging, NeverLoopResult::Diverging) => NeverLoopResult::Diverging,
     }
 }
 
 fn never_loop_block<'tcx>(
     cx: &LateContext<'tcx>,
     block: &Block<'tcx>,
-    ignore_ids: &mut Vec<HirId>,
+    local_labels: &mut Vec<(HirId, bool)>,
     main_loop_id: HirId,
 ) -> NeverLoopResult {
     let iter = block
@@ -107,15 +116,21 @@ fn never_loop_block<'tcx>(
         .iter()
         .filter_map(stmt_to_expr)
         .chain(block.expr.map(|expr| (expr, None)));
-
-    iter.map(|(e, els)| {
-        let e = never_loop_expr(cx, e, ignore_ids, main_loop_id);
+    combine_seq_many(iter.map(|(e, els)| {
+        let e = never_loop_expr(cx, e, local_labels, main_loop_id);
         // els is an else block in a let...else binding
         els.map_or(e, |els| {
-            combine_branches(e, never_loop_block(cx, els, ignore_ids, main_loop_id), ignore_ids)
+            combine_seq(e, || match never_loop_block(cx, els, local_labels, main_loop_id) {
+                // Returning MayContinueMainLoop here means that
+                // we will not evaluate the rest of the body
+                NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop,
+                // An else block always diverges, so the Normal case should not happen,
+                // but the analysis is approximate so it might return Normal anyway.
+                // Returning Normal here says that nothing more happens on the main path
+                NeverLoopResult::Diverging | NeverLoopResult::Normal => NeverLoopResult::Normal,
+            })
         })
-    })
-    .fold(NeverLoopResult::Otherwise, combine_seq)
+    }))
 }
 
 fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'tcx Block<'tcx>>)> {
@@ -131,76 +146,69 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'t
 fn never_loop_expr<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &Expr<'tcx>,
-    ignore_ids: &mut Vec<HirId>,
+    local_labels: &mut Vec<(HirId, bool)>,
     main_loop_id: HirId,
 ) -> NeverLoopResult {
-    match expr.kind {
+    let result = match expr.kind {
         ExprKind::Unary(_, e)
         | ExprKind::Cast(e, _)
         | ExprKind::Type(e, _)
         | ExprKind::Field(e, _)
         | ExprKind::AddrOf(_, _, e)
         | ExprKind::Repeat(e, _)
-        | ExprKind::DropTemps(e) => never_loop_expr(cx, e, ignore_ids, main_loop_id),
-        ExprKind::Let(let_expr) => never_loop_expr(cx, let_expr.init, ignore_ids, main_loop_id),
-        ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(cx, &mut es.iter(), ignore_ids, main_loop_id),
+        | ExprKind::DropTemps(e) => never_loop_expr(cx, e, local_labels, main_loop_id),
+        ExprKind::Let(let_expr) => never_loop_expr(cx, let_expr.init, local_labels, main_loop_id),
+        ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(cx, es.iter(), local_labels, main_loop_id),
         ExprKind::MethodCall(_, receiver, es, _) => never_loop_expr_all(
             cx,
-            &mut std::iter::once(receiver).chain(es.iter()),
-            ignore_ids,
+            std::iter::once(receiver).chain(es.iter()),
+            local_labels,
             main_loop_id,
         ),
         ExprKind::Struct(_, fields, base) => {
-            let fields = never_loop_expr_all(cx, &mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id);
+            let fields = never_loop_expr_all(cx, fields.iter().map(|f| f.expr), local_labels, main_loop_id);
             if let Some(base) = base {
-                combine_seq(fields, never_loop_expr(cx, base, ignore_ids, main_loop_id))
+                combine_seq(fields, || never_loop_expr(cx, base, local_labels, main_loop_id))
             } else {
                 fields
             }
         },
-        ExprKind::Call(e, es) => never_loop_expr_all(cx, &mut once(e).chain(es.iter()), ignore_ids, main_loop_id),
+        ExprKind::Call(e, es) => never_loop_expr_all(cx, once(e).chain(es.iter()), local_labels, main_loop_id),
         ExprKind::Binary(_, e1, e2)
         | ExprKind::Assign(e1, e2, _)
         | ExprKind::AssignOp(_, e1, e2)
-        | ExprKind::Index(e1, e2, _) => {
-            never_loop_expr_all(cx, &mut [e1, e2].iter().copied(), ignore_ids, main_loop_id)
-        },
+        | ExprKind::Index(e1, e2, _) => never_loop_expr_all(cx, [e1, e2].iter().copied(), local_labels, main_loop_id),
         ExprKind::Loop(b, _, _, _) => {
-            // Break can come from the inner loop so remove them.
-            absorb_break(never_loop_block(cx, b, ignore_ids, main_loop_id))
+            // We don't attempt to track reachability after a loop,
+            // just assume there may have been a break somewhere
+            absorb_break(never_loop_block(cx, b, local_labels, main_loop_id))
         },
         ExprKind::If(e, e2, e3) => {
-            let e1 = never_loop_expr(cx, e, ignore_ids, main_loop_id);
-            let e2 = never_loop_expr(cx, e2, ignore_ids, main_loop_id);
-            // If we know the `if` condition evaluates to `true`, don't check everything past it; it
-            // should just return whatever's evaluated for `e1` and `e2` since `e3` is unreachable
-            if let Some(Constant::Bool(true)) = constant(cx, cx.typeck_results(), e) {
-                return combine_seq(e1, e2);
-            }
-            let e3 = e3.as_ref().map_or(NeverLoopResult::Otherwise, |e| {
-                never_loop_expr(cx, e, ignore_ids, main_loop_id)
-            });
-            combine_seq(e1, combine_branches(e2, e3, ignore_ids))
+            let e1 = never_loop_expr(cx, e, local_labels, main_loop_id);
+            combine_seq(e1, || {
+                let e2 = never_loop_expr(cx, e2, local_labels, main_loop_id);
+                let e3 = e3.as_ref().map_or(NeverLoopResult::Normal, |e| {
+                    never_loop_expr(cx, e, local_labels, main_loop_id)
+                });
+                combine_branches(e2, e3)
+            })
         },
         ExprKind::Match(e, arms, _) => {
-            let e = never_loop_expr(cx, e, ignore_ids, main_loop_id);
-            if arms.is_empty() {
-                e
-            } else {
-                let arms = never_loop_expr_branch(cx, &mut arms.iter().map(|a| a.body), ignore_ids, main_loop_id);
-                combine_seq(e, arms)
-            }
+            let e = never_loop_expr(cx, e, local_labels, main_loop_id);
+            combine_seq(e, || {
+                arms.iter().fold(NeverLoopResult::Diverging, |a, b| {
+                    combine_branches(a, never_loop_expr(cx, b.body, local_labels, main_loop_id))
+                })
+            })
         },
         ExprKind::Block(b, l) => {
             if l.is_some() {
-                ignore_ids.push(b.hir_id);
-            }
-            let ret = never_loop_block(cx, b, ignore_ids, main_loop_id);
-            if l.is_some() {
-                ignore_ids.pop();
+                local_labels.push((b.hir_id, false));
             }
+            let ret = never_loop_block(cx, b, local_labels, main_loop_id);
+            let jumped_to = l.is_some() && local_labels.pop().unwrap().1;
             match ret {
-                NeverLoopResult::IgnoreUntilEnd(a) if a == b.hir_id => NeverLoopResult::Otherwise,
+                NeverLoopResult::Diverging if jumped_to => NeverLoopResult::Normal,
                 _ => ret,
             }
         },
@@ -211,74 +219,78 @@ fn never_loop_expr<'tcx>(
             if id == main_loop_id {
                 NeverLoopResult::MayContinueMainLoop
             } else {
-                NeverLoopResult::AlwaysBreak
+                NeverLoopResult::Diverging
             }
         },
-        // checks if break targets a block instead of a loop
-        ExprKind::Break(Destination { target_id: Ok(t), .. }, e) if ignore_ids.contains(&t) => e
-            .map_or(NeverLoopResult::IgnoreUntilEnd(t), |e| {
-                never_loop_expr(cx, e, ignore_ids, main_loop_id)
-            }),
-        ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
-            combine_seq(
-                never_loop_expr(cx, e, ignore_ids, main_loop_id),
-                NeverLoopResult::AlwaysBreak,
-            )
-        }),
-        ExprKind::Become(e) => combine_seq(
-            never_loop_expr(cx, e, ignore_ids, main_loop_id),
-            NeverLoopResult::AlwaysBreak,
-        ),
-        ExprKind::InlineAsm(asm) => asm
-            .operands
-            .iter()
-            .map(|(o, _)| match o {
-                InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => {
-                    never_loop_expr(cx, expr, ignore_ids, main_loop_id)
-                },
-                InlineAsmOperand::Out { expr, .. } => {
-                    never_loop_expr_all(cx, &mut expr.iter().copied(), ignore_ids, main_loop_id)
-                },
-                InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all(
-                    cx,
-                    &mut once(*in_expr).chain(out_expr.iter().copied()),
-                    ignore_ids,
-                    main_loop_id,
-                ),
-                InlineAsmOperand::Const { .. }
-                | InlineAsmOperand::SymFn { .. }
-                | InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise,
+        ExprKind::Break(_, e) | ExprKind::Ret(e) => {
+            let first = e.as_ref().map_or(NeverLoopResult::Normal, |e| {
+                never_loop_expr(cx, e, local_labels, main_loop_id)
+            });
+            combine_seq(first, || {
+                // checks if break targets a block instead of a loop
+                if let ExprKind::Break(Destination { target_id: Ok(t), .. }, _) = expr.kind {
+                    if let Some((_, reachable)) = local_labels.iter_mut().find(|(label, _)| *label == t) {
+                        *reachable = true;
+                    }
+                }
+                NeverLoopResult::Diverging
             })
-            .fold(NeverLoopResult::Otherwise, combine_seq),
+        },
+        ExprKind::Become(e) => combine_seq(never_loop_expr(cx, e, local_labels, main_loop_id), || {
+            NeverLoopResult::Diverging
+        }),
+        ExprKind::InlineAsm(asm) => combine_seq_many(asm.operands.iter().map(|(o, _)| match o {
+            InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => {
+                never_loop_expr(cx, expr, local_labels, main_loop_id)
+            },
+            InlineAsmOperand::Out { expr, .. } => {
+                never_loop_expr_all(cx, expr.iter().copied(), local_labels, main_loop_id)
+            },
+            InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all(
+                cx,
+                once(*in_expr).chain(out_expr.iter().copied()),
+                local_labels,
+                main_loop_id,
+            ),
+            InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => {
+                NeverLoopResult::Normal
+            },
+        })),
         ExprKind::OffsetOf(_, _)
         | ExprKind::Yield(_, _)
         | ExprKind::Closure { .. }
         | ExprKind::Path(_)
         | ExprKind::ConstBlock(_)
         | ExprKind::Lit(_)
-        | ExprKind::Err(_) => NeverLoopResult::Otherwise,
+        | ExprKind::Err(_) => NeverLoopResult::Normal,
+    };
+    let result = combine_seq(result, || {
+        if cx.typeck_results().expr_ty(expr).is_never() {
+            NeverLoopResult::Diverging
+        } else {
+            NeverLoopResult::Normal
+        }
+    });
+    if  let NeverLoopResult::Diverging = result &&
+        let Some(macro_call) = root_macro_call_first_node(cx, expr) &&
+        let Some(sym::todo_macro) = cx.tcx.get_diagnostic_name(macro_call.def_id)
+    {
+        // We return MayContinueMainLoop here because we treat `todo!()`
+        // as potentially containing any code, including a continue of the main loop.
+        // This effectively silences the lint whenever a loop contains this macro anywhere.
+        NeverLoopResult::MayContinueMainLoop
+    } else {
+        result
     }
 }
 
 fn never_loop_expr_all<'tcx, T: Iterator<Item = &'tcx Expr<'tcx>>>(
     cx: &LateContext<'tcx>,
-    es: &mut T,
-    ignore_ids: &mut Vec<HirId>,
-    main_loop_id: HirId,
-) -> NeverLoopResult {
-    es.map(|e| never_loop_expr(cx, e, ignore_ids, main_loop_id))
-        .fold(NeverLoopResult::Otherwise, combine_seq)
-}
-
-fn never_loop_expr_branch<'tcx, T: Iterator<Item = &'tcx Expr<'tcx>>>(
-    cx: &LateContext<'tcx>,
-    e: &mut T,
-    ignore_ids: &mut Vec<HirId>,
+    es: T,
+    local_labels: &mut Vec<(HirId, bool)>,
     main_loop_id: HirId,
 ) -> NeverLoopResult {
-    e.fold(NeverLoopResult::AlwaysBreak, |a, b| {
-        combine_branches(a, never_loop_expr(cx, b, ignore_ids, main_loop_id), ignore_ids)
-    })
+    combine_seq_many(es.map(|e| never_loop_expr(cx, e, local_labels, main_loop_id)))
 }
 
 fn for_to_if_let_sugg(cx: &LateContext<'_>, iterator: &Expr<'_>, pat: &Pat<'_>) -> String {
diff --git a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs
index 39d8b20d38d..90557b55560 100644
--- a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs
@@ -1,4 +1,5 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_opt;
 use rustc_ast::LitKind;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -6,6 +7,7 @@ use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd, UnOp};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{Span, DUMMY_SP};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -49,6 +51,29 @@ fn expr_as_i128(expr: &Expr<'_>) -> Option<i128> {
     }
 }
 
+#[derive(Copy, Clone)]
+struct Num {
+    val: i128,
+    span: Span,
+}
+
+impl Num {
+    fn new(expr: &Expr<'_>) -> Option<Self> {
+        Some(Self {
+            val: expr_as_i128(expr)?,
+            span: expr.span,
+        })
+    }
+
+    fn dummy(val: i128) -> Self {
+        Self { val, span: DUMMY_SP }
+    }
+
+    fn min(self, other: Self) -> Self {
+        if self.val < other.val { self } else { other }
+    }
+}
+
 impl LateLintPass<'_> for ManualRangePatterns {
     fn check_pat(&mut self, cx: &LateContext<'_>, pat: &'_ rustc_hir::Pat<'_>) {
         if in_external_macro(cx.sess(), pat.span) {
@@ -56,71 +81,83 @@ impl LateLintPass<'_> for ManualRangePatterns {
         }
 
         // a pattern like 1 | 2 seems fine, lint if there are at least 3 alternatives
+        // or at least one range
         if let PatKind::Or(pats) = pat.kind
-            && pats.len() >= 3
+            && (pats.len() >= 3 || pats.iter().any(|p| matches!(p.kind, PatKind::Range(..))))
         {
-            let mut min = i128::MAX;
-            let mut max = i128::MIN;
+            let mut min = Num::dummy(i128::MAX);
+            let mut max = Num::dummy(i128::MIN);
+            let mut range_kind = RangeEnd::Included;
             let mut numbers_found = FxHashSet::default();
             let mut ranges_found = Vec::new();
 
             for pat in pats {
                 if let PatKind::Lit(lit) = pat.kind
-                    && let Some(num) = expr_as_i128(lit)
+                    && let Some(num) = Num::new(lit)
                 {
-                    numbers_found.insert(num);
+                    numbers_found.insert(num.val);
 
                     min = min.min(num);
-                    max = max.max(num);
+                    if num.val >= max.val {
+                        max = num;
+                        range_kind = RangeEnd::Included;
+                    }
                 } else if let PatKind::Range(Some(left), Some(right), end) = pat.kind
-                    && let Some(left) = expr_as_i128(left)
-                    && let Some(right) = expr_as_i128(right)
-                    && right >= left
+                    && let Some(left) = Num::new(left)
+                    && let Some(mut right) = Num::new(right)
                 {
+                    if let RangeEnd::Excluded = end {
+                        right.val -= 1;
+                    }
+
                     min = min.min(left);
-                    max = max.max(right);
-                    ranges_found.push(left..=match end {
-                        RangeEnd::Included => right,
-                        RangeEnd::Excluded => right - 1,
-                    });
+                    if right.val > max.val {
+                        max = right;
+                        range_kind = end;
+                    }
+                    ranges_found.push(left.val..=right.val);
                 } else {
                     return;
                 }
             }
 
-            let contains_whole_range = 'contains: {
-                let mut num = min;
-                while num <= max {
-                    if numbers_found.contains(&num) {
-                        num += 1;
-                    }
-                    // Given a list of (potentially overlapping) ranges like:
-                    // 1..=5, 3..=7, 6..=10
-                    // We want to find the range with the highest end that still contains the current number
-                    else if let Some(range) = ranges_found
-                        .iter()
-                        .filter(|range| range.contains(&num))
-                        .max_by_key(|range| range.end())
-                    {
-                        num = range.end() + 1;
-                    } else {
-                        break 'contains false;
-                    }
+            let mut num = min.val;
+            while num <= max.val {
+                if numbers_found.contains(&num) {
+                    num += 1;
+                }
+                // Given a list of (potentially overlapping) ranges like:
+                // 1..=5, 3..=7, 6..=10
+                // We want to find the range with the highest end that still contains the current number
+                else if let Some(range) = ranges_found
+                    .iter()
+                    .filter(|range| range.contains(&num))
+                    .max_by_key(|range| range.end())
+                {
+                    num = range.end() + 1;
+                } else {
+                    return;
                 }
-                break 'contains true;
-            };
-
-            if contains_whole_range {
-                span_lint_and_sugg(
-                    cx,
-                    MANUAL_RANGE_PATTERNS,
-                    pat.span,
-                    "this OR pattern can be rewritten using a range",
-                    "try",
-                    format!("{min}..={max}"),
-                    Applicability::MachineApplicable,
-                );
             }
+
+            span_lint_and_then(
+                cx,
+                MANUAL_RANGE_PATTERNS,
+                pat.span,
+                "this OR pattern can be rewritten using a range",
+                |diag| {
+                    if let Some(min) = snippet_opt(cx, min.span)
+                        && let Some(max) = snippet_opt(cx, max.span)
+                    {
+                        diag.span_suggestion(
+                            pat.span,
+                            "try",
+                            format!("{min}{range_kind}{max}"),
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                },
+            );
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs
index 8be3c178a29..6e13148f2fc 100644
--- a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs
@@ -37,22 +37,14 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
                         Some(lhs) => constant(cx, cx.typeck_results(), lhs)?,
                         None => {
                             let min_val_const = ty.numeric_min_val(cx.tcx)?;
-                            let min_constant = mir::ConstantKind::from_value(
-                                cx.tcx.valtree_to_const_val((ty, min_val_const.to_valtree())),
-                                ty,
-                            );
-                            miri_to_const(cx, min_constant)?
+                            miri_to_const(cx, mir::ConstantKind::from_ty_const(min_val_const, cx.tcx))?
                         },
                     };
                     let rhs_const = match rhs {
                         Some(rhs) => constant(cx, cx.typeck_results(), rhs)?,
                         None => {
                             let max_val_const = ty.numeric_max_val(cx.tcx)?;
-                            let max_constant = mir::ConstantKind::from_value(
-                                cx.tcx.valtree_to_const_val((ty, max_val_const.to_valtree())),
-                                ty,
-                            );
-                            miri_to_const(cx, max_constant)?
+                            miri_to_const(cx, mir::ConstantKind::from_ty_const(max_val_const, cx.tcx))?
                         },
                     };
                     let lhs_val = lhs_const.int_value(cx, ty)?;
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs b/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs
new file mode 100644
index 00000000000..79c6d63254b
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs
@@ -0,0 +1,106 @@
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::higher::VecArgs;
+use clippy_utils::{expr_or_init, is_trait_method, match_def_path, paths};
+use rustc_ast::LitKind;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self};
+use rustc_span::sym;
+
+use super::ITER_OUT_OF_BOUNDS;
+
+fn expr_as_u128(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<u128> {
+    if let ExprKind::Lit(lit) = expr_or_init(cx, e).kind
+        && let LitKind::Int(n, _) = lit.node
+    {
+        Some(n)
+    } else {
+        None
+    }
+}
+
+/// Attempts to extract the length out of an iterator expression.
+fn get_iterator_length<'tcx>(cx: &LateContext<'tcx>, iter: &'tcx Expr<'tcx>) -> Option<u128> {
+    let ty::Adt(adt, substs) = cx.typeck_results().expr_ty(iter).kind() else {
+        return None;
+    };
+    let did = adt.did();
+
+    if match_def_path(cx, did, &paths::ARRAY_INTO_ITER) {
+        // For array::IntoIter<T, const N: usize>, the length is the second generic
+        // parameter.
+        substs
+            .const_at(1)
+            .try_eval_target_usize(cx.tcx, cx.param_env)
+            .map(u128::from)
+    } else if match_def_path(cx, did, &paths::SLICE_ITER)
+        && let ExprKind::MethodCall(_, recv, ..) = iter.kind
+    {
+        if let ty::Array(_, len) = cx.typeck_results().expr_ty(recv).peel_refs().kind() {
+            // For slice::Iter<'_, T>, the receiver might be an array literal: [1,2,3].iter().skip(..)
+            len.try_eval_target_usize(cx.tcx, cx.param_env).map(u128::from)
+        } else if let Some(args) = VecArgs::hir(cx, expr_or_init(cx, recv)) {
+            match args {
+                VecArgs::Vec(vec) => vec.len().try_into().ok(),
+                VecArgs::Repeat(_, len) => expr_as_u128(cx, len),
+            }
+        } else {
+            None
+        }
+    } else if match_def_path(cx, did, &paths::ITER_EMPTY) {
+        Some(0)
+    } else if match_def_path(cx, did, &paths::ITER_ONCE) {
+        Some(1)
+    }  else {
+        None
+    }
+}
+
+fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    recv: &'tcx Expr<'tcx>,
+    arg: &'tcx Expr<'tcx>,
+    message: &'static str,
+    note: &'static str,
+) {
+    if is_trait_method(cx, expr, sym::Iterator)
+        && let Some(len) = get_iterator_length(cx, recv)
+        && let Some(skipped) = expr_as_u128(cx, arg)
+        && skipped > len
+    {
+        span_lint_and_note(cx, ITER_OUT_OF_BOUNDS, expr.span, message, None, note);
+    }
+}
+
+pub(super) fn check_skip<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    recv: &'tcx Expr<'tcx>,
+    arg: &'tcx Expr<'tcx>,
+) {
+    check(
+        cx,
+        expr,
+        recv,
+        arg,
+        "this `.skip()` call skips more items than the iterator will produce",
+        "this operation is useless and will create an empty iterator",
+    );
+}
+
+pub(super) fn check_take<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    recv: &'tcx Expr<'tcx>,
+    arg: &'tcx Expr<'tcx>,
+) {
+    check(
+        cx,
+        expr,
+        recv,
+        arg,
+        "this `.take()` call takes more items than the iterator will produce",
+        "this operation is useless and the returned iterator will simply yield the same items",
+    );
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
index ee405a3e30c..a49dd98db87 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
@@ -21,7 +21,7 @@ pub(super) enum Op<'a> {
     RmCloned,
 
     // rm `.cloned()`
-    // e.g. `map` `for_each`
+    // e.g. `map` `for_each` `all` `any`
     NeedlessMove(&'a str, &'a Expr<'a>),
 
     // later `.cloned()`
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 5075137caa0..81223fa8d95 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -43,6 +43,7 @@ mod iter_next_slice;
 mod iter_nth;
 mod iter_nth_zero;
 mod iter_on_single_or_empty_collections;
+mod iter_out_of_bounds;
 mod iter_overeager_cloned;
 mod iter_skip_next;
 mod iter_skip_zero;
@@ -3054,12 +3055,12 @@ declare_clippy_lint! {
     ///
     /// ### Example
     /// ```rust
-    /// vec!(1, 2, 3, 4, 5).resize(0, 5)
+    /// vec![1, 2, 3, 4, 5].resize(0, 5)
     /// ```
     ///
     /// Use instead:
     /// ```rust
-    /// vec!(1, 2, 3, 4, 5).clear()
+    /// vec![1, 2, 3, 4, 5].clear()
     /// ```
     #[clippy::version = "1.46.0"]
     pub VEC_RESIZE_TO_ZERO,
@@ -3538,6 +3539,30 @@ declare_clippy_lint! {
     "acquiring a write lock when a read lock would work"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Looks for iterator combinator calls such as `.take(x)` or `.skip(x)`
+    /// where `x` is greater than the amount of items that an iterator will produce.
+    ///
+    /// ### Why is this bad?
+    /// Taking or skipping more items than there are in an iterator either creates an iterator
+    /// with all items from the original iterator or an iterator with no items at all.
+    /// This is most likely not what the user intended to do.
+    ///
+    /// ### Example
+    /// ```rust
+    /// for _ in [1, 2, 3].iter().take(4) {}
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// for _ in [1, 2, 3].iter() {}
+    /// ```
+    #[clippy::version = "1.74.0"]
+    pub ITER_OUT_OF_BOUNDS,
+    suspicious,
+    "calls to `.take()` or `.skip()` that are out of bounds"
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Msrv,
@@ -3676,7 +3701,8 @@ impl_lint_pass!(Methods => [
     STRING_LIT_CHARS_ANY,
     ITER_SKIP_ZERO,
     FILTER_MAP_BOOL_THEN,
-    READONLY_WRITE_LOCK
+    READONLY_WRITE_LOCK,
+    ITER_OUT_OF_BOUNDS,
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -3873,6 +3899,12 @@ impl Methods {
                 ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
                     zst_offset::check(cx, expr, recv);
                 },
+                ("all", [arg]) => {
+                    if let Some(("cloned", recv2, [], _, _)) = method_call(recv) {
+                        iter_overeager_cloned::check(cx, expr, recv, recv2,
+                                iter_overeager_cloned::Op::NeedlessMove(name, arg), false);
+                    }
+                }
                 ("and_then", [arg]) => {
                     let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg);
                     let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg);
@@ -3880,12 +3912,16 @@ impl Methods {
                         unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
                     }
                 },
-                ("any", [arg]) if let ExprKind::Closure(arg) = arg.kind
-                    && let body = cx.tcx.hir().body(arg.body)
-                    && let [param] = body.params
-                    && let Some(("chars", recv, _, _, _)) = method_call(recv) =>
-                {
-                    string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
+                ("any", [arg]) => {
+                    match method_call(recv) {
+                        Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::NeedlessMove(name, arg), false),
+                        Some(("chars", recv, _, _, _)) if let ExprKind::Closure(arg) = arg.kind
+                        && let body = cx.tcx.hir().body(arg.body)
+                        && let [param] = body.params => {
+                            string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
+                        }
+                        _ => {}
+                    }
                 }
                 ("arg", [arg]) => {
                     suspicious_command_arg_space::check(cx, recv, arg, span);
@@ -4136,6 +4172,7 @@ impl Methods {
                 },
                 ("skip", [arg]) => {
                     iter_skip_zero::check(cx, expr, arg);
+                    iter_out_of_bounds::check_skip(cx, expr, recv, arg);
 
                     if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
                         iter_overeager_cloned::check(cx, expr, recv, recv2,
@@ -4163,7 +4200,8 @@ impl Methods {
                     }
                 },
                 ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
-                ("take", [_arg]) => {
+                ("take", [arg]) => {
+                    iter_out_of_bounds::check_take(cx, expr, recv, arg);
                     if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
                         iter_overeager_cloned::check(cx, expr, recv, recv2,
                                 iter_overeager_cloned::Op::LaterCloned, false);
diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs
new file mode 100644
index 00000000000..08fec2b8ec8
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs
@@ -0,0 +1,391 @@
+use std::mem;
+use std::ops::ControlFlow;
+
+use clippy_utils::comparisons::{normalize_comparison, Rel};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::visitors::for_each_expr;
+use clippy_utils::{eq_expr_value, hash_expr, higher};
+use rustc_ast::{LitKind, RangeLimits};
+use rustc_data_structures::unhash::UnhashMap;
+use rustc_errors::{Applicability, Diagnostic};
+use rustc_hir::{BinOp, Block, Expr, ExprKind, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Spanned;
+use rustc_span::{sym, Span};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for repeated slice indexing without asserting beforehand that the length
+    /// is greater than the largest index used to index into the slice.
+    ///
+    /// ### Why is this bad?
+    /// In the general case where the compiler does not have a lot of information
+    /// about the length of a slice, indexing it repeatedly will generate a bounds check
+    /// for every single index.
+    ///
+    /// Asserting that the length of the slice is at least as large as the largest value
+    /// to index beforehand gives the compiler enough information to elide the bounds checks,
+    /// effectively reducing the number of bounds checks from however many times
+    /// the slice was indexed to just one (the assert).
+    ///
+    /// ### Drawbacks
+    /// False positives. It is, in general, very difficult to predict how well
+    /// the optimizer will be able to elide bounds checks and it very much depends on
+    /// the surrounding code. For example, indexing into the slice yielded by the
+    /// [`slice::chunks_exact`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.chunks_exact)
+    /// iterator will likely have all of the bounds checks elided even without an assert
+    /// if the `chunk_size` is a constant.
+    ///
+    /// Asserts are not tracked across function calls. Asserting the length of a slice
+    /// in a different function likely gives the optimizer enough information
+    /// about the length of a slice, but this lint will not detect that.
+    ///
+    /// ### Example
+    /// ```rust
+    /// fn sum(v: &[u8]) -> u8 {
+    ///     // 4 bounds checks
+    ///     v[0] + v[1] + v[2] + v[3]
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// fn sum(v: &[u8]) -> u8 {
+    ///     assert!(v.len() > 4);
+    ///     // no bounds checks
+    ///     v[0] + v[1] + v[2] + v[3]
+    /// }
+    /// ```
+    #[clippy::version = "1.70.0"]
+    pub MISSING_ASSERTS_FOR_INDEXING,
+    restriction,
+    "indexing into a slice multiple times without an `assert`"
+}
+declare_lint_pass!(MissingAssertsForIndexing => [MISSING_ASSERTS_FOR_INDEXING]);
+
+fn report_lint<F>(cx: &LateContext<'_>, full_span: Span, msg: &str, indexes: &[Span], f: F)
+where
+    F: FnOnce(&mut Diagnostic),
+{
+    span_lint_and_then(cx, MISSING_ASSERTS_FOR_INDEXING, full_span, msg, |diag| {
+        f(diag);
+        for span in indexes {
+            diag.span_note(*span, "slice indexed here");
+        }
+        diag.note("asserting the length before indexing will elide bounds checks");
+    });
+}
+
+#[derive(Copy, Clone, Debug)]
+enum LengthComparison {
+    /// `v.len() < 5`
+    LengthLessThanInt,
+    /// `5 < v.len()`
+    IntLessThanLength,
+    /// `v.len() <= 5`
+    LengthLessThanOrEqualInt,
+    /// `5 <= v.len()`
+    IntLessThanOrEqualLength,
+}
+
+/// Extracts parts out of a length comparison expression.
+///
+/// E.g. for `v.len() > 5` this returns `Some((LengthComparison::IntLessThanLength, 5, `v.len()`))`
+fn len_comparison<'hir>(
+    bin_op: BinOp,
+    left: &'hir Expr<'hir>,
+    right: &'hir Expr<'hir>,
+) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> {
+    macro_rules! int_lit_pat {
+        ($id:ident) => {
+            ExprKind::Lit(Spanned {
+                node: LitKind::Int($id, _),
+                ..
+            })
+        };
+    }
+
+    // normalize comparison, `v.len() > 4` becomes `4 < v.len()`
+    // this simplifies the logic a bit
+    let (op, left, right) = normalize_comparison(bin_op.node, left, right)?;
+    match (op, &left.kind, &right.kind) {
+        (Rel::Lt, int_lit_pat!(left), _) => Some((LengthComparison::IntLessThanLength, *left as usize, right)),
+        (Rel::Lt, _, int_lit_pat!(right)) => Some((LengthComparison::LengthLessThanInt, *right as usize, left)),
+        (Rel::Le, int_lit_pat!(left), _) => Some((LengthComparison::IntLessThanOrEqualLength, *left as usize, right)),
+        (Rel::Le, _, int_lit_pat!(right)) => Some((LengthComparison::LengthLessThanOrEqualInt, *right as usize, left)),
+        _ => None,
+    }
+}
+
+/// Attempts to extract parts out of an `assert!`-like expression
+/// in the form `assert!(some_slice.len() > 5)`.
+///
+/// `assert!` has expanded to an if expression at the HIR, so this
+/// actually works not just with `assert!` specifically, but anything
+/// that has a never type expression in the `then` block (e.g. `panic!`).
+fn assert_len_expr<'hir>(
+    cx: &LateContext<'_>,
+    expr: &'hir Expr<'hir>,
+) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> {
+    if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr)
+        && let ExprKind::Unary(UnOp::Not, condition) = &cond.kind
+        && let ExprKind::Binary(bin_op, left, right) = &condition.kind
+
+        && let Some((cmp, asserted_len, slice_len)) = len_comparison(*bin_op, left, right)
+        && let ExprKind::MethodCall(method, recv, ..) = &slice_len.kind
+        && cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice()
+        && method.ident.name == sym::len
+
+        // check if `then` block has a never type expression
+        && let ExprKind::Block(Block { expr: Some(then_expr), .. }, _) = then.kind
+        && cx.typeck_results().expr_ty(then_expr).is_never()
+    {
+        Some((cmp, asserted_len, recv))
+    } else {
+        None
+    }
+}
+
+#[derive(Debug)]
+enum IndexEntry<'hir> {
+    /// `assert!` without any indexing (so far)
+    StrayAssert {
+        asserted_len: usize,
+        comparison: LengthComparison,
+        assert_span: Span,
+        slice: &'hir Expr<'hir>,
+    },
+    /// `assert!` with indexing
+    ///
+    /// We also store the highest index to be able to check
+    /// if the `assert!` asserts the right length.
+    AssertWithIndex {
+        highest_index: usize,
+        asserted_len: usize,
+        assert_span: Span,
+        slice: &'hir Expr<'hir>,
+        indexes: Vec<Span>,
+        comparison: LengthComparison,
+    },
+    /// Indexing without an `assert!`
+    IndexWithoutAssert {
+        highest_index: usize,
+        indexes: Vec<Span>,
+        slice: &'hir Expr<'hir>,
+    },
+}
+
+impl<'hir> IndexEntry<'hir> {
+    pub fn slice(&self) -> &'hir Expr<'hir> {
+        match self {
+            IndexEntry::StrayAssert { slice, .. }
+            | IndexEntry::AssertWithIndex { slice, .. }
+            | IndexEntry::IndexWithoutAssert { slice, .. } => slice,
+        }
+    }
+
+    pub fn index_spans(&self) -> Option<&[Span]> {
+        match self {
+            IndexEntry::StrayAssert { .. } => None,
+            IndexEntry::AssertWithIndex { indexes, .. } | IndexEntry::IndexWithoutAssert { indexes, .. } => {
+                Some(indexes)
+            },
+        }
+    }
+}
+
+/// Extracts the upper index of a slice indexing expression.
+///
+/// E.g. for `5` this returns `Some(5)`, for `..5` this returns `Some(4)`,
+/// for `..=5` this returns `Some(5)`
+fn upper_index_expr(expr: &Expr<'_>) -> Option<usize> {
+    if let ExprKind::Lit(lit) = &expr.kind && let LitKind::Int(index, _) = lit.node {
+        Some(index as usize)
+    } else if let Some(higher::Range { end: Some(end), limits, .. }) = higher::Range::hir(expr)
+        && let ExprKind::Lit(lit) = &end.kind
+        && let LitKind::Int(index @ 1.., _) = lit.node
+    {
+        match limits {
+            RangeLimits::HalfOpen => Some(index as usize - 1),
+            RangeLimits::Closed => Some(index as usize),
+        }
+    } else {
+        None
+    }
+}
+
+/// Checks if the expression is an index into a slice and adds it to `indexes`
+fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnhashMap<u64, Vec<IndexEntry<'hir>>>) {
+    if let ExprKind::Index(slice, index_lit, _) = expr.kind
+        && cx.typeck_results().expr_ty_adjusted(slice).peel_refs().is_slice()
+        && let Some(index) = upper_index_expr(index_lit)
+    {
+        let hash = hash_expr(cx, slice);
+
+        let indexes = map.entry(hash).or_default();
+        let entry = indexes.iter_mut().find(|entry| eq_expr_value(cx, entry.slice(), slice));
+
+        if let Some(entry) = entry {
+            match entry {
+                IndexEntry::StrayAssert { asserted_len, comparison, assert_span, slice } => {
+                    *entry = IndexEntry::AssertWithIndex {
+                        highest_index: index,
+                        asserted_len: *asserted_len,
+                        assert_span: *assert_span,
+                        slice,
+                        indexes: vec![expr.span],
+                        comparison: *comparison,
+                    };
+                },
+                IndexEntry::IndexWithoutAssert { highest_index, indexes, .. }
+                | IndexEntry::AssertWithIndex { highest_index, indexes, .. } => {
+                    indexes.push(expr.span);
+                    *highest_index = (*highest_index).max(index);
+                },
+            }
+        } else {
+            indexes.push(IndexEntry::IndexWithoutAssert {
+                highest_index: index,
+                indexes: vec![expr.span],
+                slice,
+            });
+        }
+    }
+}
+
+/// Checks if the expression is an `assert!` expression and adds it to `asserts`
+fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnhashMap<u64, Vec<IndexEntry<'hir>>>) {
+    if let Some((comparison, asserted_len, slice)) = assert_len_expr(cx, expr) {
+        let hash = hash_expr(cx, slice);
+        let indexes = map.entry(hash).or_default();
+
+        let entry = indexes.iter_mut().find(|entry| eq_expr_value(cx, entry.slice(), slice));
+
+        if let Some(entry) = entry {
+            if let IndexEntry::IndexWithoutAssert {
+                highest_index,
+                indexes,
+                slice,
+            } = entry
+            {
+                *entry = IndexEntry::AssertWithIndex {
+                    highest_index: *highest_index,
+                    indexes: mem::take(indexes),
+                    slice,
+                    assert_span: expr.span,
+                    comparison,
+                    asserted_len,
+                };
+            }
+        } else {
+            indexes.push(IndexEntry::StrayAssert {
+                asserted_len,
+                comparison,
+                assert_span: expr.span,
+                slice,
+            });
+        }
+    }
+}
+
+/// Inspects indexes and reports lints.
+///
+/// Called at the end of this lint after all indexing and `assert!` expressions have been collected.
+fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap<u64, Vec<IndexEntry<'_>>>) {
+    for bucket in map.values() {
+        for entry in bucket {
+            let Some(full_span) = entry
+                .index_spans()
+                .and_then(|spans| spans.first().zip(spans.last()))
+                .map(|(low, &high)| low.to(high))
+            else {
+                continue;
+            };
+
+            match entry {
+                IndexEntry::AssertWithIndex {
+                    highest_index,
+                    asserted_len,
+                    indexes,
+                    comparison,
+                    assert_span,
+                    slice,
+                } if indexes.len() > 1 => {
+                    // if we have found an `assert!`, let's also check that it's actually right
+                    // and if it convers the highest index and if not, suggest the correct length
+                    let sugg = match comparison {
+                        // `v.len() < 5` and `v.len() <= 5` does nothing in terms of bounds checks.
+                        // The user probably meant `v.len() > 5`
+                        LengthComparison::LengthLessThanInt | LengthComparison::LengthLessThanOrEqualInt => Some(
+                            format!("assert!({}.len() > {highest_index})", snippet(cx, slice.span, "..")),
+                        ),
+                        // `5 < v.len()` == `v.len() > 5`
+                        LengthComparison::IntLessThanLength if asserted_len < highest_index => Some(format!(
+                            "assert!({}.len() > {highest_index})",
+                            snippet(cx, slice.span, "..")
+                        )),
+                        // `5 <= v.len() == `v.len() >= 5`
+                        LengthComparison::IntLessThanOrEqualLength if asserted_len <= highest_index => Some(format!(
+                            "assert!({}.len() > {highest_index})",
+                            snippet(cx, slice.span, "..")
+                        )),
+                        _ => None,
+                    };
+
+                    if let Some(sugg) = sugg {
+                        report_lint(
+                            cx,
+                            full_span,
+                            "indexing into a slice multiple times with an `assert` that does not cover the highest index",
+                            indexes,
+                            |diag| {
+                                diag.span_suggestion(
+                                    *assert_span,
+                                    "provide the highest index that is indexed with",
+                                    sugg,
+                                    Applicability::MachineApplicable,
+                                );
+                            },
+                        );
+                    }
+                },
+                IndexEntry::IndexWithoutAssert {
+                    indexes,
+                    highest_index,
+                    slice,
+                } if indexes.len() > 1 => {
+                    // if there was no `assert!` but more than one index, suggest
+                    // adding an `assert!` that covers the highest index
+                    report_lint(
+                        cx,
+                        full_span,
+                        "indexing into a slice multiple times without an `assert`",
+                        indexes,
+                        |diag| {
+                            diag.help(format!(
+                                "consider asserting the length before indexing: `assert!({}.len() > {highest_index});`",
+                                snippet(cx, slice.span, "..")
+                            ));
+                        },
+                    );
+                },
+                _ => {},
+            }
+        }
+    }
+}
+
+impl LateLintPass<'_> for MissingAssertsForIndexing {
+    fn check_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>) {
+        let mut map = UnhashMap::default();
+
+        for_each_expr(block, |expr| {
+            check_index(cx, expr, &mut map);
+            check_assert(cx, expr, &mut map);
+            ControlFlow::<!, ()>::Continue(())
+        });
+
+        report_indexes(cx, &map);
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/incorrect_impls.rs b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs
index 3c59b839a39..4b24f059afd 100644
--- a/src/tools/clippy/clippy_lints/src/incorrect_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs
@@ -13,11 +13,12 @@ use rustc_span::symbol::kw;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for manual implementations of `Clone` when `Copy` is already implemented.
+    /// Checks for non-canonical implementations of `Clone` when `Copy` is already implemented.
     ///
     /// ### Why is this bad?
-    /// If both `Clone` and `Copy` are implemented, they must agree. This is done by dereferencing
-    /// `self` in `Clone`'s implementation. Anything else is incorrect.
+    /// If both `Clone` and `Copy` are implemented, they must agree. This can done by dereferencing
+    /// `self` in `Clone`'s implementation, which will avoid any possibility of the implementations
+    /// becoming out of sync.
     ///
     /// ### Example
     /// ```rust,ignore
@@ -46,14 +47,13 @@ declare_clippy_lint! {
     /// impl Copy for A {}
     /// ```
     #[clippy::version = "1.72.0"]
-    pub INCORRECT_CLONE_IMPL_ON_COPY_TYPE,
-    correctness,
-    "manual implementation of `Clone` on a `Copy` type"
+    pub NON_CANONICAL_CLONE_IMPL,
+    suspicious,
+    "non-canonical implementation of `Clone` on a `Copy` type"
 }
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for manual implementations of both `PartialOrd` and `Ord` when only `Ord` is
-    /// necessary.
+    /// Checks for non-canonical implementations of `PartialOrd` when `Ord` is already implemented.
     ///
     /// ### Why is this bad?
     /// If both `PartialOrd` and `Ord` are implemented, they must agree. This is commonly done by
@@ -61,11 +61,8 @@ declare_clippy_lint! {
     /// introduce an error upon refactoring.
     ///
     /// ### Known issues
-    /// Code that calls the `.into()` method instead will be flagged as incorrect, despite `.into()`
-    /// wrapping it in `Some`.
-    ///
-    /// ### Limitations
-    /// Will not lint if `Self` and `Rhs` do not have the same type.
+    /// Code that calls the `.into()` method instead will be flagged, despite `.into()` wrapping it
+    /// in `Some`.
     ///
     /// ### Example
     /// ```rust
@@ -107,13 +104,13 @@ declare_clippy_lint! {
     /// }
     /// ```
     #[clippy::version = "1.72.0"]
-    pub INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE,
-    correctness,
-    "manual implementation of `PartialOrd` when `Ord` is already implemented"
+    pub NON_CANONICAL_PARTIAL_ORD_IMPL,
+    suspicious,
+    "non-canonical implementation of `PartialOrd` on an `Ord` type"
 }
-declare_lint_pass!(IncorrectImpls => [INCORRECT_CLONE_IMPL_ON_COPY_TYPE, INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE]);
+declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL_PARTIAL_ORD_IMPL]);
 
-impl LateLintPass<'_> for IncorrectImpls {
+impl LateLintPass<'_> for NonCanonicalImpls {
     #[expect(clippy::too_many_lines)]
     fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
         let Some(Node::Item(item)) = get_parent_node(cx.tcx, impl_item.hir_id()) else {
@@ -154,9 +151,9 @@ impl LateLintPass<'_> for IncorrectImpls {
                 {} else {
                     span_lint_and_sugg(
                         cx,
-                        INCORRECT_CLONE_IMPL_ON_COPY_TYPE,
+                        NON_CANONICAL_CLONE_IMPL,
                         block.span,
-                        "incorrect implementation of `clone` on a `Copy` type",
+                        "non-canonical implementation of `clone` on a `Copy` type",
                         "change this to",
                         "{ *self }".to_owned(),
                         Applicability::MaybeIncorrect,
@@ -169,9 +166,9 @@ impl LateLintPass<'_> for IncorrectImpls {
             if impl_item.ident.name == sym::clone_from {
                 span_lint_and_sugg(
                     cx,
-                    INCORRECT_CLONE_IMPL_ON_COPY_TYPE,
+                    NON_CANONICAL_CLONE_IMPL,
                     impl_item.span,
-                    "incorrect implementation of `clone_from` on a `Copy` type",
+                    "unnecessary implementation of `clone_from` on a `Copy` type",
                     "remove it",
                     String::new(),
                     Applicability::MaybeIncorrect,
@@ -222,9 +219,9 @@ impl LateLintPass<'_> for IncorrectImpls {
 
                 span_lint_and_then(
                     cx,
-                    INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE,
+                    NON_CANONICAL_PARTIAL_ORD_IMPL,
                     item.span,
-                    "incorrect implementation of `partial_cmp` on an `Ord` type",
+                    "non-canonical implementation of `partial_cmp` on an `Ord` type",
                     |diag| {
                         let [_, other] = body.params else {
                             return;
diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
index a687c7d29b5..8072aded851 100644
--- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
@@ -14,7 +14,12 @@ use {rustc_ast as ast, rustc_hir as hir};
 
 const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[["f32", "f32"], ["f64", "f64"], ["std::string::String", "str"]];
 const HARD_CODED_ALLOWED_UNARY: &[&str] = &["f32", "f64", "std::num::Saturating", "std::num::Wrapping"];
-const INTEGER_METHODS: &[Symbol] = &[sym::saturating_div, sym::wrapping_div, sym::wrapping_rem, sym::wrapping_rem_euclid];
+const INTEGER_METHODS: &[Symbol] = &[
+    sym::saturating_div,
+    sym::wrapping_div,
+    sym::wrapping_rem,
+    sym::wrapping_rem_euclid,
+];
 
 #[derive(Debug)]
 pub struct ArithmeticSideEffects {
@@ -93,7 +98,14 @@ impl ArithmeticSideEffects {
         let is_non_zero_u = |symbol: Option<Symbol>| {
             matches!(
                 symbol,
-                Some(sym::NonZeroU128 | sym::NonZeroU16 | sym::NonZeroU32 | sym::NonZeroU64 | sym::NonZeroU8 | sym::NonZeroUsize)
+                Some(
+                    sym::NonZeroU128
+                        | sym::NonZeroU16
+                        | sym::NonZeroU32
+                        | sym::NonZeroU64
+                        | sym::NonZeroU8
+                        | sym::NonZeroUsize
+                )
             )
         };
         let is_sat_or_wrap = |ty: Ty<'_>| {
diff --git a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs
index f3e0c58a787..bce6bdcaf61 100644
--- a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs
@@ -17,7 +17,7 @@ pub(crate) fn check<'tcx>(
     left: &'tcx Expr<'_>,
     right: &'tcx Expr<'_>,
 ) {
-    if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
+    if (op == BinOpKind::Eq || op == BinOpKind::Ne) && is_float(cx, left) {
         let left_is_local = match constant_with_source(cx, cx.typeck_results(), left) {
             Some((c, s)) if !is_allowed(&c) => s.is_local(),
             Some(_) => return,
diff --git a/src/tools/clippy/clippy_lints/src/raw_strings.rs b/src/tools/clippy/clippy_lints/src/raw_strings.rs
index ccabb577cb7..e8018462d75 100644
--- a/src/tools/clippy/clippy_lints/src/raw_strings.rs
+++ b/src/tools/clippy/clippy_lints/src/raw_strings.rs
@@ -1,7 +1,7 @@
 use std::iter::once;
 use std::ops::ControlFlow;
 
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
 use rustc_ast::ast::{Expr, ExprKind};
 use rustc_ast::token::LitKind;
@@ -9,6 +9,7 @@ use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::{BytePos, Pos, Span};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -76,14 +77,33 @@ impl EarlyLintPass for RawStrings {
             }
 
             if !str.contains(['\\', '"']) {
-                span_lint_and_sugg(
+                span_lint_and_then(
                     cx,
                     NEEDLESS_RAW_STRINGS,
                     expr.span,
                     "unnecessary raw string literal",
-                    "try",
-                    format!("{}\"{}\"", prefix.replace('r', ""), lit.symbol),
-                    Applicability::MachineApplicable,
+                    |diag| {
+                        let (start, end) = hash_spans(expr.span, prefix, 0, max);
+
+                        // BytePos: skip over the `b` in `br`, we checked the prefix appears in the source text
+                        let r_pos = expr.span.lo() + BytePos::from_usize(prefix.len() - 1);
+                        let start = start.with_lo(r_pos);
+
+                        if end.is_empty() {
+                            diag.span_suggestion(
+                                start,
+                                "use a string literal instead",
+                                format!("\"{}\"", str),
+                                Applicability::MachineApplicable,
+                            );
+                        } else {
+                            diag.multipart_suggestion(
+                                "try",
+                                vec![(start, String::new()), (end, String::new())],
+                                Applicability::MachineApplicable,
+                            );
+                        }
+                    },
                 );
 
                 return;
@@ -96,13 +116,6 @@ impl EarlyLintPass for RawStrings {
                 let num = str.as_bytes().iter().chain(once(&0)).try_fold(0u8, |acc, &b| {
                     match b {
                         b'"' if !following_quote => (following_quote, req) = (true, 1),
-                        // I'm a bit surprised the compiler didn't optimize this out, there's no
-                        // branch but it still ends up doing an unnecessary comparison, it's:
-                        // - cmp r9b,1h
-                        // - sbb cl,-1h
-                        // which will add 1 if it's true. With this change, it becomes:
-                        // - add cl,r9b
-                        // isn't that so much nicer?
                         b'#' => req += u8::from(following_quote),
                         _ => {
                             if following_quote {
@@ -126,18 +139,58 @@ impl EarlyLintPass for RawStrings {
             };
 
             if req < max {
-                let hashes = "#".repeat(req as usize);
-
-                span_lint_and_sugg(
+                span_lint_and_then(
                     cx,
                     NEEDLESS_RAW_STRING_HASHES,
                     expr.span,
                     "unnecessary hashes around raw string literal",
-                    "try",
-                    format!(r#"{prefix}{hashes}"{}"{hashes}"#, lit.symbol),
-                    Applicability::MachineApplicable,
+                    |diag| {
+                        let (start, end) = hash_spans(expr.span, prefix, req, max);
+
+                        let message = match max - req {
+                            _ if req == 0 => "remove all the hashes around the literal".to_string(),
+                            1 => "remove one hash from both sides of the literal".to_string(),
+                            n => format!("remove {n} hashes from both sides of the literal"),
+                        };
+
+                        diag.multipart_suggestion(
+                            message,
+                            vec![(start, String::new()), (end, String::new())],
+                            Applicability::MachineApplicable,
+                        );
+                    },
                 );
             }
         }
     }
 }
+
+/// Returns spans pointing at the unneeded hashes, e.g. for a `req` of `1` and `max` of `3`:
+///
+/// ```ignore
+/// r###".."###
+///   ^^    ^^
+/// ```
+fn hash_spans(literal_span: Span, prefix: &str, req: u8, max: u8) -> (Span, Span) {
+    let literal_span = literal_span.data();
+
+    // BytePos: we checked prefix appears literally in the source text
+    let hash_start = literal_span.lo + BytePos::from_usize(prefix.len());
+    let hash_end = literal_span.hi;
+
+    // BytePos: req/max are counts of the ASCII character #
+    let start = Span::new(
+        hash_start + BytePos(req.into()),
+        hash_start + BytePos(max.into()),
+        literal_span.ctxt,
+        None,
+    );
+    let end = Span::new(
+        hash_end - BytePos(req.into()),
+        hash_end - BytePos(max.into()),
+        literal_span.ctxt,
+        None,
+    );
+
+    (start, end)
+}
diff --git a/src/tools/clippy/clippy_lints/src/renamed_lints.rs b/src/tools/clippy/clippy_lints/src/renamed_lints.rs
index fc1fabcc0ae..613f1ecc6fb 100644
--- a/src/tools/clippy/clippy_lints/src/renamed_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/renamed_lints.rs
@@ -15,6 +15,8 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[
     ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"),
     ("clippy::identity_conversion", "clippy::useless_conversion"),
     ("clippy::if_let_some_result", "clippy::match_result_ok"),
+    ("clippy::incorrect_clone_impl_on_copy_type", "clippy::non_canonical_clone_impl"),
+    ("clippy::incorrect_partial_ord_impl_on_ord_type", "clippy::non_canonical_partial_ord_impl"),
     ("clippy::integer_arithmetic", "clippy::arithmetic_side_effects"),
     ("clippy::logic_bug", "clippy::overly_complex_bool_expr"),
     ("clippy::new_without_default_derive", "clippy::new_without_default"),
@@ -38,12 +40,12 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[
     ("clippy::drop_bounds", "drop_bounds"),
     ("clippy::drop_copy", "dropping_copy_types"),
     ("clippy::drop_ref", "dropping_references"),
+    ("clippy::fn_null_check", "useless_ptr_null_checks"),
     ("clippy::for_loop_over_option", "for_loops_over_fallibles"),
     ("clippy::for_loop_over_result", "for_loops_over_fallibles"),
     ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"),
     ("clippy::forget_copy", "forgetting_copy_types"),
     ("clippy::forget_ref", "forgetting_references"),
-    ("clippy::fn_null_check", "useless_ptr_null_checks"),
     ("clippy::into_iter_on_array", "array_into_iter"),
     ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"),
     ("clippy::invalid_ref", "invalid_value"),
diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
index c9ab622ad25..9db18c2976c 100644
--- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
@@ -1,4 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::macros::root_macro_call;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{
     get_enclosing_block, is_expr_path_def_path, is_integer_literal, is_path_diagnostic_item, path_to_local,
@@ -148,6 +149,15 @@ impl SlowVectorInit {
     /// - `Some(InitializedSize::Uninitialized)` for `Vec::new()`
     /// - `None` for other, unrelated kinds of expressions
     fn as_vec_initializer<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<InitializedSize<'tcx>> {
+        // Generally don't warn if the vec initializer comes from an expansion, except for the vec! macro.
+        // This lets us still warn on `vec![]`, while ignoring other kinds of macros that may output an
+        // empty vec
+        if expr.span.from_expansion()
+            && root_macro_call(expr.span).map(|m| m.def_id) != cx.tcx.get_diagnostic_item(sym::vec_macro)
+        {
+            return None;
+        }
+
         if let ExprKind::Call(func, [len_expr]) = expr.kind
             && is_expr_path_def_path(cx, func, &paths::VEC_WITH_CAPACITY)
         {
@@ -205,7 +215,7 @@ impl SlowVectorInit {
 
         span_lint_and_then(cx, SLOW_VECTOR_INITIALIZATION, slow_fill.span, msg, |diag| {
             diag.span_suggestion(
-                vec_alloc.allocation_expr.span,
+                vec_alloc.allocation_expr.span.source_callsite(),
                 "consider replacing this with",
                 format!("vec![0; {len_expr}]"),
                 Applicability::Unspecified,
diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
index f239165276f..5f54a10d1c4 100644
--- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
+++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
@@ -1,4 +1,5 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{HirId, Path, PathSegment};
@@ -99,17 +100,17 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
             && let Some(first_segment) = get_first_segment(path)
             && is_stable(cx, def_id)
         {
-            let (lint, msg, help) = match first_segment.ident.name {
+            let (lint, used_mod, replace_with) = match first_segment.ident.name {
                 sym::std => match cx.tcx.crate_name(def_id.krate) {
                     sym::core => (
                         STD_INSTEAD_OF_CORE,
-                        "used import from `std` instead of `core`",
-                        "consider importing the item from `core`",
+                        "std",
+                        "core",
                     ),
                     sym::alloc => (
                         STD_INSTEAD_OF_ALLOC,
-                        "used import from `std` instead of `alloc`",
-                        "consider importing the item from `alloc`",
+                        "std",
+                        "alloc",
                     ),
                     _ => {
                         self.prev_span = path.span;
@@ -120,8 +121,8 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
                     if cx.tcx.crate_name(def_id.krate) == sym::core {
                         (
                             ALLOC_INSTEAD_OF_CORE,
-                            "used import from `alloc` instead of `core`",
-                            "consider importing the item from `core`",
+                            "alloc",
+                            "core",
                         )
                     } else {
                         self.prev_span = path.span;
@@ -131,7 +132,14 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
                 _ => return,
             };
             if path.span != self.prev_span {
-                span_lint_and_help(cx, lint, path.span, msg, None, help);
+                span_lint_and_sugg(
+                    cx,
+                    lint,
+                    first_segment.ident.span,
+                    &format!("used import from `{used_mod}` instead of `{replace_with}`"),
+                    &format!("consider importing the item from `{replace_with}`"),
+                    replace_with.to_string(),
+                    Applicability::MachineApplicable);
                 self.prev_span = path.span;
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
index 23d6e2a845f..d10f10ef87e 100644
--- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
+++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
@@ -586,7 +586,7 @@ fn ident_difference_expr_with_base_location(
         | (ForLoop(_, _, _, _), ForLoop(_, _, _, _))
         | (While(_, _, _), While(_, _, _))
         | (If(_, _, _), If(_, _, _))
-        | (Let(_, _, _), Let(_, _, _))
+        | (Let(_, _, _, _), Let(_, _, _, _))
         | (Type(_, _), Type(_, _))
         | (Cast(_, _), Cast(_, _))
         | (Lit(_), Lit(_))
diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
index a1ea3a495eb..6193fdeb433 100644
--- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -341,44 +341,21 @@ fn block_parents_have_safety_comment(
     id: hir::HirId,
 ) -> bool {
     if let Some(node) = get_parent_node(cx.tcx, id) {
-        return match node {
-            Node::Expr(expr) => {
-                if let Some(
-                    Node::Local(hir::Local { span, .. })
-                    | Node::Item(hir::Item {
-                        kind: hir::ItemKind::Const(..) | ItemKind::Static(..),
-                        span,
-                        ..
-                    }),
-                ) = get_parent_node(cx.tcx, expr.hir_id)
-                {
-                    let hir_id = match get_parent_node(cx.tcx, expr.hir_id) {
-                        Some(Node::Local(hir::Local { hir_id, .. })) => *hir_id,
-                        Some(Node::Item(hir::Item { owner_id, .. })) => {
-                            cx.tcx.hir().local_def_id_to_hir_id(owner_id.def_id)
-                        },
-                        _ => unreachable!(),
-                    };
-
-                    // if unsafe block is part of a let/const/static statement,
-                    // and accept_comment_above_statement is set to true
-                    // we accept the safety comment in the line the precedes this statement.
-                    accept_comment_above_statement
-                        && span_with_attrs_in_body_has_safety_comment(
-                            cx,
-                            *span,
-                            hir_id,
-                            accept_comment_above_attributes,
-                        )
-                } else {
-                    !is_branchy(expr)
-                        && span_with_attrs_in_body_has_safety_comment(
-                            cx,
-                            expr.span,
-                            expr.hir_id,
-                            accept_comment_above_attributes,
-                        )
-                }
+        let (span, hir_id) = match node {
+            Node::Expr(expr) => match get_parent_node(cx.tcx, expr.hir_id) {
+                Some(Node::Local(hir::Local { span, hir_id, .. })) => (*span, *hir_id),
+                Some(Node::Item(hir::Item {
+                    kind: hir::ItemKind::Const(..) | ItemKind::Static(..),
+                    span,
+                    owner_id,
+                    ..
+                })) => (*span, cx.tcx.hir().local_def_id_to_hir_id(owner_id.def_id)),
+                _ => {
+                    if is_branchy(expr) {
+                        return false;
+                    }
+                    (expr.span, expr.hir_id)
+                },
             },
             Node::Stmt(hir::Stmt {
                 kind:
@@ -387,28 +364,27 @@ fn block_parents_have_safety_comment(
                     | hir::StmtKind::Semi(hir::Expr { span, hir_id, .. }),
                 ..
             })
-            | Node::Local(hir::Local { span, hir_id, .. }) => {
-                span_with_attrs_in_body_has_safety_comment(cx, *span, *hir_id, accept_comment_above_attributes)
-            },
+            | Node::Local(hir::Local { span, hir_id, .. }) => (*span, *hir_id),
             Node::Item(hir::Item {
                 kind: hir::ItemKind::Const(..) | ItemKind::Static(..),
                 span,
                 owner_id,
                 ..
-            }) => span_with_attrs_in_body_has_safety_comment(
-                cx,
-                *span,
-                cx.tcx.hir().local_def_id_to_hir_id(owner_id.def_id),
-                accept_comment_above_attributes,
-            ),
-            _ => false,
+            }) => (*span, cx.tcx.hir().local_def_id_to_hir_id(owner_id.def_id)),
+            _ => return false,
         };
+        // if unsafe block is part of a let/const/static statement,
+        // and accept_comment_above_statement is set to true
+        // we accept the safety comment in the line the precedes this statement.
+        accept_comment_above_statement
+            && span_with_attrs_has_safety_comment(cx, span, hir_id, accept_comment_above_attributes)
+    } else {
+        false
     }
-    false
 }
 
 /// Extends `span` to also include its attributes, then checks if that span has a safety comment.
-fn span_with_attrs_in_body_has_safety_comment(
+fn span_with_attrs_has_safety_comment(
     cx: &LateContext<'_>,
     span: Span,
     hir_id: HirId,
@@ -420,7 +396,7 @@ fn span_with_attrs_in_body_has_safety_comment(
         span
     };
 
-    span_in_body_has_safety_comment(cx, span)
+    span_has_safety_comment(cx, span)
 }
 
 /// Checks if an expression is "branchy", e.g. loop, match/if/etc.
@@ -444,7 +420,7 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
     matches!(
         span_from_macro_expansion_has_safety_comment(cx, span),
         HasSafetyComment::Yes(_)
-    ) || span_in_body_has_safety_comment(cx, span)
+    ) || span_has_safety_comment(cx, span)
 }
 
 fn include_attrs_in_span(cx: &LateContext<'_>, hir_id: HirId, span: Span) -> Span {
@@ -507,20 +483,18 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf
             && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
             && let Some(src) = unsafe_line.sf.src.as_deref()
         {
-            return unsafe_line.sf.lines(|lines| {
-                if comment_start_line.line >= unsafe_line.line {
-                    HasSafetyComment::No
-                } else {
-                    match text_has_safety_comment(
-                        src,
-                        &lines[comment_start_line.line + 1..=unsafe_line.line],
-                        unsafe_line.sf.start_pos,
-                    ) {
-                        Some(b) => HasSafetyComment::Yes(b),
-                        None => HasSafetyComment::No,
-                    }
+            return if comment_start_line.line >= unsafe_line.line {
+                HasSafetyComment::No
+            } else {
+                match text_has_safety_comment(
+                    src,
+                    &unsafe_line.sf.lines()[comment_start_line.line + 1..=unsafe_line.line],
+                    unsafe_line.sf.start_pos,
+                ) {
+                    Some(b) => HasSafetyComment::Yes(b),
+                    None => HasSafetyComment::No,
                 }
-            });
+            };
         }
     }
     HasSafetyComment::Maybe
@@ -551,20 +525,18 @@ fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> H
             && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
             && let Some(src) = unsafe_line.sf.src.as_deref()
         {
-            return unsafe_line.sf.lines(|lines| {
-                if comment_start_line.line >= unsafe_line.line {
-                    HasSafetyComment::No
-                } else {
-                    match text_has_safety_comment(
-                        src,
-                        &lines[comment_start_line.line + 1..=unsafe_line.line],
-                        unsafe_line.sf.start_pos,
-                    ) {
-                        Some(b) => HasSafetyComment::Yes(b),
-                        None => HasSafetyComment::No,
-                    }
+            return if comment_start_line.line >= unsafe_line.line {
+                HasSafetyComment::No
+            } else {
+                match text_has_safety_comment(
+                    src,
+                    &unsafe_line.sf.lines()[comment_start_line.line + 1..=unsafe_line.line],
+                    unsafe_line.sf.start_pos,
+                ) {
+                    Some(b) => HasSafetyComment::Yes(b),
+                    None => HasSafetyComment::No,
                 }
-            });
+            };
         }
     }
     HasSafetyComment::Maybe
@@ -614,20 +586,18 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span
             && Lrc::ptr_eq(&unsafe_line.sf, &macro_line.sf)
             && let Some(src) = unsafe_line.sf.src.as_deref()
         {
-            unsafe_line.sf.lines(|lines| {
-                if macro_line.line < unsafe_line.line {
-                    match text_has_safety_comment(
-                        src,
-                        &lines[macro_line.line + 1..=unsafe_line.line],
-                        unsafe_line.sf.start_pos,
-                    ) {
-                        Some(b) => HasSafetyComment::Yes(b),
-                        None => HasSafetyComment::No,
-                    }
-                } else {
-                    HasSafetyComment::No
+            if macro_line.line < unsafe_line.line {
+                match text_has_safety_comment(
+                    src,
+                    &unsafe_line.sf.lines()[macro_line.line + 1..=unsafe_line.line],
+                    unsafe_line.sf.start_pos,
+                ) {
+                    Some(b) => HasSafetyComment::Yes(b),
+                    None => HasSafetyComment::No,
                 }
-            })
+            } else {
+                HasSafetyComment::No
+            }
         } else {
             // Problem getting source text. Pretend a comment was found.
             HasSafetyComment::Maybe
@@ -639,29 +609,36 @@ fn get_body_search_span(cx: &LateContext<'_>) -> Option<Span> {
     let body = cx.enclosing_body?;
     let map = cx.tcx.hir();
     let mut span = map.body(body).value.span;
+    let mut maybe_global_var = false;
     for (_, node) in map.parent_iter(body.hir_id) {
         match node {
             Node::Expr(e) => span = e.span,
-            Node::Block(_)
-            | Node::Arm(_)
-            | Node::Stmt(_)
-            | Node::Local(_)
-            | Node::Item(hir::Item {
+            Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::Local(_) => (),
+            Node::Item(hir::Item {
                 kind: hir::ItemKind::Const(..) | ItemKind::Static(..),
                 ..
-            }) => (),
+            }) => maybe_global_var = true,
+            Node::Item(hir::Item {
+                kind: hir::ItemKind::Mod(_),
+                span: item_span,
+                ..
+            }) => {
+                span = *item_span;
+                break;
+            },
+            Node::Crate(mod_) if maybe_global_var => {
+                span = mod_.spans.inner_span;
+            },
             _ => break,
         }
     }
     Some(span)
 }
 
-fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
+fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
     let source_map = cx.sess().source_map();
     let ctxt = span.ctxt();
-    if ctxt == SyntaxContext::root()
-        && let Some(search_span) = get_body_search_span(cx)
-    {
+    if ctxt.is_root() && let Some(search_span) = get_body_search_span(cx) {
         if let Ok(unsafe_line) = source_map.lookup_line(span.lo())
             && let Some(body_span) = walk_span_to_context(search_span, SyntaxContext::root())
             && let Ok(body_line) = source_map.lookup_line(body_span.lo())
@@ -671,13 +648,11 @@ fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
             // Get the text from the start of function body to the unsafe block.
             //     fn foo() { some_stuff; unsafe { stuff }; other_stuff; }
             //              ^-------------^
-            unsafe_line.sf.lines(|lines| {
-                body_line.line < unsafe_line.line && text_has_safety_comment(
-                    src,
-                    &lines[body_line.line + 1..=unsafe_line.line],
-                    unsafe_line.sf.start_pos,
-                ).is_some()
-            })
+            body_line.line < unsafe_line.line && text_has_safety_comment(
+                src,
+                &unsafe_line.sf.lines()[body_line.line + 1..=unsafe_line.line],
+                unsafe_line.sf.start_pos,
+            ).is_some()
         } else {
             // Problem getting source text. Pretend a comment was found.
             true
diff --git a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
index dd829ded0d0..de4b8738e35 100644
--- a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
@@ -26,7 +26,7 @@ declare_clippy_lint! {
     ///
     /// ### Example
     /// ```rust
-    /// let mut twins = vec!((1, 1), (2, 2));
+    /// let mut twins = vec![(1, 1), (2, 2)];
     /// twins.sort_by_key(|x| { x.1; });
     /// ```
     #[clippy::version = "1.47.0"]
diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
index 9cf59577229..766a5481451 100644
--- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
@@ -68,7 +68,7 @@ impl EarlyLintPass for UnnestedOrPatterns {
 
     fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
         if self.msrv.meets(msrvs::OR_PATTERNS) {
-            if let ast::ExprKind::Let(pat, _, _) = &e.kind {
+            if let ast::ExprKind::Let(pat, _, _, _) = &e.kind {
                 lint_unnested_or_patterns(cx, pat);
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs
index c99b0290c0c..9a0d83d83f1 100644
--- a/src/tools/clippy/clippy_lints/src/unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/unwrap.rs
@@ -1,15 +1,18 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::usage::is_potentially_mutated;
+use clippy_utils::usage::is_potentially_local_place;
 use clippy_utils::{higher, path_to_local};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor};
-use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, PathSegment, UnOp};
+use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, PathSegment, UnOp};
+use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceWithHirId};
+use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::Ty;
+use rustc_middle::mir::FakeReadCause;
+use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::def_id::LocalDefId;
 use rustc_span::source_map::Span;
@@ -192,6 +195,55 @@ fn collect_unwrap_info<'tcx>(
     Vec::new()
 }
 
+/// A HIR visitor delegate that checks if a local variable of type `Option<_>` is mutated,
+/// *except* for if `Option::as_mut` is called.
+/// The reason for why we allow that one specifically is that `.as_mut()` cannot change
+/// the option to `None`, and that is important because this lint relies on the fact that
+/// `is_some` + `unwrap` is equivalent to `if let Some(..) = ..`, which it would not be if
+/// the option is changed to None between `is_some` and `unwrap`.
+/// (And also `.as_mut()` is a somewhat common method that is still worth linting on.)
+struct MutationVisitor<'tcx> {
+    is_mutated: bool,
+    local_id: HirId,
+    tcx: TyCtxt<'tcx>,
+}
+
+/// Checks if the parent of the expression pointed at by the given `HirId` is a call to
+/// `Option::as_mut`.
+///
+/// Used by the mutation visitor to specifically allow `.as_mut()` calls.
+/// In particular, the `HirId` that the visitor receives is the id of the local expression
+/// (i.e. the `x` in `x.as_mut()`), and that is the reason for why we care about its parent
+/// expression: that will be where the actual method call is.
+fn is_option_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool {
+    if let Node::Expr(mutating_expr) = tcx.hir().get_parent(expr_id)
+        && let ExprKind::MethodCall(path, ..) = mutating_expr.kind
+    {
+        path.ident.name.as_str() == "as_mut"
+    } else {
+        false
+    }
+}
+
+impl<'tcx> Delegate<'tcx> for MutationVisitor<'tcx> {
+    fn borrow(&mut self, cat: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) {
+        if let ty::BorrowKind::MutBorrow = bk
+            && is_potentially_local_place(self.local_id, &cat.place)
+            && !is_option_as_mut_use(self.tcx, diag_expr_id)
+        {
+            self.is_mutated = true;
+        }
+    }
+
+    fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {
+        self.is_mutated = true;
+    }
+
+    fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
+
+    fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {}
+}
+
 impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> {
     fn visit_branch(
         &mut self,
@@ -202,10 +254,26 @@ impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> {
     ) {
         let prev_len = self.unwrappables.len();
         for unwrap_info in collect_unwrap_info(self.cx, if_expr, cond, branch, else_branch, true) {
-            if is_potentially_mutated(unwrap_info.local_id, cond, self.cx)
-                || is_potentially_mutated(unwrap_info.local_id, branch, self.cx)
-            {
-                // if the variable is mutated, we don't know whether it can be unwrapped:
+            let mut delegate = MutationVisitor {
+                tcx: self.cx.tcx,
+                is_mutated: false,
+                local_id: unwrap_info.local_id,
+            };
+
+            let infcx = self.cx.tcx.infer_ctxt().build();
+            let mut vis = ExprUseVisitor::new(
+                &mut delegate,
+                &infcx,
+                cond.hir_id.owner.def_id,
+                self.cx.param_env,
+                self.cx.typeck_results(),
+            );
+            vis.walk_expr(cond);
+            vis.walk_expr(branch);
+
+            if delegate.is_mutated {
+                // if the variable is mutated, we don't know whether it can be unwrapped.
+                // it might have been changed to `None` in between `is_some` + `unwrap`.
                 continue;
             }
             self.unwrappables.push(unwrap_info);
@@ -215,6 +283,27 @@ impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> {
     }
 }
 
+enum AsRefKind {
+    AsRef,
+    AsMut,
+}
+
+/// Checks if the expression is a method call to `as_{ref,mut}` and returns the receiver of it.
+/// If it isn't, the expression itself is returned.
+fn consume_option_as_ref<'tcx>(expr: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, Option<AsRefKind>) {
+    if let ExprKind::MethodCall(path, recv, ..) = expr.kind {
+        if path.ident.name == sym::as_ref {
+            (recv, Some(AsRefKind::AsRef))
+        } else if path.ident.name.as_str() == "as_mut" {
+            (recv, Some(AsRefKind::AsMut))
+        } else {
+            (expr, None)
+        }
+    } else {
+        (expr, None)
+    }
+}
+
 impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
     type NestedFilter = nested_filter::OnlyBodies;
 
@@ -233,6 +322,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
             // find `unwrap[_err]()` calls:
             if_chain! {
                 if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind;
+                let (self_arg, as_ref_kind) = consume_option_as_ref(self_arg);
                 if let Some(id) = path_to_local(self_arg);
                 if [sym::unwrap, sym::expect, sym!(unwrap_err)].contains(&method_name.ident.name);
                 let call_to_unwrap = [sym::unwrap, sym::expect].contains(&method_name.ident.name);
@@ -268,7 +358,12 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
                                         unwrappable.check.span.with_lo(unwrappable.if_expr.span.lo()),
                                         "try",
                                         format!(
-                                            "if let {suggested_pattern} = {unwrappable_variable_name}",
+                                            "if let {suggested_pattern} = {borrow_prefix}{unwrappable_variable_name}",
+                                            borrow_prefix = match as_ref_kind {
+                                                Some(AsRefKind::AsRef) => "&",
+                                                Some(AsRefKind::AsMut) => "&mut ",
+                                                None => "",
+                                            },
                                         ),
                                         // We don't track how the unwrapped value is used inside the
                                         // block or suggest deleting the unwrap, so we can't offer a
diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs
index 58ae0656db7..26889475522 100644
--- a/src/tools/clippy/clippy_lints/src/utils/conf.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs
@@ -561,6 +561,26 @@ define_Conf! {
     /// Which crates to allow absolute paths from
     (absolute_paths_allowed_crates: rustc_data_structures::fx::FxHashSet<String> =
         rustc_data_structures::fx::FxHashSet::default()),
+    /// Lint: EXPLICIT_ITER_LOOP
+    ///
+    /// Whether to recommend using implicit into iter for reborrowed values.
+    ///
+    /// #### Example
+    /// ```
+    /// let mut vec = vec![1, 2, 3];
+    /// let rmvec = &mut vec;
+    /// for _ in rmvec.iter() {}
+    /// for _ in rmvec.iter_mut() {}
+    /// ```
+    ///
+    /// Use instead:
+    /// ```
+    /// let mut vec = vec![1, 2, 3];
+    /// let rmvec = &mut vec;
+    /// for _ in &*rmvec {}
+    /// for _ in &mut *rmvec {}
+    /// ```
+    (enforce_iter_loop_reborrow: bool = false),
 }
 
 /// Search for the configuration file.
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
index f66f33fee16..4a5b6fa5c18 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
@@ -232,7 +232,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve
                 cx.tcx.type_of(def_id).instantiate_identity(),
             ),
             Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? {
-                ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => {
+                ConstValue::Indirect { alloc, offset } if offset.bytes() == 0 => {
                     read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id).instantiate_identity())
                 },
                 _ => None,
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index 140cfa2194f..a78ff02021f 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -166,7 +166,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
         (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r),
         (Lit(l), Lit(r)) => l == r,
         (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt),
-        (Let(lp, le, _), Let(rp, re, _)) => eq_pat(lp, rp) && eq_expr(le, re),
+        (Let(lp, le, _, _), Let(rp, re, _, _)) => eq_pat(lp, rp) && eq_expr(le, re),
         (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re),
         (While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt),
         (ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => {
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index adeb673b6b9..fcb90c63a6f 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -671,47 +671,41 @@ pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'t
             ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))),
             _ => None,
         },
-        mir::ConstantKind::Val(ConstValue::Slice { data, start, end }, _) => match result.ty().kind() {
-            ty::Ref(_, tam, _) => match tam.kind() {
-                ty::Str => String::from_utf8(
-                    data.inner()
-                        .inspect_with_uninit_and_ptr_outside_interpreter(start..end)
-                        .to_owned(),
-                )
-                .ok()
-                .map(Constant::Str),
-                _ => None,
-            },
-            _ => None,
-        },
-        mir::ConstantKind::Val(ConstValue::ByRef { alloc, offset: _ }, _) => match result.ty().kind() {
-            ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)),
-            ty::Array(sub_type, len) => match sub_type.kind() {
-                ty::Float(FloatTy::F32) => match len.try_to_target_usize(lcx.tcx) {
-                    Some(len) => alloc
-                        .inner()
-                        .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap()))
-                        .to_owned()
-                        .array_chunks::<4>()
-                        .map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk))))
-                        .collect::<Option<Vec<Constant<'tcx>>>>()
-                        .map(Constant::Vec),
-                    _ => None,
-                },
-                ty::Float(FloatTy::F64) => match len.try_to_target_usize(lcx.tcx) {
-                    Some(len) => alloc
-                        .inner()
-                        .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap()))
-                        .to_owned()
-                        .array_chunks::<8>()
-                        .map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk))))
-                        .collect::<Option<Vec<Constant<'tcx>>>>()
-                        .map(Constant::Vec),
+        mir::ConstantKind::Val(cv, _) if matches!(result.ty().kind(), ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Str)) => {
+            let data = cv.try_get_slice_bytes_for_diagnostics(lcx.tcx)?;
+            String::from_utf8(data.to_owned()).ok().map(Constant::Str)
+        }
+        mir::ConstantKind::Val(ConstValue::Indirect { alloc_id, offset: _ }, _) => {
+            let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory();
+            match result.ty().kind() {
+                ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)),
+                ty::Array(sub_type, len) => match sub_type.kind() {
+                    ty::Float(FloatTy::F32) => match len.try_to_target_usize(lcx.tcx) {
+                        Some(len) => alloc
+                            .inner()
+                            .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap()))
+                            .to_owned()
+                            .array_chunks::<4>()
+                            .map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk))))
+                            .collect::<Option<Vec<Constant<'tcx>>>>()
+                            .map(Constant::Vec),
+                        _ => None,
+                    },
+                    ty::Float(FloatTy::F64) => match len.try_to_target_usize(lcx.tcx) {
+                        Some(len) => alloc
+                            .inner()
+                            .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap()))
+                            .to_owned()
+                            .array_chunks::<8>()
+                            .map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk))))
+                            .collect::<Option<Vec<Constant<'tcx>>>>()
+                            .map(Constant::Vec),
+                        _ => None,
+                    },
                     _ => None,
                 },
                 _ => None,
-            },
-            _ => None,
+            }
         },
         _ => None,
     }
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 6c4cec59524..4ef3ec19647 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -5,6 +5,7 @@
 #![feature(lint_reasons)]
 #![feature(never_type)]
 #![feature(rustc_private)]
+#![feature(assert_matches)]
 #![recursion_limit = "512"]
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
@@ -110,6 +111,7 @@ use rustc_span::source_map::SourceMap;
 use rustc_span::symbol::{kw, Ident, Symbol};
 use rustc_span::{sym, Span};
 use rustc_target::abi::Integer;
+use visitors::Visitable;
 
 use crate::consts::{constant, miri_to_const, Constant};
 use crate::higher::Range;
@@ -1286,7 +1288,7 @@ pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<
 }
 
 /// Returns `true` if `expr` contains a return expression
-pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
+pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
     for_each_expr(expr, |e| {
         if matches!(e.kind, hir::ExprKind::Ret(..)) {
             ControlFlow::Break(())
diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs
index a567c5cbdc9..2fb24b5c7ed 100644
--- a/src/tools/clippy/clippy_utils/src/paths.rs
+++ b/src/tools/clippy/clippy_utils/src/paths.rs
@@ -49,6 +49,7 @@ pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
 pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
 pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
 pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"];
+pub const ITER_ONCE: [&str; 5] = ["core", "iter", "sources", "once", "Once"];
 pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
 #[cfg(feature = "internal")]
 pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
@@ -163,3 +164,4 @@ pub const DEBUG_STRUCT: [&str; 4] = ["core", "fmt", "builders", "DebugStruct"];
 pub const ORD_CMP: [&str; 4] = ["core", "cmp", "Ord", "cmp"];
 #[expect(clippy::invalid_paths)] // not sure why it thinks this, it works so
 pub const BOOL_THEN: [&str; 4] = ["core", "bool", "<impl bool>", "then"];
+pub const ARRAY_INTO_ITER: [&str; 4] = ["core", "array", "iter", "IntoIter"];
diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs
index 03416d35ba4..31cb421095e 100644
--- a/src/tools/clippy/clippy_utils/src/source.rs
+++ b/src/tools/clippy/clippy_utils/src/source.rs
@@ -118,7 +118,7 @@ fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePo
 fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
     let span = original_sp(span, DUMMY_SP);
     let SourceFileAndLine { sf, line } = cx.sess().source_map().lookup_line(span.lo()).unwrap();
-    let line_start = sf.lines(|lines| lines[line]);
+    let line_start = sf.lines()[line];
     let line_start = sf.absolute_position(line_start);
     span.with_lo(line_start)
 }
@@ -362,7 +362,7 @@ pub fn snippet_block_with_context<'a>(
 }
 
 /// Same as `snippet_with_applicability`, but first walks the span up to the given context. This
-/// will result in the macro call, rather then the expansion, if the span is from a child context.
+/// will result in the macro call, rather than the expansion, if the span is from a child context.
 /// If the span is not from a child context, it will be used directly instead.
 ///
 /// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index ee5a49a2073..ae8ee371ffa 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -88,7 +88,7 @@ impl<'a> Sugg<'a> {
     }
 
     /// Same as `hir`, but first walks the span up to the given context. This will result in the
-    /// macro call, rather then the expansion, if the span is from a child context. If the span is
+    /// macro call, rather than the expansion, if the span is from a child context. If the span is
     /// not from a child context, it will be used directly instead.
     ///
     /// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index a05f682aa8c..f0b4ede35fb 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -27,6 +27,7 @@ use rustc_target::abi::{Size, VariantIdx};
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
 use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
 use rustc_trait_selection::traits::{Obligation, ObligationCause};
+use std::assert_matches::debug_assert_matches;
 use std::iter;
 
 use crate::{match_def_path, path_res, paths};
@@ -259,7 +260,11 @@ pub fn implements_trait_with_env_from_iter<'tcx>(
             })),
     );
 
-    debug_assert_eq!(tcx.def_kind(trait_id), DefKind::Trait);
+    debug_assert_matches!(
+        tcx.def_kind(trait_id),
+        DefKind::Trait | DefKind::TraitAlias,
+        "`DefId` must belong to a trait or trait alias"
+    );
     #[cfg(debug_assertions)]
     assert_generic_args_match(tcx, trait_id, trait_ref.args);
 
diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
index 06fd9529044..dc7756533ac 100644
--- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
@@ -150,7 +150,7 @@ fn generic_args_certainty(cx: &LateContext<'_>, args: &GenericArgs<'_>) -> Certa
 }
 
 /// Tries to tell whether a `QPath` resolves to something certain, e.g., whether all of its path
-/// segments generic arguments are are instantiated.
+/// segments generic arguments are instantiated.
 ///
 /// `qpath` could refer to either a type or a value. The heuristic never needs the `DefId` of a
 /// value. So `DefId`s are retained only when `resolves_to_type` is true.
diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs
index 39ef76348d7..ec131c7f6a3 100644
--- a/src/tools/clippy/clippy_utils/src/usage.rs
+++ b/src/tools/clippy/clippy_utils/src/usage.rs
@@ -4,7 +4,7 @@ use core::ops::ControlFlow;
 use hir::def::Res;
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{self as hir, Expr, ExprKind, HirId, HirIdSet};
-use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
+use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, Place, PlaceBase, PlaceWithHirId};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::LateContext;
 use rustc_middle::hir::nested_filter;
@@ -37,6 +37,17 @@ pub fn is_potentially_mutated<'tcx>(variable: HirId, expr: &'tcx Expr<'_>, cx: &
     mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&variable))
 }
 
+pub fn is_potentially_local_place(local_id: HirId, place: &Place<'_>) -> bool {
+    match place.base {
+        PlaceBase::Local(id) => id == local_id,
+        PlaceBase::Upvar(_) => {
+            // Conservatively assume yes.
+            true
+        },
+        _ => false,
+    }
+}
+
 struct MutVarsDelegate {
     used_mutably: HirIdSet,
     skip: bool,
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index 19c09b58b98..9f5116eb73b 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2023-08-24"
+channel = "nightly-2023-09-07"
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs
index e329a94ff6a..9fcc269dbf8 100644
--- a/src/tools/clippy/tests/compile-test.rs
+++ b/src/tools/clippy/tests/compile-test.rs
@@ -4,7 +4,7 @@
 #![warn(rust_2018_idioms, unused_lifetimes)]
 #![allow(unused_extern_crates)]
 
-use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, Mode, OutputConflictHandling, RustfixMode};
+use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, Mode, OutputConflictHandling};
 
 use std::collections::BTreeMap;
 use std::env::{self, set_var, var_os};
@@ -114,34 +114,26 @@ fn canonicalize(path: impl AsRef<Path>) -> PathBuf {
 }
 
 fn base_config(test_dir: &str) -> (Config, Args) {
-    let bless = var_os("RUSTC_BLESS").is_some_and(|v| v != "0") || env::args().any(|arg| arg == "--bless");
-
-    let args = Args {
-        filters: env::var("TESTNAME")
-            .map(|filters| filters.split(',').map(str::to_string).collect())
-            .unwrap_or_default(),
-        quiet: false,
-        check: !bless,
-        threads: match std::env::var_os("RUST_TEST_THREADS") {
-            Some(n) => n.to_str().unwrap().parse().unwrap(),
-            None => std::thread::available_parallelism().unwrap(),
-        },
-        skip: Vec::new(),
-    };
+    let mut args = Args::test().unwrap();
+    args.bless |= var_os("RUSTC_BLESS").is_some_and(|v| v != "0");
 
     let mut config = Config {
-        mode: Mode::Yolo { rustfix: RustfixMode::Everything },
+        mode: Mode::Yolo {
+            rustfix: ui_test::RustfixMode::Everything,
+        },
         stderr_filters: vec![(Match::PathBackslash, b"/")],
         stdout_filters: vec![],
-        output_conflict_handling: if bless {
-            OutputConflictHandling::Bless
-        } else {
-            OutputConflictHandling::Error("cargo uibless".into())
-        },
+        filter_files: env::var("TESTNAME")
+            .map(|filters| filters.split(',').map(str::to_string).collect())
+            .unwrap_or_default(),
         target: None,
         out_dir: canonicalize(var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into())).join("ui_test"),
         ..Config::rustc(Path::new("tests").join(test_dir))
     };
+    config.with_args(&args, /* bless by default */ false);
+    if let OutputConflictHandling::Error(err) = &mut config.output_conflict_handling {
+        *err = "cargo uibless".into();
+    }
     let current_exe_path = env::current_exe().unwrap();
     let deps_path = current_exe_path.parent().unwrap();
     let profile_path = deps_path.parent().unwrap();
@@ -184,7 +176,6 @@ fn run_ui() {
 
     ui_test::run_tests_generic(
         vec![config],
-        args,
         ui_test::default_file_filter,
         ui_test::default_per_file_config,
         if quiet {
@@ -209,7 +200,6 @@ fn run_internal_tests() {
 
     ui_test::run_tests_generic(
         vec![config],
-        args,
         ui_test::default_file_filter,
         ui_test::default_per_file_config,
         if quiet {
@@ -243,7 +233,6 @@ fn run_ui_toml() {
 
     ui_test::run_tests_generic(
         vec![config],
-        args,
         ui_test::default_file_filter,
         |config, path, _file_contents| {
             config
@@ -300,10 +289,16 @@ fn run_ui_cargo() {
 
     let quiet = args.quiet;
 
+    let ignored_32bit = |path: &Path| {
+        // FIXME: for some reason the modules are linted in a different order for this test
+        cfg!(target_pointer_width = "32") && path.ends_with("tests/ui-cargo/module_style/fail_mod/Cargo.toml")
+    };
+
     ui_test::run_tests_generic(
         vec![config],
-        args,
-        |path, args, _config| path.ends_with("Cargo.toml") && ui_test::default_filter_by_arg(path, args),
+        |path, config| {
+            path.ends_with("Cargo.toml") && ui_test::default_any_file_filter(path, config) && !ignored_32bit(path)
+        },
         |config, path, _file_contents| {
             config.out_dir = canonicalize(
                 std::env::current_dir()
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/Cargo.stderr
index e161507b533..4d97d54963d 100644
--- a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/Cargo.stderr
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/Cargo.stderr
@@ -1,6 +1,7 @@
 error: package `cargo_common_metadata_fail` is missing `package.description` metadata
   |
   = note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
+  = help: to override `-D warnings` add `#[allow(clippy::cargo_common_metadata)]`
 
 error: package `cargo_common_metadata_fail` is missing `either package.license or package.license_file` metadata
 
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.stderr
index dbf494cc342..9eb884f0890 100644
--- a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.stderr
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.stderr
@@ -1,6 +1,7 @@
 error: package `cargo_common_metadata_fail_publish` is missing `package.description` metadata
   |
   = note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
+  = help: to override `-D warnings` add `#[allow(clippy::cargo_common_metadata)]`
 
 error: package `cargo_common_metadata_fail_publish` is missing `either package.license or package.license_file` metadata
 
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.stderr
index ae5967406f6..f9685a784a3 100644
--- a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.stderr
+++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.stderr
@@ -1,6 +1,7 @@
 error: package `cargo_common_metadata_fail_publish_true` is missing `package.description` metadata
   |
   = note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
+  = help: to override `-D warnings` add `#[allow(clippy::cargo_common_metadata)]`
 
 error: package `cargo_common_metadata_fail_publish_true` is missing `either package.license or package.license_file` metadata
 
diff --git a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr
index fde3a1e6599..912ea9bb4d1 100644
--- a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr
+++ b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr
@@ -9,6 +9,7 @@ error: file is loaded as a module multiple times: `src/b.rs`
   |
   = help: replace all but one `mod` item with `use` items
   = note: `-D clippy::duplicate-mod` implied by `-D warnings`
+  = help: to override `-D warnings` add `#[allow(clippy::duplicate_mod)]`
 
 error: file is loaded as a module multiple times: `src/c.rs`
   --> src/main.rs:9:1
diff --git a/src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.stderr
index da2db45d3b8..388f49fb213 100644
--- a/src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.stderr
+++ b/src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.stderr
@@ -2,6 +2,7 @@ error: the "no-" prefix in the feature name "no-qaq" is negative
   |
   = help: consider renaming the feature to "qaq", but make sure the feature adds functionality
   = note: `-D clippy::negative-feature-names` implied by `-D warnings`
+  = help: to override `-D warnings` add `#[allow(clippy::negative_feature_names)]`
 
 error: the "no_" prefix in the feature name "no_qaq" is negative
   |
@@ -19,6 +20,7 @@ error: the "-support" suffix in the feature name "qvq-support" is redundant
   |
   = help: consider renaming the feature to "qvq"
   = note: `-D clippy::redundant-feature-names` implied by `-D warnings`
+  = help: to override `-D warnings` add `#[allow(clippy::redundant_feature_names)]`
 
 error: the "_support" suffix in the feature name "qvq_support" is redundant
   |
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.stderr
index c2907f319e6..902330e1785 100644
--- a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.stderr
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.stderr
@@ -6,6 +6,7 @@ error: `mod.rs` files are required, found `src/bad/inner.rs`
   |
   = help: move `src/bad/inner.rs` to `src/bad/inner/mod.rs`
   = note: `-D clippy::self-named-module-files` implied by `-D warnings`
+  = help: to override `-D warnings` add `#[allow(clippy::self_named_module_files)]`
 
 error: `mod.rs` files are required, found `src/bad/inner/stuff.rs`
  --> src/bad/inner/stuff.rs:1:1
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr
index fcf1a3c5e66..d776feb7f2e 100644
--- a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr
@@ -6,5 +6,6 @@ error: `mod.rs` files are required, found `src/bad.rs`
   |
   = help: move `src/bad.rs` to `src/bad/mod.rs`
   = note: `-D clippy::self-named-module-files` implied by `-D warnings`
+  = help: to override `-D warnings` add `#[allow(clippy::self_named_module_files)]`
 
 error: could not compile `fail-mod-remap` (bin "fail-mod-remap") due to previous error
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr
index f61642ca2ef..22558bc4ce8 100644
--- a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr
@@ -6,5 +6,6 @@ error: `mod.rs` files are not allowed, found `src/bad/mod.rs`
   |
   = help: move `src/bad/mod.rs` to `src/bad.rs`
   = note: `-D clippy::mod-module-files` implied by `-D warnings`
+  = help: to override `-D warnings` add `#[allow(clippy::mod_module_files)]`
 
 error: could not compile `fail-no-mod` (bin "fail-no-mod") due to previous error
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr
index 5bcce920455..4beedc10830 100644
--- a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr
+++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr
@@ -1,5 +1,6 @@
 error: multiple versions for dependency `winapi`: 0.2.8, 0.3.9
   |
   = note: `-D clippy::multiple-crate-versions` implied by `-D warnings`
+  = help: to override `-D warnings` add `#[allow(clippy::multiple_crate_versions)]`
 
 error: could not compile `multiple_crate_versions` (bin "multiple_crate_versions") due to previous error
diff --git a/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr
index b1578c9f324..65a19bb0718 100644
--- a/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr
+++ b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr
@@ -1,5 +1,6 @@
 error: wildcard dependency for `regex`
   |
   = note: `-D clippy::wildcard-dependencies` implied by `-D warnings`
+  = help: to override `-D warnings` add `#[allow(clippy::wildcard_dependencies)]`
 
 error: could not compile `wildcard_dependencies` (bin "wildcard_dependencies") due to previous error
diff --git a/src/tools/clippy/tests/ui-internal/check_formulation.stderr b/src/tools/clippy/tests/ui-internal/check_formulation.stderr
index 10eabca4b9d..96fa617601f 100644
--- a/src/tools/clippy/tests/ui-internal/check_formulation.stderr
+++ b/src/tools/clippy/tests/ui-internal/check_formulation.stderr
@@ -6,6 +6,7 @@ LL |     /// Check for lint formulations that are correct
    |
    = help: try using `Checks for` instead
    = note: `-D clippy::almost-standard-lint-formulation` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::almost_standard_lint_formulation)]`
 
 error: non-standard lint formulation
   --> $DIR/check_formulation.rs:33:5
diff --git a/src/tools/clippy/tests/ui-internal/if_chain_style.stderr b/src/tools/clippy/tests/ui-internal/if_chain_style.stderr
index b12df278652..ea04955323d 100644
--- a/src/tools/clippy/tests/ui-internal/if_chain_style.stderr
+++ b/src/tools/clippy/tests/ui-internal/if_chain_style.stderr
@@ -16,6 +16,7 @@ help: this `let` statement can also be in the `if_chain!`
 LL |         let x = "";
    |         ^^^^^^^^^^^
    = note: `-D clippy::if-chain-style` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::if_chain_style)]`
 
 error: `if a && b;` should be `if a; if b;`
   --> $DIR/if_chain_style.rs:24:12
diff --git a/src/tools/clippy/tests/ui-internal/invalid_paths.stderr b/src/tools/clippy/tests/ui-internal/invalid_paths.stderr
index 0e850886917..988d32d5259 100644
--- a/src/tools/clippy/tests/ui-internal/invalid_paths.stderr
+++ b/src/tools/clippy/tests/ui-internal/invalid_paths.stderr
@@ -5,6 +5,7 @@ LL |     pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::invalid-paths` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::invalid_paths)]`
 
 error: invalid path
   --> $DIR/invalid_paths.rs:18:5
diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
index 3ca45404e44..58b1fd92b5d 100644
--- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
+++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
@@ -6,6 +6,7 @@ LL |     const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"];
    |
    = help: convert all references to use `sym::Deref`
    = note: `-D clippy::unnecessary-def-path` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::unnecessary_def_path)]`
 
 error: hardcoded path to a language item
   --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40
diff --git a/src/tools/clippy/tests/ui-toml/disallowed_macros/auxiliary/macros.rs b/src/tools/clippy/tests/ui-toml/disallowed_macros/auxiliary/macros.rs
index fcaeace0e98..f4166b227fc 100644
--- a/src/tools/clippy/tests/ui-toml/disallowed_macros/auxiliary/macros.rs
+++ b/src/tools/clippy/tests/ui-toml/disallowed_macros/auxiliary/macros.rs
@@ -30,3 +30,18 @@ macro_rules! item {
         const ITEM: usize = 1;
     };
 }
+
+#[macro_export]
+macro_rules! binop {
+    ($t:tt) => {
+        $t + $t
+    };
+}
+
+#[macro_export]
+macro_rules! attr {
+    ($i:item) => {
+        #[repr(C)]
+        $i
+    };
+}
diff --git a/src/tools/clippy/tests/ui-toml/disallowed_macros/clippy.toml b/src/tools/clippy/tests/ui-toml/disallowed_macros/clippy.toml
index c8fe8be9a77..85f1b71eb66 100644
--- a/src/tools/clippy/tests/ui-toml/disallowed_macros/clippy.toml
+++ b/src/tools/clippy/tests/ui-toml/disallowed_macros/clippy.toml
@@ -8,4 +8,6 @@ disallowed-macros = [
     "macros::ty",
     "macros::pat",
     "macros::item",
+    "macros::binop",
+    "macros::attr",
 ]
diff --git a/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.rs b/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.rs
index ba919b48788..4a3d55e13c9 100644
--- a/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.rs
+++ b/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.rs
@@ -20,11 +20,14 @@ fn main() {
     let macros::pat!() = 1;
     let _: macros::ty!() = "";
     macros::item!();
+    let _ = macros::binop!(1);
 
     eprintln!("allowed");
 }
 
-struct S;
+macros::attr! {
+    struct S;
+}
 
 impl S {
     macros::item!();
diff --git a/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr b/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr
index 0eebd587a5f..3c6f59b16e7 100644
--- a/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr
+++ b/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr
@@ -63,23 +63,37 @@ error: use of a disallowed macro `macros::item`
 LL |     macros::item!();
    |     ^^^^^^^^^^^^^^^
 
+error: use of a disallowed macro `macros::binop`
+  --> $DIR/disallowed_macros.rs:23:13
+   |
+LL |     let _ = macros::binop!(1);
+   |             ^^^^^^^^^^^^^^^^^
+
+error: use of a disallowed macro `macros::attr`
+  --> $DIR/disallowed_macros.rs:28:1
+   |
+LL | / macros::attr! {
+LL | |     struct S;
+LL | | }
+   | |_^
+
 error: use of a disallowed macro `macros::item`
-  --> $DIR/disallowed_macros.rs:30:5
+  --> $DIR/disallowed_macros.rs:33:5
    |
 LL |     macros::item!();
    |     ^^^^^^^^^^^^^^^
 
 error: use of a disallowed macro `macros::item`
-  --> $DIR/disallowed_macros.rs:34:5
+  --> $DIR/disallowed_macros.rs:37:5
    |
 LL |     macros::item!();
    |     ^^^^^^^^^^^^^^^
 
 error: use of a disallowed macro `macros::item`
-  --> $DIR/disallowed_macros.rs:38:5
+  --> $DIR/disallowed_macros.rs:41:5
    |
 LL |     macros::item!();
    |     ^^^^^^^^^^^^^^^
 
-error: aborting due to 13 previous errors
+error: aborting due to 15 previous errors
 
diff --git a/src/tools/clippy/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs b/src/tools/clippy/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs
deleted file mode 100644
index 60fbaaea3d3..00000000000
--- a/src/tools/clippy/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs
+++ /dev/null
@@ -1,469 +0,0 @@
-// NOTE: Copied from `ui/auxiliary/proc_macros.rs`, couldn't get `../` to work for some reason
-
-#![feature(let_chains)]
-#![feature(proc_macro_span)]
-#![allow(clippy::excessive_nesting, dead_code)]
-
-extern crate proc_macro;
-
-use core::mem;
-use proc_macro::token_stream::IntoIter;
-use proc_macro::Delimiter::{self, Brace, Parenthesis};
-use proc_macro::Spacing::{self, Alone, Joint};
-use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree as TT};
-
-type Result<T> = core::result::Result<T, TokenStream>;
-
-/// Make a `compile_error!` pointing to the given span.
-fn make_error(msg: &str, span: Span) -> TokenStream {
-    TokenStream::from_iter([
-        TT::Ident(Ident::new("compile_error", span)),
-        TT::Punct(punct_with_span('!', Alone, span)),
-        TT::Group({
-            let mut msg = Literal::string(msg);
-            msg.set_span(span);
-            group_with_span(Parenthesis, TokenStream::from_iter([TT::Literal(msg)]), span)
-        }),
-    ])
-}
-
-fn expect_tt<T>(tt: Option<TT>, f: impl FnOnce(TT) -> Option<T>, expected: &str, span: Span) -> Result<T> {
-    match tt {
-        None => Err(make_error(
-            &format!("unexpected end of input, expected {expected}"),
-            span,
-        )),
-        Some(tt) => {
-            let span = tt.span();
-            match f(tt) {
-                Some(x) => Ok(x),
-                None => Err(make_error(&format!("unexpected token, expected {expected}"), span)),
-            }
-        },
-    }
-}
-
-fn punct_with_span(c: char, spacing: Spacing, span: Span) -> Punct {
-    let mut p = Punct::new(c, spacing);
-    p.set_span(span);
-    p
-}
-
-fn group_with_span(delimiter: Delimiter, stream: TokenStream, span: Span) -> Group {
-    let mut g = Group::new(delimiter, stream);
-    g.set_span(span);
-    g
-}
-
-/// Token used to escape the following token from the macro's span rules.
-const ESCAPE_CHAR: char = '$';
-
-/// Takes a single token followed by a sequence of tokens. Returns the sequence of tokens with their
-/// span set to that of the first token. Tokens may be escaped with either `#ident` or `#(tokens)`.
-#[proc_macro]
-pub fn with_span(input: TokenStream) -> TokenStream {
-    let mut iter = input.into_iter();
-    let span = iter.next().unwrap().span();
-    let mut res = TokenStream::new();
-    if let Err(e) = write_with_span(span, iter, &mut res) {
-        e
-    } else {
-        res
-    }
-}
-
-/// Takes a sequence of tokens and return the tokens with the span set such that they appear to be
-/// from an external macro. Tokens may be escaped with either `#ident` or `#(tokens)`.
-#[proc_macro]
-pub fn external(input: TokenStream) -> TokenStream {
-    let mut res = TokenStream::new();
-    if let Err(e) = write_with_span(Span::mixed_site(), input.into_iter(), &mut res) {
-        e
-    } else {
-        res
-    }
-}
-
-/// Copies all the tokens, replacing all their spans with the given span. Tokens can be escaped
-/// either by `#ident` or `#(tokens)`.
-fn write_with_span(s: Span, mut input: IntoIter, out: &mut TokenStream) -> Result<()> {
-    while let Some(tt) = input.next() {
-        match tt {
-            TT::Punct(p) if p.as_char() == ESCAPE_CHAR => {
-                expect_tt(
-                    input.next(),
-                    |tt| match tt {
-                        tt @ (TT::Ident(_) | TT::Literal(_)) => {
-                            out.extend([tt]);
-                            Some(())
-                        },
-                        TT::Punct(mut p) if p.as_char() == ESCAPE_CHAR => {
-                            p.set_span(s);
-                            out.extend([TT::Punct(p)]);
-                            Some(())
-                        },
-                        TT::Group(g) if g.delimiter() == Parenthesis => {
-                            out.extend([TT::Group(group_with_span(Delimiter::None, g.stream(), g.span()))]);
-                            Some(())
-                        },
-                        _ => None,
-                    },
-                    "an ident, a literal, or parenthesized tokens",
-                    p.span(),
-                )?;
-            },
-            TT::Group(g) => {
-                let mut stream = TokenStream::new();
-                write_with_span(s, g.stream().into_iter(), &mut stream)?;
-                out.extend([TT::Group(group_with_span(g.delimiter(), stream, s))]);
-            },
-            mut tt => {
-                tt.set_span(s);
-                out.extend([tt]);
-            },
-        }
-    }
-    Ok(())
-}
-
-/// Within the item this attribute is attached to, an `inline!` macro is available which expands the
-/// contained tokens as though they came from a macro expansion.
-///
-/// Within the `inline!` macro, any token preceded by `$` is passed as though it were an argument
-/// with an automatically chosen fragment specifier. `$ident` will be passed as `ident`, `$1` or
-/// `$"literal"` will be passed as `literal`, `$'lt` will be passed as `lifetime`, and `$(...)` will
-/// pass the contained tokens as a `tt` sequence (the wrapping parenthesis are removed). If another
-/// specifier is required it can be specified within parenthesis like `$(@expr ...)`. This will
-/// expand the remaining tokens as a single argument.
-///
-/// Multiple `inline!` macros may be nested within each other. This will expand as nested macro
-/// calls. However, any arguments will be passed as though they came from the outermost context.
-#[proc_macro_attribute]
-pub fn inline_macros(args: TokenStream, input: TokenStream) -> TokenStream {
-    let mut args = args.into_iter();
-    let mac_name = match args.next() {
-        Some(TT::Ident(name)) => Some(name),
-        Some(tt) => {
-            return make_error(
-                "unexpected argument, expected either an ident or no arguments",
-                tt.span(),
-            );
-        },
-        None => None,
-    };
-    if let Some(tt) = args.next() {
-        return make_error(
-            "unexpected argument, expected either an ident or no arguments",
-            tt.span(),
-        );
-    };
-
-    let mac_name = if let Some(mac_name) = mac_name {
-        Ident::new(&format!("__inline_mac_{mac_name}"), Span::call_site())
-    } else {
-        let mut input = match LookaheadIter::new(input.clone().into_iter()) {
-            Some(x) => x,
-            None => return input,
-        };
-        loop {
-            match input.next() {
-                None => break Ident::new("__inline_mac", Span::call_site()),
-                Some(TT::Ident(kind)) => match &*kind.to_string() {
-                    "impl" => break Ident::new("__inline_mac_impl", Span::call_site()),
-                    kind @ ("struct" | "enum" | "union" | "fn" | "mod" | "trait" | "type" | "const" | "static") => {
-                        if let TT::Ident(name) = &input.tt {
-                            break Ident::new(&format!("__inline_mac_{kind}_{name}"), Span::call_site());
-                        } else {
-                            break Ident::new(&format!("__inline_mac_{kind}"), Span::call_site());
-                        }
-                    },
-                    _ => {},
-                },
-                _ => {},
-            }
-        }
-    };
-
-    let mut expander = Expander::default();
-    let mut mac = MacWriter::new(mac_name);
-    if let Err(e) = expander.expand(input.into_iter(), &mut mac) {
-        return e;
-    }
-    let mut out = TokenStream::new();
-    mac.finish(&mut out);
-    out.extend(expander.expn);
-    out
-}
-
-/// Wraps a `TokenStream` iterator with a single token lookahead.
-struct LookaheadIter {
-    tt: TT,
-    iter: IntoIter,
-}
-impl LookaheadIter {
-    fn new(mut iter: IntoIter) -> Option<Self> {
-        iter.next().map(|tt| Self { tt, iter })
-    }
-
-    /// Get's the lookahead token, replacing it with the next token in the stream.
-    /// Note: If there isn't a next token, this will not return the lookahead token.
-    fn next(&mut self) -> Option<TT> {
-        self.iter.next().map(|tt| mem::replace(&mut self.tt, tt))
-    }
-}
-
-/// Builds the macro used to implement all the `inline!` macro calls.
-struct MacWriter {
-    name: Ident,
-    macros: TokenStream,
-    next_idx: usize,
-}
-impl MacWriter {
-    fn new(name: Ident) -> Self {
-        Self {
-            name,
-            macros: TokenStream::new(),
-            next_idx: 0,
-        }
-    }
-
-    /// Inserts a new `inline!` call.
-    fn insert(&mut self, name_span: Span, bang_span: Span, body: Group, expander: &mut Expander) -> Result<()> {
-        let idx = self.next_idx;
-        self.next_idx += 1;
-
-        let mut inner = Expander::for_arm(idx);
-        inner.expand(body.stream().into_iter(), self)?;
-        let new_arm = inner.arm.unwrap();
-
-        self.macros.extend([
-            TT::Group(Group::new(Parenthesis, new_arm.args_def)),
-            TT::Punct(Punct::new('=', Joint)),
-            TT::Punct(Punct::new('>', Alone)),
-            TT::Group(Group::new(Parenthesis, inner.expn)),
-            TT::Punct(Punct::new(';', Alone)),
-        ]);
-
-        expander.expn.extend([
-            TT::Ident({
-                let mut name = self.name.clone();
-                name.set_span(name_span);
-                name
-            }),
-            TT::Punct(punct_with_span('!', Alone, bang_span)),
-        ]);
-        let mut call_body = TokenStream::from_iter([TT::Literal(Literal::usize_unsuffixed(idx))]);
-        if let Some(arm) = expander.arm.as_mut() {
-            if !new_arm.args.is_empty() {
-                arm.add_sub_args(new_arm.args, &mut call_body);
-            }
-        } else {
-            call_body.extend(new_arm.args);
-        }
-        let mut g = Group::new(body.delimiter(), call_body);
-        g.set_span(body.span());
-        expander.expn.extend([TT::Group(g)]);
-        Ok(())
-    }
-
-    /// Creates the macro definition.
-    fn finish(self, out: &mut TokenStream) {
-        if self.next_idx != 0 {
-            out.extend([
-                TT::Ident(Ident::new("macro_rules", Span::call_site())),
-                TT::Punct(Punct::new('!', Alone)),
-                TT::Ident(self.name),
-                TT::Group(Group::new(Brace, self.macros)),
-            ])
-        }
-    }
-}
-
-struct MacroArm {
-    args_def: TokenStream,
-    args: Vec<TT>,
-}
-impl MacroArm {
-    fn add_single_arg_def(&mut self, kind: &str, dollar_span: Span, arg_span: Span, out: &mut TokenStream) {
-        let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site());
-        self.args_def.extend([
-            TT::Punct(Punct::new('$', Alone)),
-            TT::Ident(name.clone()),
-            TT::Punct(Punct::new(':', Alone)),
-            TT::Ident(Ident::new(kind, Span::call_site())),
-        ]);
-        name.set_span(arg_span);
-        out.extend([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]);
-    }
-
-    fn add_parenthesized_arg_def(&mut self, kind: Ident, dollar_span: Span, arg_span: Span, out: &mut TokenStream) {
-        let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site());
-        self.args_def.extend([TT::Group(Group::new(
-            Parenthesis,
-            TokenStream::from_iter([
-                TT::Punct(Punct::new('$', Alone)),
-                TT::Ident(name.clone()),
-                TT::Punct(Punct::new(':', Alone)),
-                TT::Ident(kind),
-            ]),
-        ))]);
-        name.set_span(arg_span);
-        out.extend([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]);
-    }
-
-    fn add_multi_arg_def(&mut self, dollar_span: Span, arg_span: Span, out: &mut TokenStream) {
-        let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site());
-        self.args_def.extend([TT::Group(Group::new(
-            Parenthesis,
-            TokenStream::from_iter([
-                TT::Punct(Punct::new('$', Alone)),
-                TT::Group(Group::new(
-                    Parenthesis,
-                    TokenStream::from_iter([
-                        TT::Punct(Punct::new('$', Alone)),
-                        TT::Ident(name.clone()),
-                        TT::Punct(Punct::new(':', Alone)),
-                        TT::Ident(Ident::new("tt", Span::call_site())),
-                    ]),
-                )),
-                TT::Punct(Punct::new('*', Alone)),
-            ]),
-        ))]);
-        name.set_span(arg_span);
-        out.extend([
-            TT::Punct(punct_with_span('$', Alone, dollar_span)),
-            TT::Group(group_with_span(
-                Parenthesis,
-                TokenStream::from_iter([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]),
-                dollar_span,
-            )),
-            TT::Punct(punct_with_span('*', Alone, dollar_span)),
-        ]);
-    }
-
-    fn add_arg(&mut self, dollar_span: Span, tt: TT, input: &mut IntoIter, out: &mut TokenStream) -> Result<()> {
-        match tt {
-            TT::Punct(p) if p.as_char() == ESCAPE_CHAR => out.extend([TT::Punct(p)]),
-            TT::Punct(p) if p.as_char() == '\'' && p.spacing() == Joint => {
-                let lt_name = expect_tt(
-                    input.next(),
-                    |tt| match tt {
-                        TT::Ident(x) => Some(x),
-                        _ => None,
-                    },
-                    "lifetime name",
-                    p.span(),
-                )?;
-                let arg_span = p.span().join(lt_name.span()).unwrap_or(p.span());
-                self.add_single_arg_def("lifetime", dollar_span, arg_span, out);
-                self.args.extend([TT::Punct(p), TT::Ident(lt_name)]);
-            },
-            TT::Ident(x) => {
-                self.add_single_arg_def("ident", dollar_span, x.span(), out);
-                self.args.push(TT::Ident(x));
-            },
-            TT::Literal(x) => {
-                self.add_single_arg_def("literal", dollar_span, x.span(), out);
-                self.args.push(TT::Literal(x));
-            },
-            TT::Group(g) if g.delimiter() == Parenthesis => {
-                let mut inner = g.stream().into_iter();
-                if let Some(TT::Punct(p)) = inner.next()
-                    && p.as_char() == '@'
-                {
-                    let kind = expect_tt(
-                        inner.next(),
-                        |tt| match tt {
-                            TT::Ident(kind) => Some(kind),
-                            _ => None,
-                        },
-                        "a macro fragment specifier",
-                        p.span(),
-                    )?;
-                    self.add_parenthesized_arg_def(kind, dollar_span, g.span(), out);
-                    self.args.push(TT::Group(group_with_span(Parenthesis, inner.collect(), g.span())))
-                } else {
-                    self.add_multi_arg_def(dollar_span, g.span(), out);
-                    self.args.push(TT::Group(g));
-                }
-            },
-            tt => return Err(make_error("unsupported escape", tt.span())),
-        };
-        Ok(())
-    }
-
-    fn add_sub_args(&mut self, args: Vec<TT>, out: &mut TokenStream) {
-        self.add_multi_arg_def(Span::call_site(), Span::call_site(), out);
-        self.args
-            .extend([TT::Group(Group::new(Parenthesis, TokenStream::from_iter(args)))]);
-    }
-}
-
-#[derive(Default)]
-struct Expander {
-    arm: Option<MacroArm>,
-    expn: TokenStream,
-}
-impl Expander {
-    fn for_arm(idx: usize) -> Self {
-        Self {
-            arm: Some(MacroArm {
-                args_def: TokenStream::from_iter([TT::Literal(Literal::usize_unsuffixed(idx))]),
-                args: Vec::new(),
-            }),
-            expn: TokenStream::new(),
-        }
-    }
-
-    fn write_tt(&mut self, tt: TT, mac: &mut MacWriter) -> Result<()> {
-        match tt {
-            TT::Group(g) => {
-                let outer = mem::take(&mut self.expn);
-                self.expand(g.stream().into_iter(), mac)?;
-                let inner = mem::replace(&mut self.expn, outer);
-                self.expn
-                    .extend([TT::Group(group_with_span(g.delimiter(), inner, g.span()))]);
-            },
-            tt => self.expn.extend([tt]),
-        }
-        Ok(())
-    }
-
-    fn expand(&mut self, input: IntoIter, mac: &mut MacWriter) -> Result<()> {
-        let Some(mut input) = LookaheadIter::new(input) else {
-            return Ok(());
-        };
-        while let Some(tt) = input.next() {
-            if let TT::Punct(p) = &tt
-                && p.as_char() == ESCAPE_CHAR
-                && let Some(arm) = self.arm.as_mut()
-            {
-                arm.add_arg(p.span(), mem::replace(&mut input.tt, tt), &mut input.iter, &mut self.expn)?;
-                if input.next().is_none() {
-                    return Ok(());
-                }
-            } else if let TT::Punct(p) = &input.tt
-                && p.as_char() == '!'
-                && let TT::Ident(name) = &tt
-                && name.to_string() == "inline"
-            {
-                let g = expect_tt(
-                    input.iter.next(),
-                    |tt| match tt {
-                        TT::Group(g) => Some(g),
-                        _ => None,
-                    },
-                    "macro arguments",
-                    p.span(),
-                )?;
-                mac.insert(name.span(), p.span(), g, self)?;
-                if input.next().is_none() {
-                    return Ok(());
-                }
-            } else {
-                self.write_tt(tt, mac)?;
-            }
-        }
-        self.write_tt(input.tt, mac)
-    }
-}
diff --git a/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs b/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs
index 25f0d0d6230..d737a832dd1 100644
--- a/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs
+++ b/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs
@@ -1,4 +1,4 @@
-//@aux-build:proc_macros.rs
+//@aux-build:../../ui/auxiliary/proc_macros.rs
 #![rustfmt::skip]
 #![feature(custom_inner_attributes)]
 #![allow(unused)]
@@ -156,7 +156,7 @@ fn main() {
     for i in {{{{xx}}}} {{{{{{{{}}}}}}}}
 
     while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}}
-    
+
     while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}}
 
     let d = D { d: {{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}} };
diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
index cdabe6460cd..b97bb144468 100644
--- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
+++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
@@ -28,6 +28,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
            disallowed-types
            doc-valid-idents
            enable-raw-pointer-heuristic-for-send
+           enforce-iter-loop-reborrow
            enforced-import-renames
            enum-variant-name-threshold
            enum-variant-size-threshold
@@ -99,6 +100,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
            disallowed-types
            doc-valid-idents
            enable-raw-pointer-heuristic-for-send
+           enforce-iter-loop-reborrow
            enforced-import-renames
            enum-variant-name-threshold
            enum-variant-size-threshold
diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/auxiliary/proc_macro_unsafe.rs b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/auxiliary/proc_macro_unsafe.rs
deleted file mode 100644
index 1c591fc76f3..00000000000
--- a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/auxiliary/proc_macro_unsafe.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-extern crate proc_macro;
-
-use proc_macro::{Delimiter, Group, Ident, TokenStream, TokenTree};
-
-#[proc_macro]
-pub fn unsafe_block(input: TokenStream) -> TokenStream {
-    let span = input.into_iter().next().unwrap().span();
-    TokenStream::from_iter([TokenTree::Ident(Ident::new("unsafe", span)), {
-        let mut group = Group::new(Delimiter::Brace, TokenStream::new());
-        group.set_span(span);
-        TokenTree::Group(group)
-    }])
-}
diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs
index c0976f0d600..b28e1b7d180 100644
--- a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs
+++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs
@@ -1,4 +1,4 @@
-//@aux-build:proc_macro_unsafe.rs
+//@aux-build:../../ui/auxiliary/proc_macro_unsafe.rs
 
 #![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)]
 #![allow(deref_nullptr, clippy::let_unit_value, clippy::missing_safety_doc)]
@@ -564,4 +564,18 @@ fn issue_8679<T: Copy>() {
     unsafe {}
 }
 
+mod issue_11246 {
+    // Safety: foo
+    const _: () = unsafe {};
+
+    // Safety: A safety comment
+    const FOO: () = unsafe {};
+
+    // Safety: bar
+    static BAR: u8 = unsafe { 0 };
+}
+
+// Safety: Another safety comment
+const FOO: () = unsafe {};
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/allow_attributes.fixed b/src/tools/clippy/tests/ui/allow_attributes.fixed
index 945ba83611c..b506a9890f5 100644
--- a/src/tools/clippy/tests/ui/allow_attributes.fixed
+++ b/src/tools/clippy/tests/ui/allow_attributes.fixed
@@ -22,6 +22,13 @@ struct T4;
 #[cfg_attr(panic = "unwind", expect(dead_code))]
 struct CfgT;
 
+#[allow(clippy::allow_attributes, unused)]
+struct Allowed;
+
+#[expect(clippy::allow_attributes)]
+#[allow(unused)]
+struct Expected;
+
 fn ignore_external() {
     external! {
         #[allow(clippy::needless_borrow)] // Should not lint
diff --git a/src/tools/clippy/tests/ui/allow_attributes.rs b/src/tools/clippy/tests/ui/allow_attributes.rs
index 8afa61c7002..c7daa7abd9d 100644
--- a/src/tools/clippy/tests/ui/allow_attributes.rs
+++ b/src/tools/clippy/tests/ui/allow_attributes.rs
@@ -22,6 +22,13 @@ struct T4;
 #[cfg_attr(panic = "unwind", allow(dead_code))]
 struct CfgT;
 
+#[allow(clippy::allow_attributes, unused)]
+struct Allowed;
+
+#[expect(clippy::allow_attributes)]
+#[allow(unused)]
+struct Expected;
+
 fn ignore_external() {
     external! {
         #[allow(clippy::needless_borrow)] // Should not lint
diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
index def30f5903d..b454c29aef4 100644
--- a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
+++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
@@ -10,7 +10,7 @@
     arithmetic_overflow,
     unconditional_panic
 )]
-#![feature(const_mut_refs, inline_const, saturating_int_impl)]
+#![feature(const_mut_refs, inline_const)]
 #![warn(clippy::arithmetic_side_effects)]
 
 extern crate proc_macro_derive;
diff --git a/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr b/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr
index d04ea7151c2..afb634f34b4 100644
--- a/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr
+++ b/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr
@@ -1,17 +1,12 @@
 error: `clippy::restriction` is not meant to be enabled as a group
-   |
-   = note: because of the command line `--warn clippy::restriction`
-   = help: enable the restriction lints you need individually
-   = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::blanket_clippy_restriction_lints)]`
-
-error: `clippy::restriction` is not meant to be enabled as a group
   --> $DIR/blanket_clippy_restriction_lints.rs:6:9
    |
 LL | #![warn(clippy::restriction)]
    |         ^^^^^^^^^^^^^^^^^^^
    |
    = help: enable the restriction lints you need individually
+   = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::blanket_clippy_restriction_lints)]`
 
 error: `clippy::restriction` is not meant to be enabled as a group
   --> $DIR/blanket_clippy_restriction_lints.rs:8:9
@@ -29,5 +24,10 @@ LL | #![forbid(clippy::restriction)]
    |
    = help: enable the restriction lints you need individually
 
+error: `clippy::restriction` is not meant to be enabled as a group
+   |
+   = note: because of the command line `--warn clippy::restriction`
+   = help: enable the restriction lints you need individually
+
 error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/bool_comparison.fixed b/src/tools/clippy/tests/ui/bool_comparison.fixed
index db85247f47d..e3f2ca72d1c 100644
--- a/src/tools/clippy/tests/ui/bool_comparison.fixed
+++ b/src/tools/clippy/tests/ui/bool_comparison.fixed
@@ -1,6 +1,6 @@
 #![allow(clippy::needless_if)]
 #![warn(clippy::bool_comparison)]
-#![allow(clippy::incorrect_partial_ord_impl_on_ord_type)]
+#![allow(clippy::non_canonical_partial_ord_impl)]
 
 fn main() {
     let x = true;
diff --git a/src/tools/clippy/tests/ui/bool_comparison.rs b/src/tools/clippy/tests/ui/bool_comparison.rs
index 0915f88544b..d1bc20d6831 100644
--- a/src/tools/clippy/tests/ui/bool_comparison.rs
+++ b/src/tools/clippy/tests/ui/bool_comparison.rs
@@ -1,6 +1,6 @@
 #![allow(clippy::needless_if)]
 #![warn(clippy::bool_comparison)]
-#![allow(clippy::incorrect_partial_ord_impl_on_ord_type)]
+#![allow(clippy::non_canonical_partial_ord_impl)]
 
 fn main() {
     let x = true;
diff --git a/src/tools/clippy/tests/ui/cast_size_32bit.stderr b/src/tools/clippy/tests/ui/cast_size.32bit.stderr
index fb51783a487..379ca60862b 100644
--- a/src/tools/clippy/tests/ui/cast_size_32bit.stderr
+++ b/src/tools/clippy/tests/ui/cast_size.32bit.stderr
@@ -1,44 +1,46 @@
 error: casting `isize` to `i8` may truncate the value
-  --> $DIR/cast_size_32bit.rs:12:5
+  --> $DIR/cast_size.rs:15:5
    |
 LL |     1isize as i8;
    |     ^^^^^^^^^^^^
    |
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
    = note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::cast_possible_truncation)]`
 help: ... or use `try_from` and handle the error accordingly
    |
 LL |     i8::try_from(1isize);
    |     ~~~~~~~~~~~~~~~~~~~~
 
 error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
-  --> $DIR/cast_size_32bit.rs:15:5
+  --> $DIR/cast_size.rs:18:5
    |
 LL |     x0 as f64;
    |     ^^^^^^^^^
    |
    = note: `-D clippy::cast-precision-loss` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::cast_precision_loss)]`
 
 error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
-  --> $DIR/cast_size_32bit.rs:16:5
+  --> $DIR/cast_size.rs:19:5
    |
 LL |     x1 as f64;
    |     ^^^^^^^^^
 
 error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
-  --> $DIR/cast_size_32bit.rs:17:5
+  --> $DIR/cast_size.rs:20:5
    |
 LL |     x0 as f32;
    |     ^^^^^^^^^
 
 error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
-  --> $DIR/cast_size_32bit.rs:18:5
+  --> $DIR/cast_size.rs:21:5
    |
 LL |     x1 as f32;
    |     ^^^^^^^^^
 
 error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers
-  --> $DIR/cast_size_32bit.rs:19:5
+  --> $DIR/cast_size.rs:22:5
    |
 LL |     1isize as i32;
    |     ^^^^^^^^^^^^^
@@ -50,7 +52,7 @@ LL |     i32::try_from(1isize);
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers
-  --> $DIR/cast_size_32bit.rs:20:5
+  --> $DIR/cast_size.rs:23:5
    |
 LL |     1isize as u32;
    |     ^^^^^^^^^^^^^
@@ -62,7 +64,7 @@ LL |     u32::try_from(1isize);
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers
-  --> $DIR/cast_size_32bit.rs:21:5
+  --> $DIR/cast_size.rs:24:5
    |
 LL |     1usize as u32;
    |     ^^^^^^^^^^^^^
@@ -74,7 +76,7 @@ LL |     u32::try_from(1usize);
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers
-  --> $DIR/cast_size_32bit.rs:22:5
+  --> $DIR/cast_size.rs:25:5
    |
 LL |     1usize as i32;
    |     ^^^^^^^^^^^^^
@@ -86,15 +88,16 @@ LL |     i32::try_from(1usize);
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers
-  --> $DIR/cast_size_32bit.rs:22:5
+  --> $DIR/cast_size.rs:25:5
    |
 LL |     1usize as i32;
    |     ^^^^^^^^^^^^^
    |
    = note: `-D clippy::cast-possible-wrap` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]`
 
 error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers
-  --> $DIR/cast_size_32bit.rs:24:5
+  --> $DIR/cast_size.rs:26:5
    |
 LL |     1i64 as isize;
    |     ^^^^^^^^^^^^^
@@ -106,7 +109,7 @@ LL |     isize::try_from(1i64);
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers
-  --> $DIR/cast_size_32bit.rs:25:5
+  --> $DIR/cast_size.rs:27:5
    |
 LL |     1i64 as usize;
    |     ^^^^^^^^^^^^^
@@ -118,7 +121,7 @@ LL |     usize::try_from(1i64);
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers
-  --> $DIR/cast_size_32bit.rs:26:5
+  --> $DIR/cast_size.rs:28:5
    |
 LL |     1u64 as isize;
    |     ^^^^^^^^^^^^^
@@ -130,13 +133,13 @@ LL |     isize::try_from(1u64);
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers
-  --> $DIR/cast_size_32bit.rs:26:5
+  --> $DIR/cast_size.rs:28:5
    |
 LL |     1u64 as isize;
    |     ^^^^^^^^^^^^^
 
 error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers
-  --> $DIR/cast_size_32bit.rs:27:5
+  --> $DIR/cast_size.rs:29:5
    |
 LL |     1u64 as usize;
    |     ^^^^^^^^^^^^^
@@ -148,24 +151,31 @@ LL |     usize::try_from(1u64);
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers
-  --> $DIR/cast_size_32bit.rs:28:5
+  --> $DIR/cast_size.rs:30:5
    |
 LL |     1u32 as isize;
    |     ^^^^^^^^^^^^^
 
 error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
-  --> $DIR/cast_size_32bit.rs:33:5
+  --> $DIR/cast_size.rs:35:5
    |
 LL |     999_999_999 as f32;
    |     ^^^^^^^^^^^^^^^^^^
 
-error: casting integer literal to `f64` is unnecessary
-  --> $DIR/cast_size_32bit.rs:34:5
+error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
+  --> $DIR/cast_size.rs:36:5
+   |
+LL |     9_999_999_999_999_999usize as f64;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: literal out of range for `usize`
+  --> $DIR/cast_size.rs:36:5
    |
-LL |     3_999_999_999usize as f64;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `3_999_999_999_f64`
+LL |     9_999_999_999_999_999usize as f64;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
+   = note: the literal `9_999_999_999_999_999usize` does not fit into the type `usize` whose range is `0..=4294967295`
+   = note: `#[deny(overflowing_literals)]` on by default
 
-error: aborting due to 18 previous errors
+error: aborting due to 19 previous errors
 
diff --git a/src/tools/clippy/tests/ui/cast_size.stderr b/src/tools/clippy/tests/ui/cast_size.64bit.stderr
index bc9224be644..7fae92b1250 100644
--- a/src/tools/clippy/tests/ui/cast_size.stderr
+++ b/src/tools/clippy/tests/ui/cast_size.64bit.stderr
@@ -1,5 +1,5 @@
 error: casting `isize` to `i8` may truncate the value
-  --> $DIR/cast_size.rs:12:5
+  --> $DIR/cast_size.rs:15:5
    |
 LL |     1isize as i8;
    |     ^^^^^^^^^^^^
@@ -13,7 +13,7 @@ LL |     i8::try_from(1isize);
    |     ~~~~~~~~~~~~~~~~~~~~
 
 error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
-  --> $DIR/cast_size.rs:16:5
+  --> $DIR/cast_size.rs:18:5
    |
 LL |     x0 as f64;
    |     ^^^^^^^^^
@@ -28,19 +28,19 @@ LL |     x1 as f64;
    |     ^^^^^^^^^
 
 error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
-  --> $DIR/cast_size.rs:21:5
+  --> $DIR/cast_size.rs:20:5
    |
 LL |     x0 as f32;
    |     ^^^^^^^^^
 
 error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
-  --> $DIR/cast_size.rs:23:5
+  --> $DIR/cast_size.rs:21:5
    |
 LL |     x1 as f32;
    |     ^^^^^^^^^
 
 error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers
-  --> $DIR/cast_size.rs:25:5
+  --> $DIR/cast_size.rs:22:5
    |
 LL |     1isize as i32;
    |     ^^^^^^^^^^^^^
@@ -52,7 +52,7 @@ LL |     i32::try_from(1isize);
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers
-  --> $DIR/cast_size.rs:27:5
+  --> $DIR/cast_size.rs:23:5
    |
 LL |     1isize as u32;
    |     ^^^^^^^^^^^^^
@@ -64,7 +64,7 @@ LL |     u32::try_from(1isize);
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers
-  --> $DIR/cast_size.rs:29:5
+  --> $DIR/cast_size.rs:24:5
    |
 LL |     1usize as u32;
    |     ^^^^^^^^^^^^^
@@ -76,7 +76,7 @@ LL |     u32::try_from(1usize);
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers
-  --> $DIR/cast_size.rs:31:5
+  --> $DIR/cast_size.rs:25:5
    |
 LL |     1usize as i32;
    |     ^^^^^^^^^^^^^
@@ -88,7 +88,7 @@ LL |     i32::try_from(1usize);
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers
-  --> $DIR/cast_size.rs:31:5
+  --> $DIR/cast_size.rs:25:5
    |
 LL |     1usize as i32;
    |     ^^^^^^^^^^^^^
@@ -97,7 +97,7 @@ LL |     1usize as i32;
    = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]`
 
 error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers
-  --> $DIR/cast_size.rs:36:5
+  --> $DIR/cast_size.rs:26:5
    |
 LL |     1i64 as isize;
    |     ^^^^^^^^^^^^^
@@ -109,7 +109,7 @@ LL |     isize::try_from(1i64);
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers
-  --> $DIR/cast_size.rs:38:5
+  --> $DIR/cast_size.rs:27:5
    |
 LL |     1i64 as usize;
    |     ^^^^^^^^^^^^^
@@ -121,7 +121,7 @@ LL |     usize::try_from(1i64);
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers
-  --> $DIR/cast_size.rs:40:5
+  --> $DIR/cast_size.rs:28:5
    |
 LL |     1u64 as isize;
    |     ^^^^^^^^^^^^^
@@ -133,13 +133,13 @@ LL |     isize::try_from(1u64);
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers
-  --> $DIR/cast_size.rs:40:5
+  --> $DIR/cast_size.rs:28:5
    |
 LL |     1u64 as isize;
    |     ^^^^^^^^^^^^^
 
 error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers
-  --> $DIR/cast_size.rs:43:5
+  --> $DIR/cast_size.rs:29:5
    |
 LL |     1u64 as usize;
    |     ^^^^^^^^^^^^^
@@ -151,19 +151,19 @@ LL |     usize::try_from(1u64);
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers
-  --> $DIR/cast_size.rs:45:5
+  --> $DIR/cast_size.rs:30:5
    |
 LL |     1u32 as isize;
    |     ^^^^^^^^^^^^^
 
 error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
-  --> $DIR/cast_size.rs:51:5
+  --> $DIR/cast_size.rs:35:5
    |
 LL |     999_999_999 as f32;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
-  --> $DIR/cast_size.rs:53:5
+  --> $DIR/cast_size.rs:36:5
    |
 LL |     9_999_999_999_999_999usize as f64;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/cast_size.rs b/src/tools/clippy/tests/ui/cast_size.rs
index 95626b20b27..d063a70ccdf 100644
--- a/src/tools/clippy/tests/ui/cast_size.rs
+++ b/src/tools/clippy/tests/ui/cast_size.rs
@@ -1,56 +1,37 @@
-//@ignore-32bit
-#[warn(
+//@stderr-per-bitwidth
+//@no-rustfix
+
+#![warn(
     clippy::cast_precision_loss,
     clippy::cast_possible_truncation,
     clippy::cast_sign_loss,
     clippy::cast_possible_wrap,
     clippy::cast_lossless
 )]
-#[allow(clippy::no_effect, clippy::unnecessary_operation)]
+#![allow(clippy::no_effect, clippy::unnecessary_operation)]
+
 fn main() {
     // Casting from *size
     1isize as i8;
-    //~^ ERROR: casting `isize` to `i8` may truncate the value
     let x0 = 1isize;
     let x1 = 1usize;
     x0 as f64;
-    //~^ ERROR: casting `isize` to `f64` causes a loss of precision on targets with 64-bit
-    //~| NOTE: `-D clippy::cast-precision-loss` implied by `-D warnings`
     x1 as f64;
-    //~^ ERROR: casting `usize` to `f64` causes a loss of precision on targets with 64-bit
     x0 as f32;
-    //~^ ERROR: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 b
     x1 as f32;
-    //~^ ERROR: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 b
     1isize as i32;
-    //~^ ERROR: casting `isize` to `i32` may truncate the value on targets with 64-bit wid
     1isize as u32;
-    //~^ ERROR: casting `isize` to `u32` may truncate the value on targets with 64-bit wid
     1usize as u32;
-    //~^ ERROR: casting `usize` to `u32` may truncate the value on targets with 64-bit wid
     1usize as i32;
-    //~^ ERROR: casting `usize` to `i32` may truncate the value on targets with 64-bit wid
-    //~| ERROR: casting `usize` to `i32` may wrap around the value on targets with 32-bit
-    //~| NOTE: `-D clippy::cast-possible-wrap` implied by `-D warnings`
-    // Casting to *size
     1i64 as isize;
-    //~^ ERROR: casting `i64` to `isize` may truncate the value on targets with 32-bit wid
     1i64 as usize;
-    //~^ ERROR: casting `i64` to `usize` may truncate the value on targets with 32-bit wid
     1u64 as isize;
-    //~^ ERROR: casting `u64` to `isize` may truncate the value on targets with 32-bit wid
-    //~| ERROR: casting `u64` to `isize` may wrap around the value on targets with 64-bit
     1u64 as usize;
-    //~^ ERROR: casting `u64` to `usize` may truncate the value on targets with 32-bit wid
     1u32 as isize;
-    //~^ ERROR: casting `u32` to `isize` may wrap around the value on targets with 32-bit
     1u32 as usize; // Should not trigger any lint
     1i32 as isize; // Neither should this
     1i32 as usize;
     // Big integer literal to float
     999_999_999 as f32;
-    //~^ ERROR: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide,
     9_999_999_999_999_999usize as f64;
-    //~^ ERROR: casting `usize` to `f64` causes a loss of precision on targets with 64-bit
 }
-//@no-rustfix
diff --git a/src/tools/clippy/tests/ui/cast_size_32bit.rs b/src/tools/clippy/tests/ui/cast_size_32bit.rs
deleted file mode 100644
index 5a06e34bdb8..00000000000
--- a/src/tools/clippy/tests/ui/cast_size_32bit.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-//@ignore-64bit
-#[warn(
-    clippy::cast_precision_loss,
-    clippy::cast_possible_truncation,
-    clippy::cast_sign_loss,
-    clippy::cast_possible_wrap,
-    clippy::cast_lossless
-)]
-#[allow(clippy::no_effect, clippy::unnecessary_operation)]
-fn main() {
-    // Casting from *size
-    1isize as i8;
-    //~^ ERROR: casting `isize` to `i8` may truncate the value
-    let x0 = 1isize;
-    let x1 = 1usize;
-    x0 as f64;
-    //~^ ERROR: casting `isize` to `f64` causes a loss of precision on targets with 64-bit
-    //~| NOTE: `-D clippy::cast-precision-loss` implied by `-D warnings`
-    x1 as f64;
-    //~^ ERROR: casting `usize` to `f64` causes a loss of precision on targets with 64-bit
-    x0 as f32;
-    //~^ ERROR: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 b
-    x1 as f32;
-    //~^ ERROR: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 b
-    1isize as i32;
-    //~^ ERROR: casting `isize` to `i32` may truncate the value on targets with 64-bit wid
-    1isize as u32;
-    //~^ ERROR: casting `isize` to `u32` may truncate the value on targets with 64-bit wid
-    1usize as u32;
-    //~^ ERROR: casting `usize` to `u32` may truncate the value on targets with 64-bit wid
-    1usize as i32;
-    //~^ ERROR: casting `usize` to `i32` may truncate the value on targets with 64-bit wid
-    //~| ERROR: casting `usize` to `i32` may wrap around the value on targets with 32-bit
-    //~| NOTE: `-D clippy::cast-possible-wrap` implied by `-D warnings`
-    // Casting to *size
-    1i64 as isize;
-    //~^ ERROR: casting `i64` to `isize` may truncate the value on targets with 32-bit wid
-    1i64 as usize;
-    //~^ ERROR: casting `i64` to `usize` may truncate the value on targets with 32-bit wid
-    1u64 as isize;
-    //~^ ERROR: casting `u64` to `isize` may truncate the value on targets with 32-bit wid
-    //~| ERROR: casting `u64` to `isize` may wrap around the value on targets with 64-bit
-    1u64 as usize;
-    //~^ ERROR: casting `u64` to `usize` may truncate the value on targets with 32-bit wid
-    1u32 as isize;
-    //~^ ERROR: casting `u32` to `isize` may wrap around the value on targets with 32-bit
-    1u32 as usize; // Should not trigger any lint
-    1i32 as isize; // Neither should this
-    1i32 as usize;
-    // Big integer literal to float
-    999_999_999 as f32;
-    //~^ ERROR: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide,
-    3_999_999_999usize as f64;
-    //~^ ERROR: casting integer literal to `f64` is unnecessary
-    //~| NOTE: `-D clippy::unnecessary-cast` implied by `-D warnings`
-}
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs
index e82e7bcb06c..02f80cc52ac 100644
--- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs
+++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs
@@ -128,6 +128,57 @@ fn main() {
     assert!(x.is_ok(), "{:?}", x.unwrap_err());
 }
 
+fn issue11371() {
+    let option = Some(());
+
+    if option.is_some() {
+        option.as_ref().unwrap();
+        //~^ ERROR: called `unwrap` on `option` after checking its variant with `is_some`
+    } else {
+        option.as_ref().unwrap();
+        //~^ ERROR: this call to `unwrap()` will always panic
+    }
+
+    let result = Ok::<(), ()>(());
+
+    if result.is_ok() {
+        result.as_ref().unwrap();
+        //~^ ERROR: called `unwrap` on `result` after checking its variant with `is_ok`
+    } else {
+        result.as_ref().unwrap();
+        //~^ ERROR: this call to `unwrap()` will always panic
+    }
+
+    let mut option = Some(());
+    if option.is_some() {
+        option.as_mut().unwrap();
+        //~^ ERROR: called `unwrap` on `option` after checking its variant with `is_some`
+    } else {
+        option.as_mut().unwrap();
+        //~^ ERROR: this call to `unwrap()` will always panic
+    }
+
+    let mut result = Ok::<(), ()>(());
+    if result.is_ok() {
+        result.as_mut().unwrap();
+        //~^ ERROR: called `unwrap` on `result` after checking its variant with `is_ok`
+    } else {
+        result.as_mut().unwrap();
+        //~^ ERROR: this call to `unwrap()` will always panic
+    }
+
+    // This should not lint. Statics are, at the time of writing, not linted on anyway,
+    // but if at some point they are supported by this lint, it should correctly see that
+    // `X` is being mutated and not suggest `if let Some(..) = X {}`
+    static mut X: Option<i32> = Some(123);
+    unsafe {
+        if X.is_some() {
+            X = None;
+            X.unwrap();
+        }
+    }
+}
+
 fn check_expect() {
     let x = Some(());
     if x.is_some() {
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
index ed603581ecd..a5afbba7317 100644
--- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
+++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
@@ -168,5 +168,73 @@ LL |     if x.is_err() {
 LL |         x.unwrap_err();
    |         ^^^^^^^^^^^^^^
 
-error: aborting due to 17 previous errors
+error: called `unwrap` on `option` after checking its variant with `is_some`
+  --> $DIR/simple_conditionals.rs:135:9
+   |
+LL |     if option.is_some() {
+   |     ------------------- help: try: `if let Some(..) = &option`
+LL |         option.as_ref().unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this call to `unwrap()` will always panic
+  --> $DIR/simple_conditionals.rs:138:9
+   |
+LL |     if option.is_some() {
+   |        ---------------- because of this check
+...
+LL |         option.as_ref().unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: called `unwrap` on `result` after checking its variant with `is_ok`
+  --> $DIR/simple_conditionals.rs:145:9
+   |
+LL |     if result.is_ok() {
+   |     ----------------- help: try: `if let Ok(..) = &result`
+LL |         result.as_ref().unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this call to `unwrap()` will always panic
+  --> $DIR/simple_conditionals.rs:148:9
+   |
+LL |     if result.is_ok() {
+   |        -------------- because of this check
+...
+LL |         result.as_ref().unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: called `unwrap` on `option` after checking its variant with `is_some`
+  --> $DIR/simple_conditionals.rs:154:9
+   |
+LL |     if option.is_some() {
+   |     ------------------- help: try: `if let Some(..) = &mut option`
+LL |         option.as_mut().unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this call to `unwrap()` will always panic
+  --> $DIR/simple_conditionals.rs:157:9
+   |
+LL |     if option.is_some() {
+   |        ---------------- because of this check
+...
+LL |         option.as_mut().unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: called `unwrap` on `result` after checking its variant with `is_ok`
+  --> $DIR/simple_conditionals.rs:163:9
+   |
+LL |     if result.is_ok() {
+   |     ----------------- help: try: `if let Ok(..) = &mut result`
+LL |         result.as_mut().unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this call to `unwrap()` will always panic
+  --> $DIR/simple_conditionals.rs:166:9
+   |
+LL |     if result.is_ok() {
+   |        -------------- because of this check
+...
+LL |         result.as_mut().unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 25 previous errors
 
diff --git a/src/tools/clippy/tests/ui/clone_on_copy_impl.rs b/src/tools/clippy/tests/ui/clone_on_copy_impl.rs
index b7c186bef77..2d03544ad8b 100644
--- a/src/tools/clippy/tests/ui/clone_on_copy_impl.rs
+++ b/src/tools/clippy/tests/ui/clone_on_copy_impl.rs
@@ -1,4 +1,4 @@
-#![allow(clippy::incorrect_clone_impl_on_copy_type)]
+#![allow(clippy::non_canonical_clone_impl)]
 
 use std::fmt;
 use std::marker::PhantomData;
diff --git a/src/tools/clippy/tests/ui/crashes/ice-11337.rs b/src/tools/clippy/tests/ui/crashes/ice-11337.rs
new file mode 100644
index 00000000000..0bed4035f6b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-11337.rs
@@ -0,0 +1,9 @@
+#![feature(trait_alias)]
+
+trait Confusing<F> = Fn(i32) where F: Fn(u32);
+
+fn alias<T: Confusing<F>, F>(_: T, _: F) {}
+
+fn main() {
+    alias(|_| {}, |_| {});
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-11422.fixed b/src/tools/clippy/tests/ui/crashes/ice-11422.fixed
new file mode 100644
index 00000000000..ca5721cbb2b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-11422.fixed
@@ -0,0 +1,25 @@
+#![warn(clippy::implied_bounds_in_impls)]
+
+use std::fmt::Debug;
+use std::ops::*;
+
+fn gen() -> impl PartialOrd + Debug {}
+
+struct Bar {}
+trait Foo<T = Self> {}
+trait FooNested<T = Option<Self>> {}
+impl Foo for Bar {}
+impl FooNested for Bar {}
+
+fn foo() -> impl Foo + FooNested {
+    Bar {}
+}
+
+fn test_impl_ops() -> impl Add + Sub + Mul + Div {
+    1
+}
+fn test_impl_assign_ops() -> impl AddAssign + SubAssign + MulAssign + DivAssign {
+    1
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-11422.rs b/src/tools/clippy/tests/ui/crashes/ice-11422.rs
new file mode 100644
index 00000000000..355ec2480bb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-11422.rs
@@ -0,0 +1,25 @@
+#![warn(clippy::implied_bounds_in_impls)]
+
+use std::fmt::Debug;
+use std::ops::*;
+
+fn gen() -> impl PartialOrd + PartialEq + Debug {}
+
+struct Bar {}
+trait Foo<T = Self> {}
+trait FooNested<T = Option<Self>> {}
+impl Foo for Bar {}
+impl FooNested for Bar {}
+
+fn foo() -> impl Foo + FooNested {
+    Bar {}
+}
+
+fn test_impl_ops() -> impl Add + Sub + Mul + Div {
+    1
+}
+fn test_impl_assign_ops() -> impl AddAssign + SubAssign + MulAssign + DivAssign {
+    1
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-11422.stderr b/src/tools/clippy/tests/ui/crashes/ice-11422.stderr
new file mode 100644
index 00000000000..fb80b5b147f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-11422.stderr
@@ -0,0 +1,16 @@
+error: this bound is already specified as the supertrait of `PartialOrd`
+  --> $DIR/ice-11422.rs:6:31
+   |
+LL | fn gen() -> impl PartialOrd + PartialEq + Debug {}
+   |                               ^^^^^^^^^
+   |
+   = note: `-D clippy::implied-bounds-in-impls` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::implied_bounds_in_impls)]`
+help: try removing this bound
+   |
+LL - fn gen() -> impl PartialOrd + PartialEq + Debug {}
+LL + fn gen() -> impl PartialOrd + Debug {}
+   |
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-360.rs b/src/tools/clippy/tests/ui/crashes/ice-360.rs
index 28589e1efed..0d10932b098 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-360.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-360.rs
@@ -3,7 +3,8 @@ fn main() {}
 fn no_panic<T>(slice: &[T]) {
     let mut iter = slice.iter();
     loop {
-        //~^ ERROR: this loop could be written as a `while let` loop
+        //~^ ERROR: this loop never actually loops
+        //~| ERROR: this loop could be written as a `while let` loop
         //~| NOTE: `-D clippy::while-let-loop` implied by `-D warnings`
         let _ = match iter.next() {
             Some(ele) => ele,
diff --git a/src/tools/clippy/tests/ui/crashes/ice-360.stderr b/src/tools/clippy/tests/ui/crashes/ice-360.stderr
index dd016355b53..a84697a9f29 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-360.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-360.stderr
@@ -1,10 +1,24 @@
+error: this loop never actually loops
+  --> $DIR/ice-360.rs:5:5
+   |
+LL | /     loop {
+LL | |
+LL | |
+LL | |
+...  |
+LL | |
+LL | |     }
+   | |_____^
+   |
+   = note: `#[deny(clippy::never_loop)]` on by default
+
 error: this loop could be written as a `while let` loop
   --> $DIR/ice-360.rs:5:5
    |
 LL | /     loop {
 LL | |
 LL | |
-LL | |         let _ = match iter.next() {
+LL | |
 ...  |
 LL | |
 LL | |     }
@@ -14,7 +28,7 @@ LL | |     }
    = help: to override `-D warnings` add `#[allow(clippy::while_let_loop)]`
 
 error: empty `loop {}` wastes CPU cycles
-  --> $DIR/ice-360.rs:12:9
+  --> $DIR/ice-360.rs:13:9
    |
 LL |         loop {}
    |         ^^^^^^^
@@ -23,5 +37,5 @@ LL |         loop {}
    = note: `-D clippy::empty-loop` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::empty_loop)]`
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/derivable_impls.fixed b/src/tools/clippy/tests/ui/derivable_impls.fixed
index 6cc202414f5..68c5a5c5ca4 100644
--- a/src/tools/clippy/tests/ui/derivable_impls.fixed
+++ b/src/tools/clippy/tests/ui/derivable_impls.fixed
@@ -287,4 +287,17 @@ mod issue10158 {
     }
 }
 
+mod issue11368 {
+    pub struct A {
+        a: u32,
+    }
+
+    impl Default for A {
+        #[track_caller]
+        fn default() -> Self {
+            Self { a: 0 }
+        }
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/derivable_impls.rs b/src/tools/clippy/tests/ui/derivable_impls.rs
index 0aa9acd752d..21d73ba8b77 100644
--- a/src/tools/clippy/tests/ui/derivable_impls.rs
+++ b/src/tools/clippy/tests/ui/derivable_impls.rs
@@ -323,4 +323,17 @@ mod issue10158 {
     }
 }
 
+mod issue11368 {
+    pub struct A {
+        a: u32,
+    }
+
+    impl Default for A {
+        #[track_caller]
+        fn default() -> Self {
+            Self { a: 0 }
+        }
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/derive.rs b/src/tools/clippy/tests/ui/derive.rs
index 310c701765b..20ac8a6e6be 100644
--- a/src/tools/clippy/tests/ui/derive.rs
+++ b/src/tools/clippy/tests/ui/derive.rs
@@ -1,8 +1,4 @@
-#![allow(
-    clippy::incorrect_clone_impl_on_copy_type,
-    clippy::incorrect_partial_ord_impl_on_ord_type,
-    dead_code
-)]
+#![allow(clippy::non_canonical_clone_impl, clippy::non_canonical_partial_ord_impl, dead_code)]
 #![warn(clippy::expl_impl_clone_on_copy)]
 
 
diff --git a/src/tools/clippy/tests/ui/derive.stderr b/src/tools/clippy/tests/ui/derive.stderr
index e9b2ee34760..88942d95432 100644
--- a/src/tools/clippy/tests/ui/derive.stderr
+++ b/src/tools/clippy/tests/ui/derive.stderr
@@ -1,5 +1,5 @@
 error: you are implementing `Clone` explicitly on a `Copy` type
-  --> $DIR/derive.rs:12:1
+  --> $DIR/derive.rs:8:1
    |
 LL | / impl Clone for Qux {
 LL | |
@@ -10,7 +10,7 @@ LL | | }
    | |_^
    |
 note: consider deriving `Clone` or removing `Copy`
-  --> $DIR/derive.rs:12:1
+  --> $DIR/derive.rs:8:1
    |
 LL | / impl Clone for Qux {
 LL | |
@@ -23,7 +23,7 @@ LL | | }
    = help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]`
 
 error: you are implementing `Clone` explicitly on a `Copy` type
-  --> $DIR/derive.rs:37:1
+  --> $DIR/derive.rs:33:1
    |
 LL | / impl<'a> Clone for Lt<'a> {
 LL | |
@@ -34,7 +34,7 @@ LL | | }
    | |_^
    |
 note: consider deriving `Clone` or removing `Copy`
-  --> $DIR/derive.rs:37:1
+  --> $DIR/derive.rs:33:1
    |
 LL | / impl<'a> Clone for Lt<'a> {
 LL | |
@@ -45,7 +45,7 @@ LL | | }
    | |_^
 
 error: you are implementing `Clone` explicitly on a `Copy` type
-  --> $DIR/derive.rs:49:1
+  --> $DIR/derive.rs:45:1
    |
 LL | / impl Clone for BigArray {
 LL | |
@@ -56,7 +56,7 @@ LL | | }
    | |_^
    |
 note: consider deriving `Clone` or removing `Copy`
-  --> $DIR/derive.rs:49:1
+  --> $DIR/derive.rs:45:1
    |
 LL | / impl Clone for BigArray {
 LL | |
@@ -67,7 +67,7 @@ LL | | }
    | |_^
 
 error: you are implementing `Clone` explicitly on a `Copy` type
-  --> $DIR/derive.rs:61:1
+  --> $DIR/derive.rs:57:1
    |
 LL | / impl Clone for FnPtr {
 LL | |
@@ -78,7 +78,7 @@ LL | | }
    | |_^
    |
 note: consider deriving `Clone` or removing `Copy`
-  --> $DIR/derive.rs:61:1
+  --> $DIR/derive.rs:57:1
    |
 LL | / impl Clone for FnPtr {
 LL | |
@@ -89,7 +89,7 @@ LL | | }
    | |_^
 
 error: you are implementing `Clone` explicitly on a `Copy` type
-  --> $DIR/derive.rs:82:1
+  --> $DIR/derive.rs:78:1
    |
 LL | / impl<T: Clone> Clone for Generic2<T> {
 LL | |
@@ -100,7 +100,7 @@ LL | | }
    | |_^
    |
 note: consider deriving `Clone` or removing `Copy`
-  --> $DIR/derive.rs:82:1
+  --> $DIR/derive.rs:78:1
    |
 LL | / impl<T: Clone> Clone for Generic2<T> {
 LL | |
diff --git a/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs b/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs
index 2c19942d420..1c7e6d1c202 100644
--- a/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs
+++ b/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs
@@ -1,6 +1,6 @@
 #![warn(clippy::derive_ord_xor_partial_ord)]
 #![allow(clippy::unnecessary_wraps)]
-#![allow(clippy::incorrect_partial_ord_impl_on_ord_type)]
+#![allow(clippy::non_canonical_partial_ord_impl)]
 
 use std::cmp::Ordering;
 
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
index f7c2f14a482..47b56960a00 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
@@ -198,6 +198,16 @@ fn pulldown_cmark_crash() {}
 /// [plain text][path::to::item]
 fn intra_doc_link() {}
 
+/// Ignore escaped\_underscores
+///
+/// \\[
+///     \\prod\_{x\\in X} p\_x
+/// \\]
+fn issue_2581() {}
+
+/// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint`
+fn lint_after_escaped_chars() {}
+
 // issue #7033 - generic_const_exprs ICE
 struct S<T, const N: usize>
 where [(); N.checked_next_power_of_two().unwrap()]: {
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs
index 51961e75b84..4d9a4eafa5f 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs
@@ -198,6 +198,16 @@ fn pulldown_cmark_crash() {}
 /// [plain text][path::to::item]
 fn intra_doc_link() {}
 
+/// Ignore escaped\_underscores
+///
+/// \\[
+///     \\prod\_{x\\in X} p\_x
+/// \\]
+fn issue_2581() {}
+
+/// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint
+fn lint_after_escaped_chars() {}
+
 // issue #7033 - generic_const_exprs ICE
 struct S<T, const N: usize>
 where [(); N.checked_next_power_of_two().unwrap()]: {
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
index a30ded81385..4c9ff41d918 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
@@ -308,5 +308,16 @@ help: try
 LL | /// An iterator over `mycrate::Collection`'s values.
    |                      ~~~~~~~~~~~~~~~~~~~~~
 
-error: aborting due to 28 previous errors
+error: item in documentation is missing backticks
+  --> $DIR/doc-fixable.rs:208:34
+   |
+LL | /// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint
+   |                                  ^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint`
+   |                                  ~~~~~~~~~~~~~~~~~
+
+error: aborting due to 29 previous errors
 
diff --git a/src/tools/clippy/tests/ui/doc/unbalanced_ticks.rs b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.rs
index 4a1711f79a0..6f7bab72040 100644
--- a/src/tools/clippy/tests/ui/doc/unbalanced_ticks.rs
+++ b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.rs
@@ -48,4 +48,4 @@ fn other_markdown() {}
 ///   /// `lol`
 ///   pub struct Struct;
 ///   ```
-fn iss_7421() {}
+fn issue_7421() {}
diff --git a/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr
index 92b6f8536c8..89ad8db3916 100644
--- a/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr
+++ b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr
@@ -1,7 +1,8 @@
 error: backticks are unbalanced
-  --> $DIR/unbalanced_ticks.rs:7:1
+  --> $DIR/unbalanced_ticks.rs:7:5
    |
-LL | / /// This is a doc comment with `unbalanced_tick marks and several words that
+LL |   /// This is a doc comment with `unbalanced_tick marks and several words that
+   |  _____^
 LL | |
 LL | | /// should be `encompassed_by` tick marks because they `contain_underscores`.
 LL | | /// Because of the initial `unbalanced_tick` pair, the error message is
@@ -13,10 +14,10 @@ LL | | /// very `confusing_and_misleading`.
    = help: to override `-D warnings` add `#[allow(clippy::doc_markdown)]`
 
 error: backticks are unbalanced
-  --> $DIR/unbalanced_ticks.rs:14:1
+  --> $DIR/unbalanced_ticks.rs:14:5
    |
 LL | /// This paragraph has `unbalanced_tick marks and should stop_linting.
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: a backtick may be missing a pair
 
@@ -32,10 +33,10 @@ LL | /// This paragraph is fine and `should_be` linted normally.
    |                                ~~~~~~~~~~~
 
 error: backticks are unbalanced
-  --> $DIR/unbalanced_ticks.rs:20:1
+  --> $DIR/unbalanced_ticks.rs:20:5
    |
 LL | /// Double unbalanced backtick from ``here to here` should lint.
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: a backtick may be missing a pair
 
@@ -51,18 +52,18 @@ LL | /// ## `not_fine`
    |        ~~~~~~~~~~
 
 error: backticks are unbalanced
-  --> $DIR/unbalanced_ticks.rs:37:1
+  --> $DIR/unbalanced_ticks.rs:37:5
    |
 LL | /// ### `unbalanced
-   | ^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^
    |
    = help: a backtick may be missing a pair
 
 error: backticks are unbalanced
-  --> $DIR/unbalanced_ticks.rs:40:1
+  --> $DIR/unbalanced_ticks.rs:40:5
    |
 LL | /// - This `item has unbalanced tick marks
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: a backtick may be missing a pair
 
diff --git a/src/tools/clippy/tests/ui/doc_errors.rs b/src/tools/clippy/tests/ui/doc_errors.rs
index 86721f61d19..9b3783aaf09 100644
--- a/src/tools/clippy/tests/ui/doc_errors.rs
+++ b/src/tools/clippy/tests/ui/doc_errors.rs
@@ -85,6 +85,27 @@ impl Struct1 {
     async fn async_priv_method_missing_errors_header() -> Result<(), ()> {
         unimplemented!();
     }
+
+    /**
+    # Errors
+    A description of the errors goes here.
+    */
+    fn block_comment() -> Result<(), ()> {
+        unimplemented!();
+    }
+
+    /**
+     * # Errors
+     * A description of the errors goes here.
+     */
+    fn block_comment_leading_asterisks() -> Result<(), ()> {
+        unimplemented!();
+    }
+
+    #[doc(hidden)]
+    fn doc_hidden() -> Result<(), ()> {
+        unimplemented!();
+    }
 }
 
 pub trait Trait1 {
@@ -95,6 +116,11 @@ pub trait Trait1 {
     /// # Errors
     /// A description of the errors goes here.
     fn trait_method_with_errors_header() -> Result<(), ()>;
+
+    #[doc(hidden)]
+    fn doc_hidden() -> Result<(), ()> {
+        unimplemented!();
+    }
 }
 
 impl Trait1 for Struct1 {
@@ -107,6 +133,11 @@ impl Trait1 for Struct1 {
     }
 }
 
+#[doc(hidden)]
+pub trait DocHidden {
+    fn f() -> Result<(), ()>;
+}
+
 fn main() -> Result<(), ()> {
     Ok(())
 }
diff --git a/src/tools/clippy/tests/ui/doc_errors.stderr b/src/tools/clippy/tests/ui/doc_errors.stderr
index 9cea864f1d8..dc59675b9e5 100644
--- a/src/tools/clippy/tests/ui/doc_errors.stderr
+++ b/src/tools/clippy/tests/ui/doc_errors.stderr
@@ -38,7 +38,7 @@ LL |     pub async fn async_pub_method_missing_errors_header() -> Result<(), ()>
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: docs for function returning `Result` missing `# Errors` section
-  --> $DIR/doc_errors.rs:92:5
+  --> $DIR/doc_errors.rs:113:5
    |
 LL |     fn trait_method_missing_errors_header() -> Result<(), ()>;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/empty_loop.rs b/src/tools/clippy/tests/ui/empty_loop.rs
index 54e8fb4907c..be347563135 100644
--- a/src/tools/clippy/tests/ui/empty_loop.rs
+++ b/src/tools/clippy/tests/ui/empty_loop.rs
@@ -7,10 +7,12 @@ use proc_macros::{external, inline_macros};
 
 fn should_trigger() {
     loop {}
+    #[allow(clippy::never_loop)]
     loop {
         loop {}
     }
 
+    #[allow(clippy::never_loop)]
     'outer: loop {
         'inner: loop {}
     }
@@ -18,6 +20,7 @@ fn should_trigger() {
 
 #[inline_macros]
 fn should_not_trigger() {
+    #[allow(clippy::never_loop)]
     loop {
         panic!("This is fine")
     }
diff --git a/src/tools/clippy/tests/ui/empty_loop.stderr b/src/tools/clippy/tests/ui/empty_loop.stderr
index 84d7d61c7da..113556f673c 100644
--- a/src/tools/clippy/tests/ui/empty_loop.stderr
+++ b/src/tools/clippy/tests/ui/empty_loop.stderr
@@ -9,7 +9,7 @@ LL |     loop {}
    = help: to override `-D warnings` add `#[allow(clippy::empty_loop)]`
 
 error: empty `loop {}` wastes CPU cycles
-  --> $DIR/empty_loop.rs:11:9
+  --> $DIR/empty_loop.rs:12:9
    |
 LL |         loop {}
    |         ^^^^^^^
@@ -17,7 +17,7 @@ LL |         loop {}
    = help: you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body
 
 error: empty `loop {}` wastes CPU cycles
-  --> $DIR/empty_loop.rs:15:9
+  --> $DIR/empty_loop.rs:17:9
    |
 LL |         'inner: loop {}
    |         ^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs
index 964e5634ddb..c50404c5047 100644
--- a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs
+++ b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs
@@ -1,4 +1,4 @@
-//@ignore-target-x86
+//@ignore-32bit
 
 #![warn(clippy::enum_clike_unportable_variant)]
 #![allow(unused, non_upper_case_globals)]
diff --git a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr
index 5935eea5e03..93ad4daa97f 100644
--- a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr
+++ b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr
@@ -5,51 +5,52 @@ LL |     X = 0x1_0000_0000,
    |     ^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::enum-clike-unportable-variant` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::enum_clike_unportable_variant)]`
 
 error: C-like enum variant discriminant is not portable to 32-bit targets
-  --> $DIR/enum_clike_unportable_variant.rs:15:5
+  --> $DIR/enum_clike_unportable_variant.rs:17:5
    |
 LL |     X = 0x1_0000_0000,
    |     ^^^^^^^^^^^^^^^^^
 
 error: C-like enum variant discriminant is not portable to 32-bit targets
-  --> $DIR/enum_clike_unportable_variant.rs:18:5
+  --> $DIR/enum_clike_unportable_variant.rs:21:5
    |
 LL |     A = 0xFFFF_FFFF,
    |     ^^^^^^^^^^^^^^^
 
 error: C-like enum variant discriminant is not portable to 32-bit targets
-  --> $DIR/enum_clike_unportable_variant.rs:25:5
+  --> $DIR/enum_clike_unportable_variant.rs:29:5
    |
 LL |     Z = 0xFFFF_FFFF,
    |     ^^^^^^^^^^^^^^^
 
 error: C-like enum variant discriminant is not portable to 32-bit targets
-  --> $DIR/enum_clike_unportable_variant.rs:26:5
+  --> $DIR/enum_clike_unportable_variant.rs:31:5
    |
 LL |     A = 0x1_0000_0000,
    |     ^^^^^^^^^^^^^^^^^
 
 error: C-like enum variant discriminant is not portable to 32-bit targets
-  --> $DIR/enum_clike_unportable_variant.rs:28:5
+  --> $DIR/enum_clike_unportable_variant.rs:34:5
    |
 LL |     C = (i32::MIN as isize) - 1,
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: C-like enum variant discriminant is not portable to 32-bit targets
-  --> $DIR/enum_clike_unportable_variant.rs:34:5
+  --> $DIR/enum_clike_unportable_variant.rs:41:5
    |
 LL |     Z = 0xFFFF_FFFF,
    |     ^^^^^^^^^^^^^^^
 
 error: C-like enum variant discriminant is not portable to 32-bit targets
-  --> $DIR/enum_clike_unportable_variant.rs:35:5
+  --> $DIR/enum_clike_unportable_variant.rs:43:5
    |
 LL |     A = 0x1_0000_0000,
    |     ^^^^^^^^^^^^^^^^^
 
 error: C-like enum variant discriminant is not portable to 32-bit targets
-  --> $DIR/enum_clike_unportable_variant.rs:40:5
+  --> $DIR/enum_clike_unportable_variant.rs:49:5
    |
 LL |     X = <usize as Trait>::Number,
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.fixed b/src/tools/clippy/tests/ui/explicit_auto_deref.fixed
index 86bee11eb87..12158d0d12a 100644
--- a/src/tools/clippy/tests/ui/explicit_auto_deref.fixed
+++ b/src/tools/clippy/tests/ui/explicit_auto_deref.fixed
@@ -205,7 +205,7 @@ fn main() {
         }
     }
 
-    f_str(&&ref_str); // `needless_borrow` will suggest removing both references
+    f_str(&ref_str); // `needless_borrow` will suggest removing both references
     f_str(&ref_str); // `needless_borrow` will suggest removing only one reference
 
     let x = &&40;
@@ -293,4 +293,32 @@ fn main() {
     fn return_dyn_assoc<'a>(x: &'a &'a u32) -> &'a <&'a u32 as WithAssoc>::Assoc {
         *x
     }
+
+    // Issue #11366
+    let _: &mut u32 = match &mut Some(&mut 0u32) {
+        Some(x) => x,
+        None => panic!(),
+    };
+
+    // Issue #11474
+    pub struct Variant {
+        pub anonymous: Variant0,
+    }
+
+    pub union Variant0 {
+        pub anonymous: std::mem::ManuallyDrop<Variant00>,
+    }
+
+    pub struct Variant00 {
+        pub anonymous: Variant000,
+    }
+
+    pub union Variant000 {
+        pub val: i32,
+    }
+
+    unsafe {
+        let mut p = core::mem::zeroed::<Variant>();
+        (*p.anonymous.anonymous).anonymous.val = 1;
+    }
 }
diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.rs b/src/tools/clippy/tests/ui/explicit_auto_deref.rs
index 7a505bdf558..dec021c1834 100644
--- a/src/tools/clippy/tests/ui/explicit_auto_deref.rs
+++ b/src/tools/clippy/tests/ui/explicit_auto_deref.rs
@@ -293,4 +293,32 @@ fn main() {
     fn return_dyn_assoc<'a>(x: &'a &'a u32) -> &'a <&'a u32 as WithAssoc>::Assoc {
         *x
     }
+
+    // Issue #11366
+    let _: &mut u32 = match &mut Some(&mut 0u32) {
+        Some(x) => &mut *x,
+        None => panic!(),
+    };
+
+    // Issue #11474
+    pub struct Variant {
+        pub anonymous: Variant0,
+    }
+
+    pub union Variant0 {
+        pub anonymous: std::mem::ManuallyDrop<Variant00>,
+    }
+
+    pub struct Variant00 {
+        pub anonymous: Variant000,
+    }
+
+    pub union Variant000 {
+        pub val: i32,
+    }
+
+    unsafe {
+        let mut p = core::mem::zeroed::<Variant>();
+        (*p.anonymous.anonymous).anonymous.val = 1;
+    }
 }
diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.stderr b/src/tools/clippy/tests/ui/explicit_auto_deref.stderr
index bea014d8ad5..3d2a7b0d9f4 100644
--- a/src/tools/clippy/tests/ui/explicit_auto_deref.stderr
+++ b/src/tools/clippy/tests/ui/explicit_auto_deref.stderr
@@ -194,10 +194,10 @@ LL |     let _ = f_str(**ref_ref_str);
    |                   ^^^^^^^^^^^^^ help: try: `ref_ref_str`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:208:13
+  --> $DIR/explicit_auto_deref.rs:208:12
    |
 LL |     f_str(&&*ref_str); // `needless_borrow` will suggest removing both references
-   |             ^^^^^^^^ help: try: `ref_str`
+   |            ^^^^^^^^^ help: try: `ref_str`
 
 error: deref which would be done by auto-deref
   --> $DIR/explicit_auto_deref.rs:209:12
@@ -235,5 +235,11 @@ error: deref which would be done by auto-deref
 LL |         *x
    |         ^^ help: try: `x`
 
-error: aborting due to 39 previous errors
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:299:20
+   |
+LL |         Some(x) => &mut *x,
+   |                    ^^^^^^^ help: try: `x`
+
+error: aborting due to 40 previous errors
 
diff --git a/src/tools/clippy/tests/ui/explicit_iter_loop.fixed b/src/tools/clippy/tests/ui/explicit_iter_loop.fixed
index 57292114e38..f08397defa5 100644
--- a/src/tools/clippy/tests/ui/explicit_iter_loop.fixed
+++ b/src/tools/clippy/tests/ui/explicit_iter_loop.fixed
@@ -4,6 +4,7 @@
     clippy::similar_names,
     clippy::needless_borrow,
     clippy::deref_addrof,
+    clippy::unnecessary_mut_passed,
     dead_code
 )]
 
@@ -20,15 +21,15 @@ fn main() {
     for _ in rvec {}
 
     let rmvec = &mut vec;
-    for _ in &*rmvec {}
-    for _ in &mut *rmvec {}
+    for _ in rmvec.iter() {}
+    for _ in rmvec.iter_mut() {}
 
     for _ in &vec {} // these are fine
     for _ in &mut vec {} // these are fine
 
     for _ in &[1, 2, 3] {}
 
-    for _ in &*(&mut [1, 2, 3]) {}
+    for _ in (&mut [1, 2, 3]).iter() {}
 
     for _ in &[0; 32] {}
     for _ in &[0; 33] {}
diff --git a/src/tools/clippy/tests/ui/explicit_iter_loop.rs b/src/tools/clippy/tests/ui/explicit_iter_loop.rs
index 66280c23843..2ee6825d445 100644
--- a/src/tools/clippy/tests/ui/explicit_iter_loop.rs
+++ b/src/tools/clippy/tests/ui/explicit_iter_loop.rs
@@ -4,6 +4,7 @@
     clippy::similar_names,
     clippy::needless_borrow,
     clippy::deref_addrof,
+    clippy::unnecessary_mut_passed,
     dead_code
 )]
 
diff --git a/src/tools/clippy/tests/ui/explicit_iter_loop.stderr b/src/tools/clippy/tests/ui/explicit_iter_loop.stderr
index af46d74e989..725d9b63cf8 100644
--- a/src/tools/clippy/tests/ui/explicit_iter_loop.stderr
+++ b/src/tools/clippy/tests/ui/explicit_iter_loop.stderr
@@ -1,5 +1,5 @@
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:16:14
+  --> $DIR/explicit_iter_loop.rs:17:14
    |
 LL |     for _ in vec.iter() {}
    |              ^^^^^^^^^^ help: to write this more concisely, try: `&vec`
@@ -11,133 +11,106 @@ LL | #![deny(clippy::explicit_iter_loop)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:17:14
+  --> $DIR/explicit_iter_loop.rs:18:14
    |
 LL |     for _ in vec.iter_mut() {}
    |              ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut vec`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:20:14
+  --> $DIR/explicit_iter_loop.rs:21:14
    |
 LL |     for _ in rvec.iter() {}
    |              ^^^^^^^^^^^ help: to write this more concisely, try: `rvec`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:23:14
-   |
-LL |     for _ in rmvec.iter() {}
-   |              ^^^^^^^^^^^^ help: to write this more concisely, try: `&*rmvec`
-
-error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:24:14
-   |
-LL |     for _ in rmvec.iter_mut() {}
-   |              ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut *rmvec`
-
-error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:29:14
+  --> $DIR/explicit_iter_loop.rs:30:14
    |
 LL |     for _ in [1, 2, 3].iter() {}
    |              ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:31:14
-   |
-LL |     for _ in (&mut [1, 2, 3]).iter() {}
-   |              ^^^^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&*(&mut [1, 2, 3])`
-
-error: the method `iter` doesn't need a mutable reference
-  --> $DIR/explicit_iter_loop.rs:31:14
-   |
-LL |     for _ in (&mut [1, 2, 3]).iter() {}
-   |              ^^^^^^^^^^^^^^^^
-   |
-   = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::unnecessary_mut_passed)]`
-
-error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:33:14
+  --> $DIR/explicit_iter_loop.rs:34:14
    |
 LL |     for _ in [0; 32].iter() {}
    |              ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:34:14
+  --> $DIR/explicit_iter_loop.rs:35:14
    |
 LL |     for _ in [0; 33].iter() {}
    |              ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 33]`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:37:14
+  --> $DIR/explicit_iter_loop.rs:38:14
    |
 LL |     for _ in ll.iter() {}
    |              ^^^^^^^^^ help: to write this more concisely, try: `&ll`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:39:14
+  --> $DIR/explicit_iter_loop.rs:40:14
    |
 LL |     for _ in rll.iter() {}
    |              ^^^^^^^^^^ help: to write this more concisely, try: `rll`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:42:14
+  --> $DIR/explicit_iter_loop.rs:43:14
    |
 LL |     for _ in vd.iter() {}
    |              ^^^^^^^^^ help: to write this more concisely, try: `&vd`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:44:14
+  --> $DIR/explicit_iter_loop.rs:45:14
    |
 LL |     for _ in rvd.iter() {}
    |              ^^^^^^^^^^ help: to write this more concisely, try: `rvd`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:47:14
+  --> $DIR/explicit_iter_loop.rs:48:14
    |
 LL |     for _ in bh.iter() {}
    |              ^^^^^^^^^ help: to write this more concisely, try: `&bh`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:50:14
+  --> $DIR/explicit_iter_loop.rs:51:14
    |
 LL |     for _ in hm.iter() {}
    |              ^^^^^^^^^ help: to write this more concisely, try: `&hm`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:53:14
+  --> $DIR/explicit_iter_loop.rs:54:14
    |
 LL |     for _ in bt.iter() {}
    |              ^^^^^^^^^ help: to write this more concisely, try: `&bt`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:56:14
+  --> $DIR/explicit_iter_loop.rs:57:14
    |
 LL |     for _ in hs.iter() {}
    |              ^^^^^^^^^ help: to write this more concisely, try: `&hs`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:59:14
+  --> $DIR/explicit_iter_loop.rs:60:14
    |
 LL |     for _ in bs.iter() {}
    |              ^^^^^^^^^ help: to write this more concisely, try: `&bs`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:148:14
+  --> $DIR/explicit_iter_loop.rs:149:14
    |
 LL |     for _ in x.iter() {}
    |              ^^^^^^^^ help: to write this more concisely, try: `&x`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:149:14
+  --> $DIR/explicit_iter_loop.rs:150:14
    |
 LL |     for _ in x.iter_mut() {}
    |              ^^^^^^^^^^^^ help: to write this more concisely, try: `&mut x`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:152:14
+  --> $DIR/explicit_iter_loop.rs:153:14
    |
 LL |     for _ in r.iter() {}
    |              ^^^^^^^^ help: to write this more concisely, try: `r`
 
-error: aborting due to 22 previous errors
+error: aborting due to 18 previous errors
 
diff --git a/src/tools/clippy/tests/ui/float_cmp.rs b/src/tools/clippy/tests/ui/float_cmp.rs
index a547a67430f..5057c643732 100644
--- a/src/tools/clippy/tests/ui/float_cmp.rs
+++ b/src/tools/clippy/tests/ui/float_cmp.rs
@@ -41,6 +41,16 @@ impl PartialEq for X {
     }
 }
 
+impl PartialEq<f32> for X {
+    fn eq(&self, o: &f32) -> bool {
+        if self.val.is_nan() {
+            o.is_nan()
+        } else {
+            self.val == *o // no error, inside "eq" fn
+        }
+    }
+}
+
 fn main() {
     ZERO == 0f32; //no error, comparison with zero is ok
     1.0f32 != f32::INFINITY; // also comparison with infinity
@@ -48,6 +58,9 @@ fn main() {
     ZERO == 0.0; //no error, comparison with zero is ok
     ZERO + ZERO != 1.0; //no error, comparison with zero is ok
 
+    let x = X { val: 1.0 };
+    x == 1.0; // no error, custom type that implement PartialOrder for float is not checked
+
     ONE == 1f32;
     ONE == 1.0 + 0.0;
     ONE + ONE == ZERO + ONE + ONE;
diff --git a/src/tools/clippy/tests/ui/float_cmp.stderr b/src/tools/clippy/tests/ui/float_cmp.stderr
index cbe529954d0..217e2987917 100644
--- a/src/tools/clippy/tests/ui/float_cmp.stderr
+++ b/src/tools/clippy/tests/ui/float_cmp.stderr
@@ -1,5 +1,5 @@
 error: strict comparison of `f32` or `f64`
-  --> $DIR/float_cmp.rs:57:5
+  --> $DIR/float_cmp.rs:70:5
    |
 LL |     ONE as f64 != 2.0;
    |     ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin`
@@ -9,7 +9,7 @@ LL |     ONE as f64 != 2.0;
    = help: to override `-D warnings` add `#[allow(clippy::float_cmp)]`
 
 error: strict comparison of `f32` or `f64`
-  --> $DIR/float_cmp.rs:64:5
+  --> $DIR/float_cmp.rs:77:5
    |
 LL |     x == 1.0;
    |     ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin`
@@ -17,7 +17,7 @@ LL |     x == 1.0;
    = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64`
-  --> $DIR/float_cmp.rs:69:5
+  --> $DIR/float_cmp.rs:82:5
    |
 LL |     twice(x) != twice(ONE as f64);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin`
@@ -25,7 +25,7 @@ LL |     twice(x) != twice(ONE as f64);
    = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64`
-  --> $DIR/float_cmp.rs:91:5
+  --> $DIR/float_cmp.rs:104:5
    |
 LL |     NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j];
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin`
@@ -33,7 +33,7 @@ LL |     NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j];
    = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64` arrays
-  --> $DIR/float_cmp.rs:98:5
+  --> $DIR/float_cmp.rs:111:5
    |
 LL |     a1 == a2;
    |     ^^^^^^^^
@@ -41,7 +41,7 @@ LL |     a1 == a2;
    = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64`
-  --> $DIR/float_cmp.rs:101:5
+  --> $DIR/float_cmp.rs:114:5
    |
 LL |     a1[0] == a2[0];
    |     ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin`
diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.stderr b/src/tools/clippy/tests/ui/fn_to_numeric_cast.32bit.stderr
index 671347d2bcd..ea08d8c9cc1 100644
--- a/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.stderr
+++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast.32bit.stderr
@@ -1,141 +1,143 @@
 error: casting function pointer `foo` to `i8`, which truncates the value
-  --> $DIR/fn_to_numeric_cast_32bit.rs:10:13
+  --> $DIR/fn_to_numeric_cast.rs:10:13
    |
 LL |     let _ = foo as i8;
    |             ^^^^^^^^^ help: try: `foo as usize`
    |
    = note: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast_with_truncation)]`
 
 error: casting function pointer `foo` to `i16`, which truncates the value
-  --> $DIR/fn_to_numeric_cast_32bit.rs:11:13
+  --> $DIR/fn_to_numeric_cast.rs:11:13
    |
 LL |     let _ = foo as i16;
    |             ^^^^^^^^^^ help: try: `foo as usize`
 
-error: casting function pointer `foo` to `i32`, which truncates the value
-  --> $DIR/fn_to_numeric_cast_32bit.rs:12:13
+error: casting function pointer `foo` to `i32`
+  --> $DIR/fn_to_numeric_cast.rs:12:13
    |
 LL |     let _ = foo as i32;
    |             ^^^^^^^^^^ help: try: `foo as usize`
+   |
+   = note: `-D clippy::fn-to-numeric-cast` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast)]`
 
 error: casting function pointer `foo` to `i64`
-  --> $DIR/fn_to_numeric_cast_32bit.rs:13:13
+  --> $DIR/fn_to_numeric_cast.rs:13:13
    |
 LL |     let _ = foo as i64;
    |             ^^^^^^^^^^ help: try: `foo as usize`
-   |
-   = note: `-D clippy::fn-to-numeric-cast` implied by `-D warnings`
 
 error: casting function pointer `foo` to `i128`
-  --> $DIR/fn_to_numeric_cast_32bit.rs:14:13
+  --> $DIR/fn_to_numeric_cast.rs:14:13
    |
 LL |     let _ = foo as i128;
    |             ^^^^^^^^^^^ help: try: `foo as usize`
 
 error: casting function pointer `foo` to `isize`
-  --> $DIR/fn_to_numeric_cast_32bit.rs:15:13
+  --> $DIR/fn_to_numeric_cast.rs:15:13
    |
 LL |     let _ = foo as isize;
    |             ^^^^^^^^^^^^ help: try: `foo as usize`
 
 error: casting function pointer `foo` to `u8`, which truncates the value
-  --> $DIR/fn_to_numeric_cast_32bit.rs:17:13
+  --> $DIR/fn_to_numeric_cast.rs:17:13
    |
 LL |     let _ = foo as u8;
    |             ^^^^^^^^^ help: try: `foo as usize`
 
 error: casting function pointer `foo` to `u16`, which truncates the value
-  --> $DIR/fn_to_numeric_cast_32bit.rs:18:13
+  --> $DIR/fn_to_numeric_cast.rs:18:13
    |
 LL |     let _ = foo as u16;
    |             ^^^^^^^^^^ help: try: `foo as usize`
 
-error: casting function pointer `foo` to `u32`, which truncates the value
-  --> $DIR/fn_to_numeric_cast_32bit.rs:19:13
+error: casting function pointer `foo` to `u32`
+  --> $DIR/fn_to_numeric_cast.rs:19:13
    |
 LL |     let _ = foo as u32;
    |             ^^^^^^^^^^ help: try: `foo as usize`
 
 error: casting function pointer `foo` to `u64`
-  --> $DIR/fn_to_numeric_cast_32bit.rs:20:13
+  --> $DIR/fn_to_numeric_cast.rs:20:13
    |
 LL |     let _ = foo as u64;
    |             ^^^^^^^^^^ help: try: `foo as usize`
 
 error: casting function pointer `foo` to `u128`
-  --> $DIR/fn_to_numeric_cast_32bit.rs:21:13
+  --> $DIR/fn_to_numeric_cast.rs:21:13
    |
 LL |     let _ = foo as u128;
    |             ^^^^^^^^^^^ help: try: `foo as usize`
 
 error: casting function pointer `abc` to `i8`, which truncates the value
-  --> $DIR/fn_to_numeric_cast_32bit.rs:34:13
+  --> $DIR/fn_to_numeric_cast.rs:34:13
    |
 LL |     let _ = abc as i8;
    |             ^^^^^^^^^ help: try: `abc as usize`
 
 error: casting function pointer `abc` to `i16`, which truncates the value
-  --> $DIR/fn_to_numeric_cast_32bit.rs:35:13
+  --> $DIR/fn_to_numeric_cast.rs:35:13
    |
 LL |     let _ = abc as i16;
    |             ^^^^^^^^^^ help: try: `abc as usize`
 
-error: casting function pointer `abc` to `i32`, which truncates the value
-  --> $DIR/fn_to_numeric_cast_32bit.rs:36:13
+error: casting function pointer `abc` to `i32`
+  --> $DIR/fn_to_numeric_cast.rs:36:13
    |
 LL |     let _ = abc as i32;
    |             ^^^^^^^^^^ help: try: `abc as usize`
 
 error: casting function pointer `abc` to `i64`
-  --> $DIR/fn_to_numeric_cast_32bit.rs:37:13
+  --> $DIR/fn_to_numeric_cast.rs:37:13
    |
 LL |     let _ = abc as i64;
    |             ^^^^^^^^^^ help: try: `abc as usize`
 
 error: casting function pointer `abc` to `i128`
-  --> $DIR/fn_to_numeric_cast_32bit.rs:38:13
+  --> $DIR/fn_to_numeric_cast.rs:38:13
    |
 LL |     let _ = abc as i128;
    |             ^^^^^^^^^^^ help: try: `abc as usize`
 
 error: casting function pointer `abc` to `isize`
-  --> $DIR/fn_to_numeric_cast_32bit.rs:39:13
+  --> $DIR/fn_to_numeric_cast.rs:39:13
    |
 LL |     let _ = abc as isize;
    |             ^^^^^^^^^^^^ help: try: `abc as usize`
 
 error: casting function pointer `abc` to `u8`, which truncates the value
-  --> $DIR/fn_to_numeric_cast_32bit.rs:41:13
+  --> $DIR/fn_to_numeric_cast.rs:41:13
    |
 LL |     let _ = abc as u8;
    |             ^^^^^^^^^ help: try: `abc as usize`
 
 error: casting function pointer `abc` to `u16`, which truncates the value
-  --> $DIR/fn_to_numeric_cast_32bit.rs:42:13
+  --> $DIR/fn_to_numeric_cast.rs:42:13
    |
 LL |     let _ = abc as u16;
    |             ^^^^^^^^^^ help: try: `abc as usize`
 
-error: casting function pointer `abc` to `u32`, which truncates the value
-  --> $DIR/fn_to_numeric_cast_32bit.rs:43:13
+error: casting function pointer `abc` to `u32`
+  --> $DIR/fn_to_numeric_cast.rs:43:13
    |
 LL |     let _ = abc as u32;
    |             ^^^^^^^^^^ help: try: `abc as usize`
 
 error: casting function pointer `abc` to `u64`
-  --> $DIR/fn_to_numeric_cast_32bit.rs:44:13
+  --> $DIR/fn_to_numeric_cast.rs:44:13
    |
 LL |     let _ = abc as u64;
    |             ^^^^^^^^^^ help: try: `abc as usize`
 
 error: casting function pointer `abc` to `u128`
-  --> $DIR/fn_to_numeric_cast_32bit.rs:45:13
+  --> $DIR/fn_to_numeric_cast.rs:45:13
    |
 LL |     let _ = abc as u128;
    |             ^^^^^^^^^^^ help: try: `abc as usize`
 
-error: casting function pointer `f` to `i32`, which truncates the value
-  --> $DIR/fn_to_numeric_cast_32bit.rs:52:5
+error: casting function pointer `f` to `i32`
+  --> $DIR/fn_to_numeric_cast.rs:52:5
    |
 LL |     f as i32
    |     ^^^^^^^^ help: try: `f as usize`
diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast.stderr b/src/tools/clippy/tests/ui/fn_to_numeric_cast.64bit.stderr
index 53e8ac3c4b4..62f3bfa70ea 100644
--- a/src/tools/clippy/tests/ui/fn_to_numeric_cast.stderr
+++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast.64bit.stderr
@@ -8,19 +8,19 @@ LL |     let _ = foo as i8;
    = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast_with_truncation)]`
 
 error: casting function pointer `foo` to `i16`, which truncates the value
-  --> $DIR/fn_to_numeric_cast.rs:13:13
+  --> $DIR/fn_to_numeric_cast.rs:11:13
    |
 LL |     let _ = foo as i16;
    |             ^^^^^^^^^^ help: try: `foo as usize`
 
 error: casting function pointer `foo` to `i32`, which truncates the value
-  --> $DIR/fn_to_numeric_cast.rs:15:13
+  --> $DIR/fn_to_numeric_cast.rs:12:13
    |
 LL |     let _ = foo as i32;
    |             ^^^^^^^^^^ help: try: `foo as usize`
 
 error: casting function pointer `foo` to `i64`
-  --> $DIR/fn_to_numeric_cast.rs:17:13
+  --> $DIR/fn_to_numeric_cast.rs:13:13
    |
 LL |     let _ = foo as i64;
    |             ^^^^^^^^^^ help: try: `foo as usize`
@@ -29,115 +29,115 @@ LL |     let _ = foo as i64;
    = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast)]`
 
 error: casting function pointer `foo` to `i128`
-  --> $DIR/fn_to_numeric_cast.rs:20:13
+  --> $DIR/fn_to_numeric_cast.rs:14:13
    |
 LL |     let _ = foo as i128;
    |             ^^^^^^^^^^^ help: try: `foo as usize`
 
 error: casting function pointer `foo` to `isize`
-  --> $DIR/fn_to_numeric_cast.rs:22:13
+  --> $DIR/fn_to_numeric_cast.rs:15:13
    |
 LL |     let _ = foo as isize;
    |             ^^^^^^^^^^^^ help: try: `foo as usize`
 
 error: casting function pointer `foo` to `u8`, which truncates the value
-  --> $DIR/fn_to_numeric_cast.rs:25:13
+  --> $DIR/fn_to_numeric_cast.rs:17:13
    |
 LL |     let _ = foo as u8;
    |             ^^^^^^^^^ help: try: `foo as usize`
 
 error: casting function pointer `foo` to `u16`, which truncates the value
-  --> $DIR/fn_to_numeric_cast.rs:27:13
+  --> $DIR/fn_to_numeric_cast.rs:18:13
    |
 LL |     let _ = foo as u16;
    |             ^^^^^^^^^^ help: try: `foo as usize`
 
 error: casting function pointer `foo` to `u32`, which truncates the value
-  --> $DIR/fn_to_numeric_cast.rs:29:13
+  --> $DIR/fn_to_numeric_cast.rs:19:13
    |
 LL |     let _ = foo as u32;
    |             ^^^^^^^^^^ help: try: `foo as usize`
 
 error: casting function pointer `foo` to `u64`
-  --> $DIR/fn_to_numeric_cast.rs:31:13
+  --> $DIR/fn_to_numeric_cast.rs:20:13
    |
 LL |     let _ = foo as u64;
    |             ^^^^^^^^^^ help: try: `foo as usize`
 
 error: casting function pointer `foo` to `u128`
-  --> $DIR/fn_to_numeric_cast.rs:33:13
+  --> $DIR/fn_to_numeric_cast.rs:21:13
    |
 LL |     let _ = foo as u128;
    |             ^^^^^^^^^^^ help: try: `foo as usize`
 
 error: casting function pointer `abc` to `i8`, which truncates the value
-  --> $DIR/fn_to_numeric_cast.rs:47:13
+  --> $DIR/fn_to_numeric_cast.rs:34:13
    |
 LL |     let _ = abc as i8;
    |             ^^^^^^^^^ help: try: `abc as usize`
 
 error: casting function pointer `abc` to `i16`, which truncates the value
-  --> $DIR/fn_to_numeric_cast.rs:49:13
+  --> $DIR/fn_to_numeric_cast.rs:35:13
    |
 LL |     let _ = abc as i16;
    |             ^^^^^^^^^^ help: try: `abc as usize`
 
 error: casting function pointer `abc` to `i32`, which truncates the value
-  --> $DIR/fn_to_numeric_cast.rs:51:13
+  --> $DIR/fn_to_numeric_cast.rs:36:13
    |
 LL |     let _ = abc as i32;
    |             ^^^^^^^^^^ help: try: `abc as usize`
 
 error: casting function pointer `abc` to `i64`
-  --> $DIR/fn_to_numeric_cast.rs:53:13
+  --> $DIR/fn_to_numeric_cast.rs:37:13
    |
 LL |     let _ = abc as i64;
    |             ^^^^^^^^^^ help: try: `abc as usize`
 
 error: casting function pointer `abc` to `i128`
-  --> $DIR/fn_to_numeric_cast.rs:55:13
+  --> $DIR/fn_to_numeric_cast.rs:38:13
    |
 LL |     let _ = abc as i128;
    |             ^^^^^^^^^^^ help: try: `abc as usize`
 
 error: casting function pointer `abc` to `isize`
-  --> $DIR/fn_to_numeric_cast.rs:57:13
+  --> $DIR/fn_to_numeric_cast.rs:39:13
    |
 LL |     let _ = abc as isize;
    |             ^^^^^^^^^^^^ help: try: `abc as usize`
 
 error: casting function pointer `abc` to `u8`, which truncates the value
-  --> $DIR/fn_to_numeric_cast.rs:60:13
+  --> $DIR/fn_to_numeric_cast.rs:41:13
    |
 LL |     let _ = abc as u8;
    |             ^^^^^^^^^ help: try: `abc as usize`
 
 error: casting function pointer `abc` to `u16`, which truncates the value
-  --> $DIR/fn_to_numeric_cast.rs:62:13
+  --> $DIR/fn_to_numeric_cast.rs:42:13
    |
 LL |     let _ = abc as u16;
    |             ^^^^^^^^^^ help: try: `abc as usize`
 
 error: casting function pointer `abc` to `u32`, which truncates the value
-  --> $DIR/fn_to_numeric_cast.rs:64:13
+  --> $DIR/fn_to_numeric_cast.rs:43:13
    |
 LL |     let _ = abc as u32;
    |             ^^^^^^^^^^ help: try: `abc as usize`
 
 error: casting function pointer `abc` to `u64`
-  --> $DIR/fn_to_numeric_cast.rs:66:13
+  --> $DIR/fn_to_numeric_cast.rs:44:13
    |
 LL |     let _ = abc as u64;
    |             ^^^^^^^^^^ help: try: `abc as usize`
 
 error: casting function pointer `abc` to `u128`
-  --> $DIR/fn_to_numeric_cast.rs:68:13
+  --> $DIR/fn_to_numeric_cast.rs:45:13
    |
 LL |     let _ = abc as u128;
    |             ^^^^^^^^^^^ help: try: `abc as usize`
 
 error: casting function pointer `f` to `i32`, which truncates the value
-  --> $DIR/fn_to_numeric_cast.rs:76:5
+  --> $DIR/fn_to_numeric_cast.rs:52:5
    |
 LL |     f as i32
    |     ^^^^^^^^ help: try: `f as usize`
diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs b/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs
index 09128d8176e..9501eb5da4b 100644
--- a/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs
+++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs
@@ -1,4 +1,4 @@
-//@ignore-32bit
+//@stderr-per-bitwidth
 //@no-rustfix
 #![warn(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)]
 
@@ -8,30 +8,17 @@ fn foo() -> String {
 
 fn test_function_to_numeric_cast() {
     let _ = foo as i8;
-    //~^ ERROR: casting function pointer `foo` to `i8`, which truncates the value
-    //~| NOTE: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings`
     let _ = foo as i16;
-    //~^ ERROR: casting function pointer `foo` to `i16`, which truncates the value
     let _ = foo as i32;
-    //~^ ERROR: casting function pointer `foo` to `i32`, which truncates the value
     let _ = foo as i64;
-    //~^ ERROR: casting function pointer `foo` to `i64`
-    //~| NOTE: `-D clippy::fn-to-numeric-cast` implied by `-D warnings`
     let _ = foo as i128;
-    //~^ ERROR: casting function pointer `foo` to `i128`
     let _ = foo as isize;
-    //~^ ERROR: casting function pointer `foo` to `isize`
 
     let _ = foo as u8;
-    //~^ ERROR: casting function pointer `foo` to `u8`, which truncates the value
     let _ = foo as u16;
-    //~^ ERROR: casting function pointer `foo` to `u16`, which truncates the value
     let _ = foo as u32;
-    //~^ ERROR: casting function pointer `foo` to `u32`, which truncates the value
     let _ = foo as u64;
-    //~^ ERROR: casting function pointer `foo` to `u64`
     let _ = foo as u128;
-    //~^ ERROR: casting function pointer `foo` to `u128`
 
     // Casting to usize is OK and should not warn
     let _ = foo as usize;
@@ -45,28 +32,17 @@ fn test_function_var_to_numeric_cast() {
     let abc: fn() -> String = foo;
 
     let _ = abc as i8;
-    //~^ ERROR: casting function pointer `abc` to `i8`, which truncates the value
     let _ = abc as i16;
-    //~^ ERROR: casting function pointer `abc` to `i16`, which truncates the value
     let _ = abc as i32;
-    //~^ ERROR: casting function pointer `abc` to `i32`, which truncates the value
     let _ = abc as i64;
-    //~^ ERROR: casting function pointer `abc` to `i64`
     let _ = abc as i128;
-    //~^ ERROR: casting function pointer `abc` to `i128`
     let _ = abc as isize;
-    //~^ ERROR: casting function pointer `abc` to `isize`
 
     let _ = abc as u8;
-    //~^ ERROR: casting function pointer `abc` to `u8`, which truncates the value
     let _ = abc as u16;
-    //~^ ERROR: casting function pointer `abc` to `u16`, which truncates the value
     let _ = abc as u32;
-    //~^ ERROR: casting function pointer `abc` to `u32`, which truncates the value
     let _ = abc as u64;
-    //~^ ERROR: casting function pointer `abc` to `u64`
     let _ = abc as u128;
-    //~^ ERROR: casting function pointer `abc` to `u128`
 
     // Casting to usize is OK and should not warn
     let _ = abc as usize;
@@ -74,7 +50,6 @@ fn test_function_var_to_numeric_cast() {
 
 fn fn_with_fn_args(f: fn(i32) -> i32) -> i32 {
     f as i32
-    //~^ ERROR: casting function pointer `f` to `i32`, which truncates the value
 }
 
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.rs b/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.rs
deleted file mode 100644
index 93e9361f4dc..00000000000
--- a/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.rs
+++ /dev/null
@@ -1,80 +0,0 @@
-//@ignore-64bit
-
-#![warn(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)]
-
-fn foo() -> String {
-    String::new()
-}
-
-fn test_function_to_numeric_cast() {
-    let _ = foo as i8;
-    //~^ ERROR: casting function pointer `foo` to `i8`, which truncates the value
-    //~| NOTE: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings`
-    let _ = foo as i16;
-    //~^ ERROR: casting function pointer `foo` to `i16`, which truncates the value
-    let _ = foo as i32;
-    //~^ ERROR: casting function pointer `foo` to `i32`, which truncates the value
-    let _ = foo as i64;
-    //~^ ERROR: casting function pointer `foo` to `i64`
-    //~| NOTE: `-D clippy::fn-to-numeric-cast` implied by `-D warnings`
-    let _ = foo as i128;
-    //~^ ERROR: casting function pointer `foo` to `i128`
-    let _ = foo as isize;
-    //~^ ERROR: casting function pointer `foo` to `isize`
-
-    let _ = foo as u8;
-    //~^ ERROR: casting function pointer `foo` to `u8`, which truncates the value
-    let _ = foo as u16;
-    //~^ ERROR: casting function pointer `foo` to `u16`, which truncates the value
-    let _ = foo as u32;
-    //~^ ERROR: casting function pointer `foo` to `u32`, which truncates the value
-    let _ = foo as u64;
-    //~^ ERROR: casting function pointer `foo` to `u64`
-    let _ = foo as u128;
-    //~^ ERROR: casting function pointer `foo` to `u128`
-
-    // Casting to usize is OK and should not warn
-    let _ = foo as usize;
-
-    // Cast `f` (a `FnDef`) to `fn()` should not warn
-    fn f() {}
-    let _ = f as fn();
-}
-
-fn test_function_var_to_numeric_cast() {
-    let abc: fn() -> String = foo;
-
-    let _ = abc as i8;
-    //~^ ERROR: casting function pointer `abc` to `i8`, which truncates the value
-    let _ = abc as i16;
-    //~^ ERROR: casting function pointer `abc` to `i16`, which truncates the value
-    let _ = abc as i32;
-    //~^ ERROR: casting function pointer `abc` to `i32`, which truncates the value
-    let _ = abc as i64;
-    //~^ ERROR: casting function pointer `abc` to `i64`
-    let _ = abc as i128;
-    //~^ ERROR: casting function pointer `abc` to `i128`
-    let _ = abc as isize;
-    //~^ ERROR: casting function pointer `abc` to `isize`
-
-    let _ = abc as u8;
-    //~^ ERROR: casting function pointer `abc` to `u8`, which truncates the value
-    let _ = abc as u16;
-    //~^ ERROR: casting function pointer `abc` to `u16`, which truncates the value
-    let _ = abc as u32;
-    //~^ ERROR: casting function pointer `abc` to `u32`, which truncates the value
-    let _ = abc as u64;
-    //~^ ERROR: casting function pointer `abc` to `u64`
-    let _ = abc as u128;
-    //~^ ERROR: casting function pointer `abc` to `u128`
-
-    // Casting to usize is OK and should not warn
-    let _ = abc as usize;
-}
-
-fn fn_with_fn_args(f: fn(i32) -> i32) -> i32 {
-    f as i32
-    //~^ ERROR: casting function pointer `f` to `i32`, which truncates the value
-}
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.rs b/src/tools/clippy/tests/ui/if_then_some_else_none.rs
index 8fa0f34a6c4..77abd663e0a 100644
--- a/src/tools/clippy/tests/ui/if_then_some_else_none.rs
+++ b/src/tools/clippy/tests/ui/if_then_some_else_none.rs
@@ -117,3 +117,15 @@ fn f(b: bool, v: Option<()>) -> Option<()> {
         None
     }
 }
+
+fn issue11394(b: bool, v: Result<(), ()>) -> Result<(), ()> {
+    let x = if b {
+        #[allow(clippy::let_unit_value)]
+        let _ = v?;
+        Some(())
+    } else {
+        None
+    };
+
+    Ok(())
+}
diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed b/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed
index 2912eede4d9..6c6f21fee16 100644
--- a/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed
+++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed
@@ -1,5 +1,5 @@
 #![warn(clippy::ignored_unit_patterns)]
-#![allow(clippy::redundant_pattern_matching, clippy::single_match)]
+#![allow(clippy::let_unit_value, clippy::redundant_pattern_matching, clippy::single_match)]
 
 fn foo() -> Result<(), ()> {
     unimplemented!()
@@ -7,9 +7,19 @@ fn foo() -> Result<(), ()> {
 
 fn main() {
     match foo() {
-        Ok(()) => {},
-        Err(()) => {},
+        Ok(()) => {},  //~ ERROR: matching over `()` is more explicit
+        Err(()) => {}, //~ ERROR: matching over `()` is more explicit
     }
     if let Ok(()) = foo() {}
+    //~^ ERROR: matching over `()` is more explicit
     let _ = foo().map_err(|()| todo!());
+    //~^ ERROR: matching over `()` is more explicit
+}
+
+#[allow(unused)]
+pub fn moo(_: ()) {
+    let () = foo().unwrap();
+    //~^ ERROR: matching over `()` is more explicit
+    let _: () = foo().unwrap();
+    let _: () = ();
 }
diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.rs b/src/tools/clippy/tests/ui/ignored_unit_patterns.rs
index d180cd8d2fd..5e8c2e03ba2 100644
--- a/src/tools/clippy/tests/ui/ignored_unit_patterns.rs
+++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.rs
@@ -1,5 +1,5 @@
 #![warn(clippy::ignored_unit_patterns)]
-#![allow(clippy::redundant_pattern_matching, clippy::single_match)]
+#![allow(clippy::let_unit_value, clippy::redundant_pattern_matching, clippy::single_match)]
 
 fn foo() -> Result<(), ()> {
     unimplemented!()
@@ -7,9 +7,19 @@ fn foo() -> Result<(), ()> {
 
 fn main() {
     match foo() {
-        Ok(_) => {},
-        Err(_) => {},
+        Ok(_) => {},  //~ ERROR: matching over `()` is more explicit
+        Err(_) => {}, //~ ERROR: matching over `()` is more explicit
     }
     if let Ok(_) = foo() {}
+    //~^ ERROR: matching over `()` is more explicit
     let _ = foo().map_err(|_| todo!());
+    //~^ ERROR: matching over `()` is more explicit
+}
+
+#[allow(unused)]
+pub fn moo(_: ()) {
+    let _ = foo().unwrap();
+    //~^ ERROR: matching over `()` is more explicit
+    let _: () = foo().unwrap();
+    let _: () = ();
 }
diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr b/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr
index a8ced22397d..df5e1d89e90 100644
--- a/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr
+++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr
@@ -20,10 +20,16 @@ LL |     if let Ok(_) = foo() {}
    |               ^ help: use `()` instead of `_`: `()`
 
 error: matching over `()` is more explicit
-  --> $DIR/ignored_unit_patterns.rs:14:28
+  --> $DIR/ignored_unit_patterns.rs:15:28
    |
 LL |     let _ = foo().map_err(|_| todo!());
    |                            ^ help: use `()` instead of `_`: `()`
 
-error: aborting due to 4 previous errors
+error: matching over `()` is more explicit
+  --> $DIR/ignored_unit_patterns.rs:21:9
+   |
+LL |     let _ = foo().unwrap();
+   |         ^ help: use `()` instead of `_`: `()`
+
+error: aborting due to 5 previous errors
 
diff --git a/src/tools/clippy/tests/ui/implied_bounds_in_impls.fixed b/src/tools/clippy/tests/ui/implied_bounds_in_impls.fixed
index 952c2b70619..a50fa0ccf6e 100644
--- a/src/tools/clippy/tests/ui/implied_bounds_in_impls.fixed
+++ b/src/tools/clippy/tests/ui/implied_bounds_in_impls.fixed
@@ -65,4 +65,61 @@ impl SomeTrait for SomeStruct {
     }
 }
 
+mod issue11422 {
+    use core::fmt::Debug;
+    // Some additional tests that would cause ICEs:
+
+    // `PartialOrd` has a default generic parameter and does not need to be explicitly specified.
+    // This needs special handling.
+    fn default_generic_param1() -> impl PartialOrd + Debug {}
+    fn default_generic_param2() -> impl PartialOrd + Debug {}
+
+    // Referring to `Self` in the supertrait clause needs special handling.
+    trait Trait1<X: ?Sized> {}
+    trait Trait2: Trait1<Self> {}
+    impl Trait1<()> for () {}
+    impl Trait2 for () {}
+
+    fn f() -> impl Trait1<()> + Trait2 {}
+}
+
+mod issue11435 {
+    // Associated type needs to be included on DoubleEndedIterator in the suggestion
+    fn my_iter() -> impl DoubleEndedIterator<Item = u32> {
+        0..5
+    }
+
+    // Removing the `Clone` bound should include the `+` behind it in its remove suggestion
+    fn f() -> impl Copy {
+        1
+    }
+
+    trait Trait1<T> {
+        type U;
+    }
+    impl Trait1<i32> for () {
+        type U = i64;
+    }
+    trait Trait2<T>: Trait1<T> {}
+    impl Trait2<i32> for () {}
+
+    // When the other trait has generics, it shouldn't add another pair of `<>`
+    fn f2() -> impl Trait2<i32, U = i64> {}
+
+    trait Trait3<T, U, V> {
+        type X;
+        type Y;
+    }
+    trait Trait4<T>: Trait3<T, i16, i64> {}
+    impl Trait3<i8, i16, i64> for () {
+        type X = i32;
+        type Y = i128;
+    }
+    impl Trait4<i8> for () {}
+
+    // Associated type `X` is specified, but `Y` is not, so only that associated type should be moved
+    // over
+    fn f3() -> impl Trait4<i8, X = i32, Y = i128> {}
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/implied_bounds_in_impls.rs b/src/tools/clippy/tests/ui/implied_bounds_in_impls.rs
index 475f2621bbf..e74ed4425b8 100644
--- a/src/tools/clippy/tests/ui/implied_bounds_in_impls.rs
+++ b/src/tools/clippy/tests/ui/implied_bounds_in_impls.rs
@@ -65,4 +65,61 @@ impl SomeTrait for SomeStruct {
     }
 }
 
+mod issue11422 {
+    use core::fmt::Debug;
+    // Some additional tests that would cause ICEs:
+
+    // `PartialOrd` has a default generic parameter and does not need to be explicitly specified.
+    // This needs special handling.
+    fn default_generic_param1() -> impl PartialEq + PartialOrd + Debug {}
+    fn default_generic_param2() -> impl PartialOrd + PartialEq + Debug {}
+
+    // Referring to `Self` in the supertrait clause needs special handling.
+    trait Trait1<X: ?Sized> {}
+    trait Trait2: Trait1<Self> {}
+    impl Trait1<()> for () {}
+    impl Trait2 for () {}
+
+    fn f() -> impl Trait1<()> + Trait2 {}
+}
+
+mod issue11435 {
+    // Associated type needs to be included on DoubleEndedIterator in the suggestion
+    fn my_iter() -> impl Iterator<Item = u32> + DoubleEndedIterator {
+        0..5
+    }
+
+    // Removing the `Clone` bound should include the `+` behind it in its remove suggestion
+    fn f() -> impl Copy + Clone {
+        1
+    }
+
+    trait Trait1<T> {
+        type U;
+    }
+    impl Trait1<i32> for () {
+        type U = i64;
+    }
+    trait Trait2<T>: Trait1<T> {}
+    impl Trait2<i32> for () {}
+
+    // When the other trait has generics, it shouldn't add another pair of `<>`
+    fn f2() -> impl Trait1<i32, U = i64> + Trait2<i32> {}
+
+    trait Trait3<T, U, V> {
+        type X;
+        type Y;
+    }
+    trait Trait4<T>: Trait3<T, i16, i64> {}
+    impl Trait3<i8, i16, i64> for () {
+        type X = i32;
+        type Y = i128;
+    }
+    impl Trait4<i8> for () {}
+
+    // Associated type `X` is specified, but `Y` is not, so only that associated type should be moved
+    // over
+    fn f3() -> impl Trait3<i8, i16, i64, X = i32, Y = i128> + Trait4<i8, X = i32> {}
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/implied_bounds_in_impls.stderr b/src/tools/clippy/tests/ui/implied_bounds_in_impls.stderr
index e2b1ecb9f1e..72dc2a183a3 100644
--- a/src/tools/clippy/tests/ui/implied_bounds_in_impls.stderr
+++ b/src/tools/clippy/tests/ui/implied_bounds_in_impls.stderr
@@ -120,5 +120,77 @@ LL -     fn f() -> impl Deref + DerefMut<Target = u8> {
 LL +     fn f() -> impl DerefMut<Target = u8> {
    |
 
-error: aborting due to 10 previous errors
+error: this bound is already specified as the supertrait of `PartialOrd`
+  --> $DIR/implied_bounds_in_impls.rs:74:41
+   |
+LL |     fn default_generic_param1() -> impl PartialEq + PartialOrd + Debug {}
+   |                                         ^^^^^^^^^
+   |
+help: try removing this bound
+   |
+LL -     fn default_generic_param1() -> impl PartialEq + PartialOrd + Debug {}
+LL +     fn default_generic_param1() -> impl PartialOrd + Debug {}
+   |
+
+error: this bound is already specified as the supertrait of `PartialOrd`
+  --> $DIR/implied_bounds_in_impls.rs:75:54
+   |
+LL |     fn default_generic_param2() -> impl PartialOrd + PartialEq + Debug {}
+   |                                                      ^^^^^^^^^
+   |
+help: try removing this bound
+   |
+LL -     fn default_generic_param2() -> impl PartialOrd + PartialEq + Debug {}
+LL +     fn default_generic_param2() -> impl PartialOrd + Debug {}
+   |
+
+error: this bound is already specified as the supertrait of `DoubleEndedIterator`
+  --> $DIR/implied_bounds_in_impls.rs:88:26
+   |
+LL |     fn my_iter() -> impl Iterator<Item = u32> + DoubleEndedIterator {
+   |                          ^^^^^^^^^^^^^^^^^^^^
+   |
+help: try removing this bound
+   |
+LL -     fn my_iter() -> impl Iterator<Item = u32> + DoubleEndedIterator {
+LL +     fn my_iter() -> impl DoubleEndedIterator<Item = u32> {
+   |
+
+error: this bound is already specified as the supertrait of `Copy`
+  --> $DIR/implied_bounds_in_impls.rs:93:27
+   |
+LL |     fn f() -> impl Copy + Clone {
+   |                           ^^^^^
+   |
+help: try removing this bound
+   |
+LL -     fn f() -> impl Copy + Clone {
+LL +     fn f() -> impl Copy {
+   |
+
+error: this bound is already specified as the supertrait of `Trait2<i32>`
+  --> $DIR/implied_bounds_in_impls.rs:107:21
+   |
+LL |     fn f2() -> impl Trait1<i32, U = i64> + Trait2<i32> {}
+   |                     ^^^^^^^^^^^^^^^^^^^^
+   |
+help: try removing this bound
+   |
+LL -     fn f2() -> impl Trait1<i32, U = i64> + Trait2<i32> {}
+LL +     fn f2() -> impl Trait2<i32, U = i64> {}
+   |
+
+error: this bound is already specified as the supertrait of `Trait4<i8, X = i32>`
+  --> $DIR/implied_bounds_in_impls.rs:122:21
+   |
+LL |     fn f3() -> impl Trait3<i8, i16, i64, X = i32, Y = i128> + Trait4<i8, X = i32> {}
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try removing this bound
+   |
+LL -     fn f3() -> impl Trait3<i8, i16, i64, X = i32, Y = i128> + Trait4<i8, X = i32> {}
+LL +     fn f3() -> impl Trait4<i8, X = i32, Y = i128> {}
+   |
+
+error: aborting due to 16 previous errors
 
diff --git a/src/tools/clippy/tests/ui/iter_out_of_bounds.rs b/src/tools/clippy/tests/ui/iter_out_of_bounds.rs
new file mode 100644
index 00000000000..3cfe6e82fc1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_out_of_bounds.rs
@@ -0,0 +1,71 @@
+//@no-rustfix
+
+#![deny(clippy::iter_out_of_bounds)]
+#![allow(clippy::useless_vec)]
+
+fn opaque_empty_iter() -> impl Iterator<Item = ()> {
+    std::iter::empty()
+}
+
+fn main() {
+    #[allow(clippy::never_loop)]
+    for _ in [1, 2, 3].iter().skip(4) {
+        //~^ ERROR: this `.skip()` call skips more items than the iterator will produce
+        unreachable!();
+    }
+    for (i, _) in [1, 2, 3].iter().take(4).enumerate() {
+        //~^ ERROR: this `.take()` call takes more items than the iterator will produce
+        assert!(i <= 2);
+    }
+
+    #[allow(clippy::needless_borrow)]
+    for _ in (&&&&&&[1, 2, 3]).iter().take(4) {}
+    //~^ ERROR: this `.take()` call takes more items than the iterator will produce
+
+    for _ in [1, 2, 3].iter().skip(4) {}
+    //~^ ERROR: this `.skip()` call skips more items than the iterator will produce
+
+    for _ in [1; 3].iter().skip(4) {}
+    //~^ ERROR: this `.skip()` call skips more items than the iterator will produce
+
+    // Should not lint
+    for _ in opaque_empty_iter().skip(1) {}
+
+    for _ in vec![1, 2, 3].iter().skip(4) {}
+    //~^ ERROR: this `.skip()` call skips more items than the iterator will produce
+
+    for _ in vec![1; 3].iter().skip(4) {}
+    //~^ ERROR: this `.skip()` call skips more items than the iterator will produce
+
+    let x = [1, 2, 3];
+    for _ in x.iter().skip(4) {}
+    //~^ ERROR: this `.skip()` call skips more items than the iterator will produce
+
+    let n = 4;
+    for _ in x.iter().skip(n) {}
+    //~^ ERROR: this `.skip()` call skips more items than the iterator will produce
+
+    let empty = std::iter::empty::<i8>;
+
+    for _ in empty().skip(1) {}
+    //~^ ERROR: this `.skip()` call skips more items than the iterator will produce
+
+    for _ in empty().take(1) {}
+    //~^ ERROR: this `.take()` call takes more items than the iterator will produce
+
+    for _ in std::iter::once(1).skip(2) {}
+    //~^ ERROR: this `.skip()` call skips more items than the iterator will produce
+
+    for _ in std::iter::once(1).take(2) {}
+    //~^ ERROR: this `.take()` call takes more items than the iterator will produce
+
+    for x in [].iter().take(1) {
+        //~^ ERROR: this `.take()` call takes more items than the iterator will produce
+        let _: &i32 = x;
+    }
+
+    // ok, not out of bounds
+    for _ in [1].iter().take(1) {}
+    for _ in [1, 2, 3].iter().take(2) {}
+    for _ in [1, 2, 3].iter().skip(2) {}
+}
diff --git a/src/tools/clippy/tests/ui/iter_out_of_bounds.stderr b/src/tools/clippy/tests/ui/iter_out_of_bounds.stderr
new file mode 100644
index 00000000000..f235faec8e5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/iter_out_of_bounds.stderr
@@ -0,0 +1,119 @@
+error: this `.skip()` call skips more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:12:14
+   |
+LL |     for _ in [1, 2, 3].iter().skip(4) {
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and will create an empty iterator
+note: the lint level is defined here
+  --> $DIR/iter_out_of_bounds.rs:3:9
+   |
+LL | #![deny(clippy::iter_out_of_bounds)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this `.take()` call takes more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:16:19
+   |
+LL |     for (i, _) in [1, 2, 3].iter().take(4).enumerate() {
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and the returned iterator will simply yield the same items
+
+error: this `.take()` call takes more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:22:14
+   |
+LL |     for _ in (&&&&&&[1, 2, 3]).iter().take(4) {}
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and the returned iterator will simply yield the same items
+
+error: this `.skip()` call skips more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:25:14
+   |
+LL |     for _ in [1, 2, 3].iter().skip(4) {}
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and will create an empty iterator
+
+error: this `.skip()` call skips more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:28:14
+   |
+LL |     for _ in [1; 3].iter().skip(4) {}
+   |              ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and will create an empty iterator
+
+error: this `.skip()` call skips more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:34:14
+   |
+LL |     for _ in vec![1, 2, 3].iter().skip(4) {}
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and will create an empty iterator
+
+error: this `.skip()` call skips more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:37:14
+   |
+LL |     for _ in vec![1; 3].iter().skip(4) {}
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and will create an empty iterator
+
+error: this `.skip()` call skips more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:41:14
+   |
+LL |     for _ in x.iter().skip(4) {}
+   |              ^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and will create an empty iterator
+
+error: this `.skip()` call skips more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:45:14
+   |
+LL |     for _ in x.iter().skip(n) {}
+   |              ^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and will create an empty iterator
+
+error: this `.skip()` call skips more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:50:14
+   |
+LL |     for _ in empty().skip(1) {}
+   |              ^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and will create an empty iterator
+
+error: this `.take()` call takes more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:53:14
+   |
+LL |     for _ in empty().take(1) {}
+   |              ^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and the returned iterator will simply yield the same items
+
+error: this `.skip()` call skips more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:56:14
+   |
+LL |     for _ in std::iter::once(1).skip(2) {}
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and will create an empty iterator
+
+error: this `.take()` call takes more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:59:14
+   |
+LL |     for _ in std::iter::once(1).take(2) {}
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and the returned iterator will simply yield the same items
+
+error: this `.take()` call takes more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:62:14
+   |
+LL |     for x in [].iter().take(1) {
+   |              ^^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and the returned iterator will simply yield the same items
+
+error: aborting due to 14 previous errors
+
diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed
index 9dd046fec1f..7d8a584b022 100644
--- a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed
+++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed
@@ -63,11 +63,9 @@ fn main() {
 
     let _ = vec.iter().for_each(|x| assert!(!x.is_empty()));
 
-    // Not implemented yet
-    let _ = vec.iter().cloned().all(|x| x.len() == 1);
+    let _ = vec.iter().all(|x| x.len() == 1);
 
-    // Not implemented yet
-    let _ = vec.iter().cloned().any(|x| x.len() == 1);
+    let _ = vec.iter().any(|x| x.len() == 1);
 
     // Should probably stay as it is.
     let _ = [0, 1, 2, 3, 4].iter().cloned().take(10);
diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs
index 3cab3669554..58c374ab8cd 100644
--- a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs
+++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs
@@ -64,10 +64,8 @@ fn main() {
 
     let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty()));
 
-    // Not implemented yet
     let _ = vec.iter().cloned().all(|x| x.len() == 1);
 
-    // Not implemented yet
     let _ = vec.iter().cloned().any(|x| x.len() == 1);
 
     // Should probably stay as it is.
diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr b/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr
index fbcd33066d8..a9a739688eb 100644
--- a/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr
+++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr
@@ -148,5 +148,21 @@ LL |     let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty()));
    |                       |
    |                       help: try: `.for_each(|x| assert!(!x.is_empty()))`
 
-error: aborting due to 17 previous errors
+error: unneeded cloning of iterator items
+  --> $DIR/iter_overeager_cloned.rs:67:13
+   |
+LL |     let _ = vec.iter().cloned().all(|x| x.len() == 1);
+   |             ^^^^^^^^^^-------------------------------
+   |                       |
+   |                       help: try: `.all(|x| x.len() == 1)`
+
+error: unneeded cloning of iterator items
+  --> $DIR/iter_overeager_cloned.rs:69:13
+   |
+LL |     let _ = vec.iter().cloned().any(|x| x.len() == 1);
+   |             ^^^^^^^^^^-------------------------------
+   |                       |
+   |                       help: try: `.any(|x| x.len() == 1)`
+
+error: aborting due to 19 previous errors
 
diff --git a/src/tools/clippy/tests/ui/iter_skip_next.fixed b/src/tools/clippy/tests/ui/iter_skip_next.fixed
index 310b24a9cde..3e41b363249 100644
--- a/src/tools/clippy/tests/ui/iter_skip_next.fixed
+++ b/src/tools/clippy/tests/ui/iter_skip_next.fixed
@@ -4,6 +4,7 @@
 #![allow(clippy::disallowed_names)]
 #![allow(clippy::iter_nth)]
 #![allow(clippy::useless_vec)]
+#![allow(clippy::iter_out_of_bounds)]
 #![allow(unused_mut, dead_code)]
 
 extern crate option_helpers;
diff --git a/src/tools/clippy/tests/ui/iter_skip_next.rs b/src/tools/clippy/tests/ui/iter_skip_next.rs
index 222d6a2a184..6d96441ca96 100644
--- a/src/tools/clippy/tests/ui/iter_skip_next.rs
+++ b/src/tools/clippy/tests/ui/iter_skip_next.rs
@@ -4,6 +4,7 @@
 #![allow(clippy::disallowed_names)]
 #![allow(clippy::iter_nth)]
 #![allow(clippy::useless_vec)]
+#![allow(clippy::iter_out_of_bounds)]
 #![allow(unused_mut, dead_code)]
 
 extern crate option_helpers;
diff --git a/src/tools/clippy/tests/ui/iter_skip_next.stderr b/src/tools/clippy/tests/ui/iter_skip_next.stderr
index 0b6bf652b1f..39b173e7586 100644
--- a/src/tools/clippy/tests/ui/iter_skip_next.stderr
+++ b/src/tools/clippy/tests/ui/iter_skip_next.stderr
@@ -1,5 +1,5 @@
 error: called `skip(..).next()` on an iterator
-  --> $DIR/iter_skip_next.rs:16:28
+  --> $DIR/iter_skip_next.rs:17:28
    |
 LL |     let _ = some_vec.iter().skip(42).next();
    |                            ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)`
@@ -8,37 +8,37 @@ LL |     let _ = some_vec.iter().skip(42).next();
    = help: to override `-D warnings` add `#[allow(clippy::iter_skip_next)]`
 
 error: called `skip(..).next()` on an iterator
-  --> $DIR/iter_skip_next.rs:17:36
+  --> $DIR/iter_skip_next.rs:18:36
    |
 LL |     let _ = some_vec.iter().cycle().skip(42).next();
    |                                    ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)`
 
 error: called `skip(..).next()` on an iterator
-  --> $DIR/iter_skip_next.rs:18:20
+  --> $DIR/iter_skip_next.rs:19:20
    |
 LL |     let _ = (1..10).skip(10).next();
    |                    ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(10)`
 
 error: called `skip(..).next()` on an iterator
-  --> $DIR/iter_skip_next.rs:19:33
+  --> $DIR/iter_skip_next.rs:20:33
    |
 LL |     let _ = &some_vec[..].iter().skip(3).next();
    |                                 ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(3)`
 
 error: called `skip(..).next()` on an iterator
-  --> $DIR/iter_skip_next.rs:27:26
+  --> $DIR/iter_skip_next.rs:28:26
    |
 LL |     let _: Vec<&str> = sp.skip(1).next().unwrap().split(' ').collect();
    |                          ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
 
 error: called `skip(..).next()` on an iterator
-  --> $DIR/iter_skip_next.rs:29:29
+  --> $DIR/iter_skip_next.rs:30:29
    |
 LL |         let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
    |                             ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
 
 error: called `skip(..).next()` on an iterator
-  --> $DIR/iter_skip_next.rs:35:29
+  --> $DIR/iter_skip_next.rs:36:29
    |
 LL |         let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
    |                             ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
diff --git a/src/tools/clippy/tests/ui/iter_skip_next_unfixable.rs b/src/tools/clippy/tests/ui/iter_skip_next_unfixable.rs
index 9c224f4117d..6c98bdc8c88 100644
--- a/src/tools/clippy/tests/ui/iter_skip_next_unfixable.rs
+++ b/src/tools/clippy/tests/ui/iter_skip_next_unfixable.rs
@@ -1,5 +1,5 @@
 #![warn(clippy::iter_skip_next)]
-#![allow(dead_code)]
+#![allow(dead_code, clippy::iter_out_of_bounds)]
 //@no-rustfix
 /// Checks implementation of `ITER_SKIP_NEXT` lint
 fn main() {
diff --git a/src/tools/clippy/tests/ui/iter_skip_zero.fixed b/src/tools/clippy/tests/ui/iter_skip_zero.fixed
index 62a83d5905b..447d07100e9 100644
--- a/src/tools/clippy/tests/ui/iter_skip_zero.fixed
+++ b/src/tools/clippy/tests/ui/iter_skip_zero.fixed
@@ -1,5 +1,5 @@
 //@aux-build:proc_macros.rs
-#![allow(clippy::useless_vec, unused)]
+#![allow(clippy::useless_vec, clippy::iter_out_of_bounds, unused)]
 #![warn(clippy::iter_skip_zero)]
 
 #[macro_use]
diff --git a/src/tools/clippy/tests/ui/iter_skip_zero.rs b/src/tools/clippy/tests/ui/iter_skip_zero.rs
index c96696dde65..ba63c398180 100644
--- a/src/tools/clippy/tests/ui/iter_skip_zero.rs
+++ b/src/tools/clippy/tests/ui/iter_skip_zero.rs
@@ -1,5 +1,5 @@
 //@aux-build:proc_macros.rs
-#![allow(clippy::useless_vec, unused)]
+#![allow(clippy::useless_vec, clippy::iter_out_of_bounds, unused)]
 #![warn(clippy::iter_skip_zero)]
 
 #[macro_use]
diff --git a/src/tools/clippy/tests/ui/large_enum_variant.32bit.stderr b/src/tools/clippy/tests/ui/large_enum_variant.32bit.stderr
new file mode 100644
index 00000000000..0e0eee21cf3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_enum_variant.32bit.stderr
@@ -0,0 +1,280 @@
+error: large size difference between variants
+  --> $DIR/large_enum_variant.rs:11:1
+   |
+LL | / enum LargeEnum {
+LL | |     A(i32),
+   | |     ------ the second-largest variant contains at least 4 bytes
+LL | |     B([i32; 8000]),
+   | |     -------------- the largest variant contains at least 32000 bytes
+LL | | }
+   | |_^ the entire enum is at least 32004 bytes
+   |
+   = note: `-D clippy::large-enum-variant` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::large_enum_variant)]`
+help: consider boxing the large fields to reduce the total size of the enum
+   |
+LL |     B(Box<[i32; 8000]>),
+   |       ~~~~~~~~~~~~~~~~
+
+error: large size difference between variants
+  --> $DIR/large_enum_variant.rs:35:1
+   |
+LL | / enum LargeEnum2 {
+LL | |     VariantOk(i32, u32),
+   | |     ------------------- the second-largest variant contains at least 8 bytes
+LL | |     ContainingLargeEnum(LargeEnum),
+   | |     ------------------------------ the largest variant contains at least 32004 bytes
+LL | | }
+   | |_^ the entire enum is at least 32004 bytes
+   |
+help: consider boxing the large fields to reduce the total size of the enum
+   |
+LL |     ContainingLargeEnum(Box<LargeEnum>),
+   |                         ~~~~~~~~~~~~~~
+
+error: large size difference between variants
+  --> $DIR/large_enum_variant.rs:40:1
+   |
+LL | / enum LargeEnum3 {
+LL | |     ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]),
+   | |     --------------------------------------------------------- the largest variant contains at least 70004 bytes
+LL | |     VoidVariant,
+LL | |     StructLikeLittle { x: i32, y: i32 },
+   | |     ----------------------------------- the second-largest variant contains at least 8 bytes
+LL | | }
+   | |_^ the entire enum is at least 70008 bytes
+   |
+help: consider boxing the large fields to reduce the total size of the enum
+   |
+LL |     ContainingMoreThanOneField(i32, Box<[i32; 8000]>, Box<[i32; 9500]>),
+   |                                     ~~~~~~~~~~~~~~~~  ~~~~~~~~~~~~~~~~
+
+error: large size difference between variants
+  --> $DIR/large_enum_variant.rs:46:1
+   |
+LL | / enum LargeEnum4 {
+LL | |     VariantOk(i32, u32),
+   | |     ------------------- the second-largest variant contains at least 8 bytes
+LL | |     StructLikeLarge { x: [i32; 8000], y: i32 },
+   | |     ------------------------------------------ the largest variant contains at least 32004 bytes
+LL | | }
+   | |_^ the entire enum is at least 32008 bytes
+   |
+help: consider boxing the large fields to reduce the total size of the enum
+   |
+LL |     StructLikeLarge { x: Box<[i32; 8000]>, y: i32 },
+   |                          ~~~~~~~~~~~~~~~~
+
+error: large size difference between variants
+  --> $DIR/large_enum_variant.rs:51:1
+   |
+LL | / enum LargeEnum5 {
+LL | |     VariantOk(i32, u32),
+   | |     ------------------- the second-largest variant contains at least 8 bytes
+LL | |     StructLikeLarge2 { x: [i32; 8000] },
+   | |     ----------------------------------- the largest variant contains at least 32000 bytes
+LL | | }
+   | |_^ the entire enum is at least 32004 bytes
+   |
+help: consider boxing the large fields to reduce the total size of the enum
+   |
+LL |     StructLikeLarge2 { x: Box<[i32; 8000]> },
+   |                           ~~~~~~~~~~~~~~~~
+
+error: large size difference between variants
+  --> $DIR/large_enum_variant.rs:67:1
+   |
+LL | / enum LargeEnum7 {
+LL | |     A,
+LL | |     B([u8; 1255]),
+   | |     ------------- the largest variant contains at least 1255 bytes
+LL | |     C([u8; 200]),
+   | |     ------------ the second-largest variant contains at least 200 bytes
+LL | | }
+   | |_^ the entire enum is at least 1256 bytes
+   |
+help: consider boxing the large fields to reduce the total size of the enum
+   |
+LL |     B(Box<[u8; 1255]>),
+   |       ~~~~~~~~~~~~~~~
+
+error: large size difference between variants
+  --> $DIR/large_enum_variant.rs:73:1
+   |
+LL | / enum LargeEnum8 {
+LL | |     VariantOk(i32, u32),
+   | |     ------------------- the second-largest variant contains at least 8 bytes
+LL | |     ContainingMoreThanOneField([i32; 8000], [i32; 2], [i32; 9500], [i32; 30]),
+   | |     ------------------------------------------------------------------------- the largest variant contains at least 70128 bytes
+LL | | }
+   | |_^ the entire enum is at least 70132 bytes
+   |
+help: consider boxing the large fields to reduce the total size of the enum
+   |
+LL |     ContainingMoreThanOneField(Box<[i32; 8000]>, [i32; 2], Box<[i32; 9500]>, [i32; 30]),
+   |                                ~~~~~~~~~~~~~~~~            ~~~~~~~~~~~~~~~~
+
+error: large size difference between variants
+  --> $DIR/large_enum_variant.rs:78:1
+   |
+LL | / enum LargeEnum9 {
+LL | |     A(Struct<()>),
+   | |     ------------- the second-largest variant contains at least 4 bytes
+LL | |     B(Struct2),
+   | |     ---------- the largest variant contains at least 32000 bytes
+LL | | }
+   | |_^ the entire enum is at least 32004 bytes
+   |
+help: consider boxing the large fields to reduce the total size of the enum
+   |
+LL |     B(Box<Struct2>),
+   |       ~~~~~~~~~~~~
+
+error: large size difference between variants
+  --> $DIR/large_enum_variant.rs:83:1
+   |
+LL | / enum LargeEnumOk2<T> {
+LL | |     A(T),
+   | |     ---- the second-largest variant contains at least 0 bytes
+LL | |     B(Struct2),
+   | |     ---------- the largest variant contains at least 32000 bytes
+LL | | }
+   | |_^ the entire enum is at least 32000 bytes
+   |
+help: consider boxing the large fields to reduce the total size of the enum
+   |
+LL |     B(Box<Struct2>),
+   |       ~~~~~~~~~~~~
+
+error: large size difference between variants
+  --> $DIR/large_enum_variant.rs:88:1
+   |
+LL | / enum LargeEnumOk3<T> {
+LL | |     A(Struct<T>),
+   | |     ------------ the second-largest variant contains at least 4 bytes
+LL | |     B(Struct2),
+   | |     ---------- the largest variant contains at least 32000 bytes
+LL | | }
+   | |_^ the entire enum is at least 32000 bytes
+   |
+help: consider boxing the large fields to reduce the total size of the enum
+   |
+LL |     B(Box<Struct2>),
+   |       ~~~~~~~~~~~~
+
+error: large size difference between variants
+  --> $DIR/large_enum_variant.rs:103:1
+   |
+LL | / enum CopyableLargeEnum {
+LL | |     A(bool),
+   | |     ------- the second-largest variant contains at least 1 bytes
+LL | |     B([u64; 8000]),
+   | |     -------------- the largest variant contains at least 64000 bytes
+LL | | }
+   | |_^ the entire enum is at least 64004 bytes
+   |
+note: boxing a variant would require the type no longer be `Copy`
+  --> $DIR/large_enum_variant.rs:103:6
+   |
+LL | enum CopyableLargeEnum {
+   |      ^^^^^^^^^^^^^^^^^
+help: consider boxing the large fields to reduce the total size of the enum
+  --> $DIR/large_enum_variant.rs:105:5
+   |
+LL |     B([u64; 8000]),
+   |     ^^^^^^^^^^^^^^
+
+error: large size difference between variants
+  --> $DIR/large_enum_variant.rs:108:1
+   |
+LL | / enum ManuallyCopyLargeEnum {
+LL | |     A(bool),
+   | |     ------- the second-largest variant contains at least 1 bytes
+LL | |     B([u64; 8000]),
+   | |     -------------- the largest variant contains at least 64000 bytes
+LL | | }
+   | |_^ the entire enum is at least 64004 bytes
+   |
+note: boxing a variant would require the type no longer be `Copy`
+  --> $DIR/large_enum_variant.rs:108:6
+   |
+LL | enum ManuallyCopyLargeEnum {
+   |      ^^^^^^^^^^^^^^^^^^^^^
+help: consider boxing the large fields to reduce the total size of the enum
+  --> $DIR/large_enum_variant.rs:110:5
+   |
+LL |     B([u64; 8000]),
+   |     ^^^^^^^^^^^^^^
+
+error: large size difference between variants
+  --> $DIR/large_enum_variant.rs:121:1
+   |
+LL | / enum SomeGenericPossiblyCopyEnum<T> {
+LL | |     A(bool, std::marker::PhantomData<T>),
+   | |     ------------------------------------ the second-largest variant contains at least 1 bytes
+LL | |     B([u64; 4000]),
+   | |     -------------- the largest variant contains at least 32000 bytes
+LL | | }
+   | |_^ the entire enum is at least 32004 bytes
+   |
+note: boxing a variant would require the type no longer be `Copy`
+  --> $DIR/large_enum_variant.rs:121:6
+   |
+LL | enum SomeGenericPossiblyCopyEnum<T> {
+   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider boxing the large fields to reduce the total size of the enum
+  --> $DIR/large_enum_variant.rs:123:5
+   |
+LL |     B([u64; 4000]),
+   |     ^^^^^^^^^^^^^^
+
+error: large size difference between variants
+  --> $DIR/large_enum_variant.rs:134:1
+   |
+LL | / enum LargeEnumWithGenerics<T> {
+LL | |     Small,
+   | |     ----- the second-largest variant carries no data at all
+LL | |     Large((T, [u8; 512])),
+   | |     --------------------- the largest variant contains at least 512 bytes
+LL | | }
+   | |_^ the entire enum is at least 512 bytes
+   |
+help: consider boxing the large fields to reduce the total size of the enum
+   |
+LL |     Large(Box<(T, [u8; 512])>),
+   |           ~~~~~~~~~~~~~~~~~~~
+
+error: large size difference between variants
+  --> $DIR/large_enum_variant.rs:143:1
+   |
+LL | / enum WithGenerics {
+LL | |     Large([Foo<u64>; 64]),
+   | |     --------------------- the largest variant contains at least 512 bytes
+LL | |     Small(u8),
+   | |     --------- the second-largest variant contains at least 1 bytes
+LL | | }
+   | |_^ the entire enum is at least 516 bytes
+   |
+help: consider boxing the large fields to reduce the total size of the enum
+   |
+LL |     Large(Box<[Foo<u64>; 64]>),
+   |           ~~~~~~~~~~~~~~~~~~~
+
+error: large size difference between variants
+  --> $DIR/large_enum_variant.rs:153:1
+   |
+LL | / enum LargeEnumOfConst {
+LL | |     Ok,
+   | |     -- the second-largest variant carries no data at all
+LL | |     Error(PossiblyLargeEnumWithConst<256>),
+   | |     -------------------------------------- the largest variant contains at least 514 bytes
+LL | | }
+   | |_^ the entire enum is at least 514 bytes
+   |
+help: consider boxing the large fields to reduce the total size of the enum
+   |
+LL |     Error(Box<PossiblyLargeEnumWithConst<256>>),
+   |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 16 previous errors
+
diff --git a/src/tools/clippy/tests/ui/large_enum_variant.stderr b/src/tools/clippy/tests/ui/large_enum_variant.64bit.stderr
index 7026ca785f3..3eba43e05ec 100644
--- a/src/tools/clippy/tests/ui/large_enum_variant.stderr
+++ b/src/tools/clippy/tests/ui/large_enum_variant.64bit.stderr
@@ -1,5 +1,5 @@
 error: large size difference between variants
-  --> $DIR/large_enum_variant.rs:10:1
+  --> $DIR/large_enum_variant.rs:11:1
    |
 LL | / enum LargeEnum {
 LL | |     A(i32),
@@ -17,7 +17,7 @@ LL |     B(Box<[i32; 8000]>),
    |       ~~~~~~~~~~~~~~~~
 
 error: large size difference between variants
-  --> $DIR/large_enum_variant.rs:34:1
+  --> $DIR/large_enum_variant.rs:35:1
    |
 LL | / enum LargeEnum2 {
 LL | |     VariantOk(i32, u32),
@@ -33,7 +33,7 @@ LL |     ContainingLargeEnum(Box<LargeEnum>),
    |                         ~~~~~~~~~~~~~~
 
 error: large size difference between variants
-  --> $DIR/large_enum_variant.rs:39:1
+  --> $DIR/large_enum_variant.rs:40:1
    |
 LL | / enum LargeEnum3 {
 LL | |     ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]),
@@ -50,7 +50,7 @@ LL |     ContainingMoreThanOneField(i32, Box<[i32; 8000]>, Box<[i32; 9500]>),
    |                                     ~~~~~~~~~~~~~~~~  ~~~~~~~~~~~~~~~~
 
 error: large size difference between variants
-  --> $DIR/large_enum_variant.rs:45:1
+  --> $DIR/large_enum_variant.rs:46:1
    |
 LL | / enum LargeEnum4 {
 LL | |     VariantOk(i32, u32),
@@ -66,7 +66,7 @@ LL |     StructLikeLarge { x: Box<[i32; 8000]>, y: i32 },
    |                          ~~~~~~~~~~~~~~~~
 
 error: large size difference between variants
-  --> $DIR/large_enum_variant.rs:50:1
+  --> $DIR/large_enum_variant.rs:51:1
    |
 LL | / enum LargeEnum5 {
 LL | |     VariantOk(i32, u32),
@@ -82,7 +82,7 @@ LL |     StructLikeLarge2 { x: Box<[i32; 8000]> },
    |                           ~~~~~~~~~~~~~~~~
 
 error: large size difference between variants
-  --> $DIR/large_enum_variant.rs:66:1
+  --> $DIR/large_enum_variant.rs:67:1
    |
 LL | / enum LargeEnum7 {
 LL | |     A,
@@ -99,7 +99,7 @@ LL |     B(Box<[u8; 1255]>),
    |       ~~~~~~~~~~~~~~~
 
 error: large size difference between variants
-  --> $DIR/large_enum_variant.rs:72:1
+  --> $DIR/large_enum_variant.rs:73:1
    |
 LL | / enum LargeEnum8 {
 LL | |     VariantOk(i32, u32),
@@ -115,7 +115,7 @@ LL |     ContainingMoreThanOneField(Box<[i32; 8000]>, [i32; 2], Box<[i32; 9500]>
    |                                ~~~~~~~~~~~~~~~~            ~~~~~~~~~~~~~~~~
 
 error: large size difference between variants
-  --> $DIR/large_enum_variant.rs:77:1
+  --> $DIR/large_enum_variant.rs:78:1
    |
 LL | / enum LargeEnum9 {
 LL | |     A(Struct<()>),
@@ -131,7 +131,7 @@ LL |     B(Box<Struct2>),
    |       ~~~~~~~~~~~~
 
 error: large size difference between variants
-  --> $DIR/large_enum_variant.rs:82:1
+  --> $DIR/large_enum_variant.rs:83:1
    |
 LL | / enum LargeEnumOk2<T> {
 LL | |     A(T),
@@ -147,7 +147,7 @@ LL |     B(Box<Struct2>),
    |       ~~~~~~~~~~~~
 
 error: large size difference between variants
-  --> $DIR/large_enum_variant.rs:87:1
+  --> $DIR/large_enum_variant.rs:88:1
    |
 LL | / enum LargeEnumOk3<T> {
 LL | |     A(Struct<T>),
@@ -163,7 +163,7 @@ LL |     B(Box<Struct2>),
    |       ~~~~~~~~~~~~
 
 error: large size difference between variants
-  --> $DIR/large_enum_variant.rs:102:1
+  --> $DIR/large_enum_variant.rs:103:1
    |
 LL | / enum CopyableLargeEnum {
 LL | |     A(bool),
@@ -174,18 +174,18 @@ LL | | }
    | |_^ the entire enum is at least 64008 bytes
    |
 note: boxing a variant would require the type no longer be `Copy`
-  --> $DIR/large_enum_variant.rs:102:6
+  --> $DIR/large_enum_variant.rs:103:6
    |
 LL | enum CopyableLargeEnum {
    |      ^^^^^^^^^^^^^^^^^
 help: consider boxing the large fields to reduce the total size of the enum
-  --> $DIR/large_enum_variant.rs:104:5
+  --> $DIR/large_enum_variant.rs:105:5
    |
 LL |     B([u64; 8000]),
    |     ^^^^^^^^^^^^^^
 
 error: large size difference between variants
-  --> $DIR/large_enum_variant.rs:107:1
+  --> $DIR/large_enum_variant.rs:108:1
    |
 LL | / enum ManuallyCopyLargeEnum {
 LL | |     A(bool),
@@ -196,18 +196,18 @@ LL | | }
    | |_^ the entire enum is at least 64008 bytes
    |
 note: boxing a variant would require the type no longer be `Copy`
-  --> $DIR/large_enum_variant.rs:107:6
+  --> $DIR/large_enum_variant.rs:108:6
    |
 LL | enum ManuallyCopyLargeEnum {
    |      ^^^^^^^^^^^^^^^^^^^^^
 help: consider boxing the large fields to reduce the total size of the enum
-  --> $DIR/large_enum_variant.rs:109:5
+  --> $DIR/large_enum_variant.rs:110:5
    |
 LL |     B([u64; 8000]),
    |     ^^^^^^^^^^^^^^
 
 error: large size difference between variants
-  --> $DIR/large_enum_variant.rs:120:1
+  --> $DIR/large_enum_variant.rs:121:1
    |
 LL | / enum SomeGenericPossiblyCopyEnum<T> {
 LL | |     A(bool, std::marker::PhantomData<T>),
@@ -218,18 +218,18 @@ LL | | }
    | |_^ the entire enum is at least 32008 bytes
    |
 note: boxing a variant would require the type no longer be `Copy`
-  --> $DIR/large_enum_variant.rs:120:6
+  --> $DIR/large_enum_variant.rs:121:6
    |
 LL | enum SomeGenericPossiblyCopyEnum<T> {
    |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: consider boxing the large fields to reduce the total size of the enum
-  --> $DIR/large_enum_variant.rs:122:5
+  --> $DIR/large_enum_variant.rs:123:5
    |
 LL |     B([u64; 4000]),
    |     ^^^^^^^^^^^^^^
 
 error: large size difference between variants
-  --> $DIR/large_enum_variant.rs:133:1
+  --> $DIR/large_enum_variant.rs:134:1
    |
 LL | / enum LargeEnumWithGenerics<T> {
 LL | |     Small,
@@ -245,7 +245,7 @@ LL |     Large(Box<(T, [u8; 512])>),
    |           ~~~~~~~~~~~~~~~~~~~
 
 error: large size difference between variants
-  --> $DIR/large_enum_variant.rs:142:1
+  --> $DIR/large_enum_variant.rs:143:1
    |
 LL | / enum WithGenerics {
 LL | |     Large([Foo<u64>; 64]),
@@ -261,7 +261,7 @@ LL |     Large(Box<[Foo<u64>; 64]>),
    |           ~~~~~~~~~~~~~~~~~~~
 
 error: large size difference between variants
-  --> $DIR/large_enum_variant.rs:152:1
+  --> $DIR/large_enum_variant.rs:153:1
    |
 LL | / enum LargeEnumOfConst {
 LL | |     Ok,
diff --git a/src/tools/clippy/tests/ui/large_enum_variant.rs b/src/tools/clippy/tests/ui/large_enum_variant.rs
index f101bda76a8..3625c011dbf 100644
--- a/src/tools/clippy/tests/ui/large_enum_variant.rs
+++ b/src/tools/clippy/tests/ui/large_enum_variant.rs
@@ -1,3 +1,4 @@
+//@stderr-per-bitwidth
 //@aux-build:proc_macros.rs
 //@no-rustfix
 #![allow(dead_code)]
diff --git a/src/tools/clippy/tests/ui/manual_range_patterns.fixed b/src/tools/clippy/tests/ui/manual_range_patterns.fixed
index ea06e27d153..b348d7071f6 100644
--- a/src/tools/clippy/tests/ui/manual_range_patterns.fixed
+++ b/src/tools/clippy/tests/ui/manual_range_patterns.fixed
@@ -13,8 +13,8 @@ fn main() {
     let _ = matches!(f, 1 | 2147483647);
     let _ = matches!(f, 0 | 2147483647);
     let _ = matches!(f, -2147483647 | 2147483647);
-    let _ = matches!(f, 1 | (2..=4));
-    let _ = matches!(f, 1 | (2..4));
+    let _ = matches!(f, 1..=4);
+    let _ = matches!(f, 1..4);
     let _ = matches!(f, 1..=48324729);
     let _ = matches!(f, 0..=48324730);
     let _ = matches!(f, 0..=3);
@@ -25,9 +25,20 @@ fn main() {
     };
     let _ = matches!(f, -5..=3);
     let _ = matches!(f, -1 | -5 | 3 | -2 | -4 | -3 | 0 | 1); // 2 is missing
-    let _ = matches!(f, -1000001..=1000001);
+    let _ = matches!(f, -1_000_001..=1_000_001);
     let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_002);
 
+    matches!(f, 0x00..=0x03);
+    matches!(f, 0x00..=0x07);
+    matches!(f, -0x09..=0x00);
+
+    matches!(f, 0..=5);
+    matches!(f, 0..5);
+
+    matches!(f, 0..10);
+    matches!(f, 0..=10);
+    matches!(f, 0..=10);
+
     macro_rules! mac {
         ($e:expr) => {
             matches!($e, 1..=10)
diff --git a/src/tools/clippy/tests/ui/manual_range_patterns.rs b/src/tools/clippy/tests/ui/manual_range_patterns.rs
index eb29987b89f..a0750f54b73 100644
--- a/src/tools/clippy/tests/ui/manual_range_patterns.rs
+++ b/src/tools/clippy/tests/ui/manual_range_patterns.rs
@@ -28,6 +28,17 @@ fn main() {
     let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_001);
     let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_002);
 
+    matches!(f, 0x00 | 0x01 | 0x02 | 0x03);
+    matches!(f, 0x00..=0x05 | 0x06 | 0x07);
+    matches!(f, -0x09 | -0x08 | -0x07..=0x00);
+
+    matches!(f, 0..5 | 5);
+    matches!(f, 0 | 1..5);
+
+    matches!(f, 0..=5 | 6..10);
+    matches!(f, 0..5 | 5..=10);
+    matches!(f, 5..=10 | 0..5);
+
     macro_rules! mac {
         ($e:expr) => {
             matches!($e, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10)
diff --git a/src/tools/clippy/tests/ui/manual_range_patterns.stderr b/src/tools/clippy/tests/ui/manual_range_patterns.stderr
index 3eee39af86e..fbeb9455769 100644
--- a/src/tools/clippy/tests/ui/manual_range_patterns.stderr
+++ b/src/tools/clippy/tests/ui/manual_range_patterns.stderr
@@ -14,6 +14,18 @@ LL |     let _ = matches!(f, 4 | 2 | 3 | 1 | 5 | 6 | 9 | 7 | 8 | 10);
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10`
 
 error: this OR pattern can be rewritten using a range
+  --> $DIR/manual_range_patterns.rs:16:25
+   |
+LL |     let _ = matches!(f, 1 | (2..=4));
+   |                         ^^^^^^^^^^^ help: try: `1..=4`
+
+error: this OR pattern can be rewritten using a range
+  --> $DIR/manual_range_patterns.rs:17:25
+   |
+LL |     let _ = matches!(f, 1 | (2..4));
+   |                         ^^^^^^^^^^ help: try: `1..4`
+
+error: this OR pattern can be rewritten using a range
   --> $DIR/manual_range_patterns.rs:18:25
    |
 LL |     let _ = matches!(f, (1..=10) | (2..=13) | (14..=48324728) | 48324729);
@@ -47,10 +59,58 @@ error: this OR pattern can be rewritten using a range
   --> $DIR/manual_range_patterns.rs:28:25
    |
 LL |     let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_001);
-   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-1000001..=1000001`
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-1_000_001..=1_000_001`
+
+error: this OR pattern can be rewritten using a range
+  --> $DIR/manual_range_patterns.rs:31:17
+   |
+LL |     matches!(f, 0x00 | 0x01 | 0x02 | 0x03);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0x00..=0x03`
+
+error: this OR pattern can be rewritten using a range
+  --> $DIR/manual_range_patterns.rs:32:17
+   |
+LL |     matches!(f, 0x00..=0x05 | 0x06 | 0x07);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0x00..=0x07`
+
+error: this OR pattern can be rewritten using a range
+  --> $DIR/manual_range_patterns.rs:33:17
+   |
+LL |     matches!(f, -0x09 | -0x08 | -0x07..=0x00);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-0x09..=0x00`
+
+error: this OR pattern can be rewritten using a range
+  --> $DIR/manual_range_patterns.rs:35:17
+   |
+LL |     matches!(f, 0..5 | 5);
+   |                 ^^^^^^^^ help: try: `0..=5`
+
+error: this OR pattern can be rewritten using a range
+  --> $DIR/manual_range_patterns.rs:36:17
+   |
+LL |     matches!(f, 0 | 1..5);
+   |                 ^^^^^^^^ help: try: `0..5`
+
+error: this OR pattern can be rewritten using a range
+  --> $DIR/manual_range_patterns.rs:38:17
+   |
+LL |     matches!(f, 0..=5 | 6..10);
+   |                 ^^^^^^^^^^^^^ help: try: `0..10`
+
+error: this OR pattern can be rewritten using a range
+  --> $DIR/manual_range_patterns.rs:39:17
+   |
+LL |     matches!(f, 0..5 | 5..=10);
+   |                 ^^^^^^^^^^^^^ help: try: `0..=10`
+
+error: this OR pattern can be rewritten using a range
+  --> $DIR/manual_range_patterns.rs:40:17
+   |
+LL |     matches!(f, 5..=10 | 0..5);
+   |                 ^^^^^^^^^^^^^ help: try: `0..=10`
 
 error: this OR pattern can be rewritten using a range
-  --> $DIR/manual_range_patterns.rs:33:26
+  --> $DIR/manual_range_patterns.rs:44:26
    |
 LL |             matches!($e, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10)
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10`
@@ -60,5 +120,5 @@ LL |     mac!(f);
    |
    = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 9 previous errors
+error: aborting due to 19 previous errors
 
diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed
new file mode 100644
index 00000000000..a96827259f5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed
@@ -0,0 +1,121 @@
+#![allow(unused)]
+#![warn(clippy::missing_asserts_for_indexing)]
+
+// ok
+fn sum_with_assert(v: &[u8]) -> u8 {
+    assert!(v.len() > 4);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+}
+
+// ok
+fn sum_with_assert_other_way(v: &[u8]) -> u8 {
+    assert!(5 <= v.len());
+    v[0] + v[1] + v[2] + v[3] + v[4]
+}
+
+// ok
+fn sum_with_assert_ge(v: &[u8]) -> u8 {
+    assert!(v.len() >= 5);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+}
+
+// ok
+fn sum_with_assert_ge_other_way(v: &[u8]) -> u8 {
+    assert!(4 < v.len());
+    v[0] + v[1] + v[2] + v[3] + v[4]
+}
+
+fn sum_with_assert_lt(v: &[u8]) -> u8 {
+    assert!(v.len() > 4);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+}
+
+fn sum_with_assert_le(v: &[u8]) -> u8 {
+    assert!(v.len() > 4);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+}
+
+fn sum_with_incorrect_assert_len(v: &[u8]) -> u8 {
+    assert!(v.len() > 4);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+}
+
+fn sum_with_incorrect_assert_len2(v: &[u8]) -> u8 {
+    assert!(v.len() > 4);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+}
+
+// ok, don't lint for single array access
+fn single_access(v: &[u8]) -> u8 {
+    v[0]
+}
+
+// ok
+fn subslice_ok(v: &[u8]) {
+    assert!(v.len() > 3);
+    let _ = v[0];
+    let _ = v[1..4];
+}
+
+fn subslice_bad(v: &[u8]) {
+    assert!(v.len() > 3);
+    let _ = v[0];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+    let _ = v[1..4];
+}
+
+// ok
+fn subslice_inclusive_ok(v: &[u8]) {
+    assert!(v.len() > 4);
+    let _ = v[0];
+    let _ = v[1..=4];
+}
+
+fn subslice_inclusive_bad(v: &[u8]) {
+    assert!(v.len() > 4);
+    let _ = v[0];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+    let _ = v[1..=4];
+}
+
+fn index_different_slices_ok(v1: &[u8], v2: &[u8]) {
+    assert!(v1.len() > 12);
+    assert!(v2.len() > 15);
+    let _ = v1[0] + v1[12];
+    let _ = v2[5] + v2[15];
+}
+
+fn index_different_slices_wrong_len(v1: &[u8], v2: &[u8]) {
+    assert!(v1.len() > 12);
+    assert!(v2.len() > 15);
+    let _ = v1[0] + v1[12];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+    let _ = v2[5] + v2[15];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+}
+fn index_different_slices_one_wrong_len(v1: &[u8], v2: &[u8]) {
+    assert!(v1.len() > 12);
+    assert!(v2.len() > 15);
+    let _ = v1[0] + v1[12];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+    let _ = v2[5] + v2[15];
+}
+
+fn side_effect() -> &'static [u8] {
+    &[]
+}
+
+fn index_side_effect_expr() {
+    let _ = side_effect()[0] + side_effect()[1];
+}
+
+// ok, single access for different slices
+fn index_different_slice_in_same_expr(v1: &[u8], v2: &[u8]) {
+    let _ = v1[0] + v2[1];
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs
new file mode 100644
index 00000000000..0b4b883acf8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs
@@ -0,0 +1,121 @@
+#![allow(unused)]
+#![warn(clippy::missing_asserts_for_indexing)]
+
+// ok
+fn sum_with_assert(v: &[u8]) -> u8 {
+    assert!(v.len() > 4);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+}
+
+// ok
+fn sum_with_assert_other_way(v: &[u8]) -> u8 {
+    assert!(5 <= v.len());
+    v[0] + v[1] + v[2] + v[3] + v[4]
+}
+
+// ok
+fn sum_with_assert_ge(v: &[u8]) -> u8 {
+    assert!(v.len() >= 5);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+}
+
+// ok
+fn sum_with_assert_ge_other_way(v: &[u8]) -> u8 {
+    assert!(4 < v.len());
+    v[0] + v[1] + v[2] + v[3] + v[4]
+}
+
+fn sum_with_assert_lt(v: &[u8]) -> u8 {
+    assert!(v.len() < 5);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+}
+
+fn sum_with_assert_le(v: &[u8]) -> u8 {
+    assert!(v.len() <= 5);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+}
+
+fn sum_with_incorrect_assert_len(v: &[u8]) -> u8 {
+    assert!(v.len() > 3);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+}
+
+fn sum_with_incorrect_assert_len2(v: &[u8]) -> u8 {
+    assert!(v.len() >= 4);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+}
+
+// ok, don't lint for single array access
+fn single_access(v: &[u8]) -> u8 {
+    v[0]
+}
+
+// ok
+fn subslice_ok(v: &[u8]) {
+    assert!(v.len() > 3);
+    let _ = v[0];
+    let _ = v[1..4];
+}
+
+fn subslice_bad(v: &[u8]) {
+    assert!(v.len() >= 3);
+    let _ = v[0];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+    let _ = v[1..4];
+}
+
+// ok
+fn subslice_inclusive_ok(v: &[u8]) {
+    assert!(v.len() > 4);
+    let _ = v[0];
+    let _ = v[1..=4];
+}
+
+fn subslice_inclusive_bad(v: &[u8]) {
+    assert!(v.len() >= 4);
+    let _ = v[0];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+    let _ = v[1..=4];
+}
+
+fn index_different_slices_ok(v1: &[u8], v2: &[u8]) {
+    assert!(v1.len() > 12);
+    assert!(v2.len() > 15);
+    let _ = v1[0] + v1[12];
+    let _ = v2[5] + v2[15];
+}
+
+fn index_different_slices_wrong_len(v1: &[u8], v2: &[u8]) {
+    assert!(v1.len() >= 12);
+    assert!(v2.len() >= 15);
+    let _ = v1[0] + v1[12];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+    let _ = v2[5] + v2[15];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+}
+fn index_different_slices_one_wrong_len(v1: &[u8], v2: &[u8]) {
+    assert!(v1.len() >= 12);
+    assert!(v2.len() > 15);
+    let _ = v1[0] + v1[12];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+    let _ = v2[5] + v2[15];
+}
+
+fn side_effect() -> &'static [u8] {
+    &[]
+}
+
+fn index_side_effect_expr() {
+    let _ = side_effect()[0] + side_effect()[1];
+}
+
+// ok, single access for different slices
+fn index_different_slice_in_same_expr(v1: &[u8], v2: &[u8]) {
+    let _ = v1[0] + v2[1];
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr
new file mode 100644
index 00000000000..a3e66d7958e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr
@@ -0,0 +1,253 @@
+error: indexing into a slice multiple times with an `assert` that does not cover the highest index
+  --> $DIR/missing_asserts_for_indexing.rs:30:5
+   |
+LL |     assert!(v.len() < 5);
+   |     -------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)`
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:30:5
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |     ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:30:12
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |            ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:30:19
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                   ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:30:26
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                          ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:30:33
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                                 ^^^^
+   = note: asserting the length before indexing will elide bounds checks
+   = note: `-D clippy::missing-asserts-for-indexing` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::missing_asserts_for_indexing)]`
+
+error: indexing into a slice multiple times with an `assert` that does not cover the highest index
+  --> $DIR/missing_asserts_for_indexing.rs:36:5
+   |
+LL |     assert!(v.len() <= 5);
+   |     --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)`
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:36:5
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |     ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:36:12
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |            ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:36:19
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                   ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:36:26
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                          ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:36:33
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                                 ^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times with an `assert` that does not cover the highest index
+  --> $DIR/missing_asserts_for_indexing.rs:42:5
+   |
+LL |     assert!(v.len() > 3);
+   |     -------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)`
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:42:5
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |     ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:42:12
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |            ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:42:19
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                   ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:42:26
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                          ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:42:33
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                                 ^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times with an `assert` that does not cover the highest index
+  --> $DIR/missing_asserts_for_indexing.rs:48:5
+   |
+LL |     assert!(v.len() >= 4);
+   |     --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)`
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:48:5
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |     ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:48:12
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |            ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:48:19
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                   ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:48:26
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                          ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:48:33
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                                 ^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times with an `assert` that does not cover the highest index
+  --> $DIR/missing_asserts_for_indexing.rs:66:13
+   |
+LL |       assert!(v.len() >= 3);
+   |       --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 3)`
+LL |       let _ = v[0];
+   |  _____________^
+LL | |
+LL | |     let _ = v[1..4];
+   | |___________________^
+   |
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:66:13
+   |
+LL |     let _ = v[0];
+   |             ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:68:13
+   |
+LL |     let _ = v[1..4];
+   |             ^^^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times with an `assert` that does not cover the highest index
+  --> $DIR/missing_asserts_for_indexing.rs:80:13
+   |
+LL |       assert!(v.len() >= 4);
+   |       --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)`
+LL |       let _ = v[0];
+   |  _____________^
+LL | |
+LL | |     let _ = v[1..=4];
+   | |____________________^
+   |
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:80:13
+   |
+LL |     let _ = v[0];
+   |             ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:82:13
+   |
+LL |     let _ = v[1..=4];
+   |             ^^^^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times with an `assert` that does not cover the highest index
+  --> $DIR/missing_asserts_for_indexing.rs:95:13
+   |
+LL |     assert!(v1.len() >= 12);
+   |     ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() > 12)`
+LL |     assert!(v2.len() >= 15);
+LL |     let _ = v1[0] + v1[12];
+   |             ^^^^^^^^^^^^^^
+   |
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:95:13
+   |
+LL |     let _ = v1[0] + v1[12];
+   |             ^^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:95:21
+   |
+LL |     let _ = v1[0] + v1[12];
+   |                     ^^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times with an `assert` that does not cover the highest index
+  --> $DIR/missing_asserts_for_indexing.rs:97:13
+   |
+LL |     assert!(v2.len() >= 15);
+   |     ----------------------- help: provide the highest index that is indexed with: `assert!(v2.len() > 15)`
+...
+LL |     let _ = v2[5] + v2[15];
+   |             ^^^^^^^^^^^^^^
+   |
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:97:13
+   |
+LL |     let _ = v2[5] + v2[15];
+   |             ^^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:97:21
+   |
+LL |     let _ = v2[5] + v2[15];
+   |                     ^^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times with an `assert` that does not cover the highest index
+  --> $DIR/missing_asserts_for_indexing.rs:103:13
+   |
+LL |     assert!(v1.len() >= 12);
+   |     ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() > 12)`
+LL |     assert!(v2.len() > 15);
+LL |     let _ = v1[0] + v1[12];
+   |             ^^^^^^^^^^^^^^
+   |
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:103:13
+   |
+LL |     let _ = v1[0] + v1[12];
+   |             ^^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:103:21
+   |
+LL |     let _ = v1[0] + v1[12];
+   |                     ^^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs
new file mode 100644
index 00000000000..4346ed892f2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs
@@ -0,0 +1,49 @@
+#![allow(unused)]
+#![warn(clippy::missing_asserts_for_indexing)]
+
+fn sum(v: &[u8]) -> u8 {
+    v[0] + v[1] + v[2] + v[3] + v[4]
+    //~^ ERROR: indexing into a slice multiple times without an `assert`
+}
+
+fn subslice(v: &[u8]) {
+    let _ = v[0];
+    //~^ ERROR: indexing into a slice multiple times without an `assert`
+    let _ = v[1..4];
+}
+
+fn variables(v: &[u8]) -> u8 {
+    let a = v[0];
+    //~^ ERROR: indexing into a slice multiple times without an `assert`
+    let b = v[1];
+    let c = v[2];
+    a + b + c
+}
+
+fn index_different_slices(v1: &[u8], v2: &[u8]) {
+    let _ = v1[0] + v1[12];
+    let _ = v2[5] + v2[15];
+}
+
+fn index_different_slices2(v1: &[u8], v2: &[u8]) {
+    assert!(v1.len() > 12);
+    let _ = v1[0] + v1[12];
+    let _ = v2[5] + v2[15];
+}
+
+struct Foo<'a> {
+    v: &'a [u8],
+    v2: &'a [u8],
+}
+
+fn index_struct_field(f: &Foo<'_>) {
+    let _ = f.v[0] + f.v[1];
+    //~^ ERROR: indexing into a slice multiple times without an `assert`
+}
+
+fn index_struct_different_fields(f: &Foo<'_>) {
+    // ok, different fields
+    let _ = f.v[0] + f.v2[1];
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr
new file mode 100644
index 00000000000..12c9eed5d66
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr
@@ -0,0 +1,164 @@
+error: indexing into a slice multiple times without an `assert`
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:5
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider asserting the length before indexing: `assert!(v.len() > 4);`
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:5
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |     ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:12
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |            ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:19
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                   ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:26
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                          ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:33
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                                 ^^^^
+   = note: asserting the length before indexing will elide bounds checks
+   = note: `-D clippy::missing-asserts-for-indexing` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::missing_asserts_for_indexing)]`
+
+error: indexing into a slice multiple times without an `assert`
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:10:13
+   |
+LL |       let _ = v[0];
+   |  _____________^
+LL | |
+LL | |     let _ = v[1..4];
+   | |___________________^
+   |
+   = help: consider asserting the length before indexing: `assert!(v.len() > 3);`
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:10:13
+   |
+LL |     let _ = v[0];
+   |             ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:12:13
+   |
+LL |     let _ = v[1..4];
+   |             ^^^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times without an `assert`
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:16:13
+   |
+LL |       let a = v[0];
+   |  _____________^
+LL | |
+LL | |     let b = v[1];
+LL | |     let c = v[2];
+   | |________________^
+   |
+   = help: consider asserting the length before indexing: `assert!(v.len() > 2);`
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:16:13
+   |
+LL |     let a = v[0];
+   |             ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:18:13
+   |
+LL |     let b = v[1];
+   |             ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:19:13
+   |
+LL |     let c = v[2];
+   |             ^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times without an `assert`
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:24:13
+   |
+LL |     let _ = v1[0] + v1[12];
+   |             ^^^^^^^^^^^^^^
+   |
+   = help: consider asserting the length before indexing: `assert!(v1.len() > 12);`
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:24:13
+   |
+LL |     let _ = v1[0] + v1[12];
+   |             ^^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:24:21
+   |
+LL |     let _ = v1[0] + v1[12];
+   |                     ^^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times without an `assert`
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:25:13
+   |
+LL |     let _ = v2[5] + v2[15];
+   |             ^^^^^^^^^^^^^^
+   |
+   = help: consider asserting the length before indexing: `assert!(v2.len() > 15);`
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:25:13
+   |
+LL |     let _ = v2[5] + v2[15];
+   |             ^^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:25:21
+   |
+LL |     let _ = v2[5] + v2[15];
+   |                     ^^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times without an `assert`
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:31:13
+   |
+LL |     let _ = v2[5] + v2[15];
+   |             ^^^^^^^^^^^^^^
+   |
+   = help: consider asserting the length before indexing: `assert!(v2.len() > 15);`
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:31:13
+   |
+LL |     let _ = v2[5] + v2[15];
+   |             ^^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:31:21
+   |
+LL |     let _ = v2[5] + v2[15];
+   |                     ^^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times without an `assert`
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:40:13
+   |
+LL |     let _ = f.v[0] + f.v[1];
+   |             ^^^^^^^^^^^^^^^
+   |
+   = help: consider asserting the length before indexing: `assert!(f.v.len() > 1);`
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:40:13
+   |
+LL |     let _ = f.v[0] + f.v[1];
+   |             ^^^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:40:22
+   |
+LL |     let _ = f.v[0] + f.v[1];
+   |                      ^^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_borrow.fixed b/src/tools/clippy/tests/ui/needless_borrow.fixed
index ee67224b4a8..0a52b25229d 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.fixed
+++ b/src/tools/clippy/tests/ui/needless_borrow.fixed
@@ -503,3 +503,16 @@ mod issue_10535 {
     {
     }
 }
+
+mod issue_10253 {
+    struct S;
+    trait X {
+        fn f<T>(&self);
+    }
+    impl X for &S {
+        fn f<T>(&self) {}
+    }
+    fn f() {
+        (&S).f::<()>();
+    }
+}
diff --git a/src/tools/clippy/tests/ui/needless_borrow.rs b/src/tools/clippy/tests/ui/needless_borrow.rs
index 1444f47d920..34a95d18463 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.rs
+++ b/src/tools/clippy/tests/ui/needless_borrow.rs
@@ -503,3 +503,16 @@ mod issue_10535 {
     {
     }
 }
+
+mod issue_10253 {
+    struct S;
+    trait X {
+        fn f<T>(&self);
+    }
+    impl X for &S {
+        fn f<T>(&self) {}
+    }
+    fn f() {
+        (&S).f::<()>();
+    }
+}
diff --git a/src/tools/clippy/tests/ui/needless_doc_main.rs b/src/tools/clippy/tests/ui/needless_doc_main.rs
index d31b9047eaa..fee05926ce4 100644
--- a/src/tools/clippy/tests/ui/needless_doc_main.rs
+++ b/src/tools/clippy/tests/ui/needless_doc_main.rs
@@ -10,7 +10,7 @@
 ///     unimplemented!();
 /// }
 /// ```
-/// 
+///
 /// With an explicit return type it should lint too
 /// ```edition2015
 /// fn main() -> () {
@@ -18,7 +18,7 @@
 ///     unimplemented!();
 /// }
 /// ```
-/// 
+///
 /// This should, too.
 /// ```rust
 /// fn main() {
@@ -26,11 +26,12 @@
 ///     unimplemented!();
 /// }
 /// ```
-/// 
+///
 /// This one too.
 /// ```no_run
-/// fn main() {
+/// // the fn is not always the first line
 //~^ ERROR: needless `fn main` in doctest
+/// fn main() {
 ///     unimplemented!();
 /// }
 /// ```
diff --git a/src/tools/clippy/tests/ui/needless_doc_main.stderr b/src/tools/clippy/tests/ui/needless_doc_main.stderr
index 8dd93bb05dd..84256548671 100644
--- a/src/tools/clippy/tests/ui/needless_doc_main.stderr
+++ b/src/tools/clippy/tests/ui/needless_doc_main.stderr
@@ -1,29 +1,47 @@
 error: needless `fn main` in doctest
-  --> $DIR/needless_doc_main.rs:7:4
+  --> $DIR/needless_doc_main.rs:7:5
    |
-LL | /// fn main() {
-   |    ^^^^^^^^^^^^
+LL |   /// fn main() {
+   |  _____^
+LL | |
+LL | |
+LL | | ///     unimplemented!();
+LL | | /// }
+   | |_____^
    |
    = note: `-D clippy::needless-doctest-main` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::needless_doctest_main)]`
 
 error: needless `fn main` in doctest
-  --> $DIR/needless_doc_main.rs:16:4
+  --> $DIR/needless_doc_main.rs:16:5
    |
-LL | /// fn main() -> () {
-   |    ^^^^^^^^^^^^^^^^^^
+LL |   /// fn main() -> () {
+   |  _____^
+LL | |
+LL | | ///     unimplemented!();
+LL | | /// }
+   | |_____^
 
 error: needless `fn main` in doctest
-  --> $DIR/needless_doc_main.rs:24:4
+  --> $DIR/needless_doc_main.rs:24:5
    |
-LL | /// fn main() {
-   |    ^^^^^^^^^^^^
+LL |   /// fn main() {
+   |  _____^
+LL | |
+LL | | ///     unimplemented!();
+LL | | /// }
+   | |_____^
 
 error: needless `fn main` in doctest
-  --> $DIR/needless_doc_main.rs:32:4
+  --> $DIR/needless_doc_main.rs:32:5
    |
-LL | /// fn main() {
-   |    ^^^^^^^^^^^^
+LL |   /// // the fn is not always the first line
+   |  _____^
+LL | |
+LL | | /// fn main() {
+LL | | ///     unimplemented!();
+LL | | /// }
+   | |_____^
 
 error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_raw_string.fixed b/src/tools/clippy/tests/ui/needless_raw_string.fixed
index 4db375178b4..85549810513 100644
--- a/src/tools/clippy/tests/ui/needless_raw_string.fixed
+++ b/src/tools/clippy/tests/ui/needless_raw_string.fixed
@@ -9,8 +9,13 @@ fn main() {
     b"aaa";
     br#""aaa""#;
     br#"\s"#;
-    // currently disabled: https://github.com/rust-lang/rust/issues/113333
-    // cr#"aaa"#;
-    // cr#""aaa""#;
-    // cr#"\s"#;
+    c"aaa";
+    cr#""aaa""#;
+    cr#"\s"#;
+
+    "
+        a
+        multiline
+        string
+    ";
 }
diff --git a/src/tools/clippy/tests/ui/needless_raw_string.rs b/src/tools/clippy/tests/ui/needless_raw_string.rs
index 59c75fda41b..06d49730387 100644
--- a/src/tools/clippy/tests/ui/needless_raw_string.rs
+++ b/src/tools/clippy/tests/ui/needless_raw_string.rs
@@ -9,8 +9,13 @@ fn main() {
     br#"aaa"#;
     br#""aaa""#;
     br#"\s"#;
-    // currently disabled: https://github.com/rust-lang/rust/issues/113333
-    // cr#"aaa"#;
-    // cr#""aaa""#;
-    // cr#"\s"#;
+    cr#"aaa"#;
+    cr#""aaa""#;
+    cr#"\s"#;
+
+    r#"
+        a
+        multiline
+        string
+    "#;
 }
diff --git a/src/tools/clippy/tests/ui/needless_raw_string.stderr b/src/tools/clippy/tests/ui/needless_raw_string.stderr
index 83cc6d332ee..e6806b31b1d 100644
--- a/src/tools/clippy/tests/ui/needless_raw_string.stderr
+++ b/src/tools/clippy/tests/ui/needless_raw_string.stderr
@@ -2,16 +2,58 @@ error: unnecessary raw string literal
   --> $DIR/needless_raw_string.rs:6:5
    |
 LL |     r#"aaa"#;
-   |     ^^^^^^^^ help: try: `"aaa"`
+   |     ^^^^^^^^
    |
    = note: `-D clippy::needless-raw-strings` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::needless_raw_strings)]`
+help: try
+   |
+LL -     r#"aaa"#;
+LL +     "aaa";
+   |
 
 error: unnecessary raw string literal
   --> $DIR/needless_raw_string.rs:9:5
    |
 LL |     br#"aaa"#;
-   |     ^^^^^^^^^ help: try: `b"aaa"`
+   |     ^^^^^^^^^
+   |
+help: try
+   |
+LL -     br#"aaa"#;
+LL +     b"aaa";
+   |
+
+error: unnecessary raw string literal
+  --> $DIR/needless_raw_string.rs:12:5
+   |
+LL |     cr#"aaa"#;
+   |     ^^^^^^^^^
+   |
+help: try
+   |
+LL -     cr#"aaa"#;
+LL +     c"aaa";
+   |
+
+error: unnecessary raw string literal
+  --> $DIR/needless_raw_string.rs:16:5
+   |
+LL | /     r#"
+LL | |         a
+LL | |         multiline
+LL | |         string
+LL | |     "#;
+   | |______^
+   |
+help: try
+   |
+LL ~     "
+LL |         a
+LL |         multiline
+LL |         string
+LL ~     ";
+   |
 
-error: aborting due to 2 previous errors
+error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed b/src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed
index 84902157ab4..e980adeeff4 100644
--- a/src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed
+++ b/src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed
@@ -3,17 +3,22 @@
 #![feature(c_str_literals)]
 
 fn main() {
-    r#"aaa"#;
+    r"\aaa";
     r#"Hello "world"!"#;
     r####" "### "## "# "####;
     r###" "aa" "# "## "###;
-    br#"aaa"#;
+    br"\aaa";
     br#"Hello "world"!"#;
     br####" "### "## "# "####;
     br###" "aa" "# "## "###;
-    // currently disabled: https://github.com/rust-lang/rust/issues/113333
-    // cr#"aaa"#;
-    // cr##"Hello "world"!"##;
-    // cr######" "### "## "# "######;
-    // cr######" "aa" "# "## "######;
+    cr"\aaa";
+    cr#"Hello "world"!"#;
+    cr####" "### "## "# "####;
+    cr###" "aa" "# "## "###;
+
+    r"
+        \a
+        multiline
+        string
+    ";
 }
diff --git a/src/tools/clippy/tests/ui/needless_raw_string_hashes.rs b/src/tools/clippy/tests/ui/needless_raw_string_hashes.rs
index 62abae83859..6113c5f25ae 100644
--- a/src/tools/clippy/tests/ui/needless_raw_string_hashes.rs
+++ b/src/tools/clippy/tests/ui/needless_raw_string_hashes.rs
@@ -3,17 +3,22 @@
 #![feature(c_str_literals)]
 
 fn main() {
-    r#"aaa"#;
+    r#"\aaa"#;
     r##"Hello "world"!"##;
     r######" "### "## "# "######;
     r######" "aa" "# "## "######;
-    br#"aaa"#;
+    br#"\aaa"#;
     br##"Hello "world"!"##;
     br######" "### "## "# "######;
     br######" "aa" "# "## "######;
-    // currently disabled: https://github.com/rust-lang/rust/issues/113333
-    // cr#"aaa"#;
-    // cr##"Hello "world"!"##;
-    // cr######" "### "## "# "######;
-    // cr######" "aa" "# "## "######;
+    cr#"\aaa"#;
+    cr##"Hello "world"!"##;
+    cr######" "### "## "# "######;
+    cr######" "aa" "# "## "######;
+
+    r#"
+        \a
+        multiline
+        string
+    "#;
 }
diff --git a/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr b/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr
index 94c51c423b8..5a8e3d04543 100644
--- a/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr
+++ b/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr
@@ -1,41 +1,167 @@
 error: unnecessary hashes around raw string literal
-  --> $DIR/needless_raw_string_hashes.rs:7:5
+  --> $DIR/needless_raw_string_hashes.rs:6:5
    |
-LL |     r##"Hello "world"!"##;
-   |     ^^^^^^^^^^^^^^^^^^^^^ help: try: `r#"Hello "world"!"#`
+LL |     r#"\aaa"#;
+   |     ^^^^^^^^^
    |
    = note: `-D clippy::needless-raw-string-hashes` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::needless_raw_string_hashes)]`
+help: remove all the hashes around the literal
+   |
+LL -     r#"\aaa"#;
+LL +     r"\aaa";
+   |
+
+error: unnecessary hashes around raw string literal
+  --> $DIR/needless_raw_string_hashes.rs:7:5
+   |
+LL |     r##"Hello "world"!"##;
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove one hash from both sides of the literal
+   |
+LL -     r##"Hello "world"!"##;
+LL +     r#"Hello "world"!"#;
+   |
 
 error: unnecessary hashes around raw string literal
   --> $DIR/needless_raw_string_hashes.rs:8:5
    |
 LL |     r######" "### "## "# "######;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `r####" "### "## "# "####`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove 2 hashes from both sides of the literal
+   |
+LL -     r######" "### "## "# "######;
+LL +     r####" "### "## "# "####;
+   |
 
 error: unnecessary hashes around raw string literal
   --> $DIR/needless_raw_string_hashes.rs:9:5
    |
 LL |     r######" "aa" "# "## "######;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `r###" "aa" "# "## "###`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove 3 hashes from both sides of the literal
+   |
+LL -     r######" "aa" "# "## "######;
+LL +     r###" "aa" "# "## "###;
+   |
+
+error: unnecessary hashes around raw string literal
+  --> $DIR/needless_raw_string_hashes.rs:10:5
+   |
+LL |     br#"\aaa"#;
+   |     ^^^^^^^^^^
+   |
+help: remove all the hashes around the literal
+   |
+LL -     br#"\aaa"#;
+LL +     br"\aaa";
+   |
 
 error: unnecessary hashes around raw string literal
   --> $DIR/needless_raw_string_hashes.rs:11:5
    |
 LL |     br##"Hello "world"!"##;
-   |     ^^^^^^^^^^^^^^^^^^^^^^ help: try: `br#"Hello "world"!"#`
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove one hash from both sides of the literal
+   |
+LL -     br##"Hello "world"!"##;
+LL +     br#"Hello "world"!"#;
+   |
 
 error: unnecessary hashes around raw string literal
   --> $DIR/needless_raw_string_hashes.rs:12:5
    |
 LL |     br######" "### "## "# "######;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `br####" "### "## "# "####`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove 2 hashes from both sides of the literal
+   |
+LL -     br######" "### "## "# "######;
+LL +     br####" "### "## "# "####;
+   |
 
 error: unnecessary hashes around raw string literal
   --> $DIR/needless_raw_string_hashes.rs:13:5
    |
 LL |     br######" "aa" "# "## "######;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `br###" "aa" "# "## "###`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove 3 hashes from both sides of the literal
+   |
+LL -     br######" "aa" "# "## "######;
+LL +     br###" "aa" "# "## "###;
+   |
+
+error: unnecessary hashes around raw string literal
+  --> $DIR/needless_raw_string_hashes.rs:14:5
+   |
+LL |     cr#"\aaa"#;
+   |     ^^^^^^^^^^
+   |
+help: remove all the hashes around the literal
+   |
+LL -     cr#"\aaa"#;
+LL +     cr"\aaa";
+   |
+
+error: unnecessary hashes around raw string literal
+  --> $DIR/needless_raw_string_hashes.rs:15:5
+   |
+LL |     cr##"Hello "world"!"##;
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove one hash from both sides of the literal
+   |
+LL -     cr##"Hello "world"!"##;
+LL +     cr#"Hello "world"!"#;
+   |
+
+error: unnecessary hashes around raw string literal
+  --> $DIR/needless_raw_string_hashes.rs:16:5
+   |
+LL |     cr######" "### "## "# "######;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove 2 hashes from both sides of the literal
+   |
+LL -     cr######" "### "## "# "######;
+LL +     cr####" "### "## "# "####;
+   |
+
+error: unnecessary hashes around raw string literal
+  --> $DIR/needless_raw_string_hashes.rs:17:5
+   |
+LL |     cr######" "aa" "# "## "######;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove 3 hashes from both sides of the literal
+   |
+LL -     cr######" "aa" "# "## "######;
+LL +     cr###" "aa" "# "## "###;
+   |
+
+error: unnecessary hashes around raw string literal
+  --> $DIR/needless_raw_string_hashes.rs:19:5
+   |
+LL | /     r#"
+LL | |         \a
+LL | |         multiline
+LL | |         string
+LL | |     "#;
+   | |______^
+   |
+help: remove all the hashes around the literal
+   |
+LL ~     r"
+LL |         \a
+LL |         multiline
+LL |         string
+LL ~     ";
+   |
 
-error: aborting due to 6 previous errors
+error: aborting due to 13 previous errors
 
diff --git a/src/tools/clippy/tests/ui/never_loop.rs b/src/tools/clippy/tests/ui/never_loop.rs
index 001b94391ca..c67a6d4494e 100644
--- a/src/tools/clippy/tests/ui/never_loop.rs
+++ b/src/tools/clippy/tests/ui/never_loop.rs
@@ -337,10 +337,8 @@ pub fn test26() {
 
 pub fn test27() {
     loop {
-        //~^ ERROR: this loop never actually loops
         'label: {
             let x = true;
-            // Lints because we cannot prove it's always `true`
             if x {
                 break 'label;
             }
@@ -349,6 +347,59 @@ pub fn test27() {
     }
 }
 
+// issue 11004
+pub fn test29() {
+    loop {
+        'label: {
+            if true {
+                break 'label;
+            }
+            return;
+        }
+    }
+}
+
+pub fn test30() {
+    'a: loop {
+        'b: {
+            for j in 0..2 {
+                if j == 1 {
+                    break 'b;
+                }
+            }
+            break 'a;
+        }
+    }
+}
+
+pub fn test31(b: bool) {
+    'a: loop {
+        'b: {
+            'c: loop {
+                //~^ ERROR: this loop never actually loops
+                if b { break 'c } else { break 'b }
+            }
+            continue 'a;
+        }
+        break 'a;
+    }
+}
+
+pub fn test32() {
+    loop {
+        //~^ ERROR: this loop never actually loops
+        panic!("oh no");
+    }
+    loop {
+        //~^ ERROR: this loop never actually loops
+        unimplemented!("not yet");
+    }
+    loop {
+        // no error
+        todo!("maybe later");
+    }
+}
+
 fn main() {
     test1();
     test2();
diff --git a/src/tools/clippy/tests/ui/never_loop.stderr b/src/tools/clippy/tests/ui/never_loop.stderr
index 234780007b1..3982f36cea9 100644
--- a/src/tools/clippy/tests/ui/never_loop.stderr
+++ b/src/tools/clippy/tests/ui/never_loop.stderr
@@ -154,16 +154,31 @@ LL |             if let Some(_) = (0..20).next() {
    |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: this loop never actually loops
-  --> $DIR/never_loop.rs:339:5
+  --> $DIR/never_loop.rs:378:13
+   |
+LL | /             'c: loop {
+LL | |
+LL | |                 if b { break 'c } else { break 'b }
+LL | |             }
+   | |_____________^
+
+error: this loop never actually loops
+  --> $DIR/never_loop.rs:389:5
    |
 LL | /     loop {
 LL | |
-LL | |         'label: {
-LL | |             let x = true;
-...  |
-LL | |         }
+LL | |         panic!("oh no");
+LL | |     }
+   | |_____^
+
+error: this loop never actually loops
+  --> $DIR/never_loop.rs:393:5
+   |
+LL | /     loop {
+LL | |
+LL | |         unimplemented!("not yet");
 LL | |     }
    | |_____^
 
-error: aborting due to 14 previous errors
+error: aborting due to 16 previous errors
 
diff --git a/src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.fixed b/src/tools/clippy/tests/ui/non_canonical_clone_impl.fixed
index 165702b3041..165702b3041 100644
--- a/src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.fixed
+++ b/src/tools/clippy/tests/ui/non_canonical_clone_impl.fixed
diff --git a/src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.rs b/src/tools/clippy/tests/ui/non_canonical_clone_impl.rs
index 3b07dd5ce62..3b07dd5ce62 100644
--- a/src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.rs
+++ b/src/tools/clippy/tests/ui/non_canonical_clone_impl.rs
diff --git a/src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.stderr b/src/tools/clippy/tests/ui/non_canonical_clone_impl.stderr
index 566a1a4b14b..44196751b05 100644
--- a/src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.stderr
+++ b/src/tools/clippy/tests/ui/non_canonical_clone_impl.stderr
@@ -1,5 +1,5 @@
-error: incorrect implementation of `clone` on a `Copy` type
-  --> $DIR/incorrect_clone_impl_on_copy_type.rs:9:29
+error: non-canonical implementation of `clone` on a `Copy` type
+  --> $DIR/non_canonical_clone_impl.rs:9:29
    |
 LL |       fn clone(&self) -> Self {
    |  _____________________________^
@@ -7,10 +7,11 @@ LL | |         Self(self.0)
 LL | |     }
    | |_____^ help: change this to: `{ *self }`
    |
-   = note: `#[deny(clippy::incorrect_clone_impl_on_copy_type)]` on by default
+   = note: `-D clippy::non-canonical-clone-impl` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::non_canonical_clone_impl)]`
 
-error: incorrect implementation of `clone_from` on a `Copy` type
-  --> $DIR/incorrect_clone_impl_on_copy_type.rs:13:5
+error: unnecessary implementation of `clone_from` on a `Copy` type
+  --> $DIR/non_canonical_clone_impl.rs:13:5
    |
 LL | /     fn clone_from(&mut self, source: &Self) {
 LL | |         source.clone();
@@ -18,8 +19,8 @@ LL | |         *self = source.clone();
 LL | |     }
    | |_____^ help: remove it
 
-error: incorrect implementation of `clone` on a `Copy` type
-  --> $DIR/incorrect_clone_impl_on_copy_type.rs:80:29
+error: non-canonical implementation of `clone` on a `Copy` type
+  --> $DIR/non_canonical_clone_impl.rs:80:29
    |
 LL |       fn clone(&self) -> Self {
    |  _____________________________^
@@ -27,8 +28,8 @@ LL | |         Self(self.0)
 LL | |     }
    | |_____^ help: change this to: `{ *self }`
 
-error: incorrect implementation of `clone_from` on a `Copy` type
-  --> $DIR/incorrect_clone_impl_on_copy_type.rs:84:5
+error: unnecessary implementation of `clone_from` on a `Copy` type
+  --> $DIR/non_canonical_clone_impl.rs:84:5
    |
 LL | /     fn clone_from(&mut self, source: &Self) {
 LL | |         source.clone();
diff --git a/src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type.fixed b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed
index db55cc094e3..db55cc094e3 100644
--- a/src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type.fixed
+++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed
diff --git a/src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type.rs b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs
index 52f4b85b917..52f4b85b917 100644
--- a/src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type.rs
+++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs
diff --git a/src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type.stderr b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr
index 1f706984662..05cc717b9ba 100644
--- a/src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type.stderr
+++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr
@@ -1,5 +1,5 @@
-error: incorrect implementation of `partial_cmp` on an `Ord` type
-  --> $DIR/incorrect_partial_ord_impl_on_ord_type.rs:16:1
+error: non-canonical implementation of `partial_cmp` on an `Ord` type
+  --> $DIR/non_canonical_partial_ord_impl.rs:16:1
    |
 LL | /  impl PartialOrd for A {
 LL | |      fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
@@ -10,10 +10,11 @@ LL | ||     }
 LL | |  }
    | |__^
    |
-   = note: `#[deny(clippy::incorrect_partial_ord_impl_on_ord_type)]` on by default
+   = note: `-D clippy::non-canonical-partial-ord-impl` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::non_canonical_partial_ord_impl)]`
 
-error: incorrect implementation of `partial_cmp` on an `Ord` type
-  --> $DIR/incorrect_partial_ord_impl_on_ord_type.rs:50:1
+error: non-canonical implementation of `partial_cmp` on an `Ord` type
+  --> $DIR/non_canonical_partial_ord_impl.rs:50:1
    |
 LL | / impl PartialOrd for C {
 LL | |     fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
diff --git a/src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl_fully_qual.rs
index 1173a95d065..2f8d5cf30c7 100644
--- a/src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs
+++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl_fully_qual.rs
@@ -21,8 +21,6 @@ impl cmp::Ord for A {
 }
 
 impl PartialOrd for A {
-    //~^ ERROR: incorrect implementation of `partial_cmp` on an `Ord` type
-    //~| NOTE: `#[deny(clippy::incorrect_partial_ord_impl_on_ord_type)]` on by default
     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
         // NOTE: This suggestion is wrong, as `Ord` is not in scope. But this should be fine as it isn't
         // automatically applied
@@ -46,7 +44,6 @@ impl cmp::Ord for B {
 }
 
 impl PartialOrd for B {
-    //~^ ERROR: incorrect implementation of `partial_cmp` on an `Ord` type
     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
         // This calls `B.cmp`, not `Ord::cmp`!
         Some(self.cmp(other))
diff --git a/src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.stderr b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl_fully_qual.stderr
index 09d7a32e334..4978d7a8739 100644
--- a/src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.stderr
+++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl_fully_qual.stderr
@@ -1,9 +1,7 @@
-error: incorrect implementation of `partial_cmp` on an `Ord` type
-  --> $DIR/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs:23:1
+error: non-canonical implementation of `partial_cmp` on an `Ord` type
+  --> $DIR/non_canonical_partial_ord_impl_fully_qual.rs:23:1
    |
 LL | /  impl PartialOrd for A {
-LL | |
-LL | |
 LL | |      fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
    | | _____________________________________________________________-
 LL | ||         // NOTE: This suggestion is wrong, as `Ord` is not in scope. But this should be fine as it isn't
@@ -14,13 +12,13 @@ LL | ||     }
 LL | |  }
    | |__^
    |
-   = note: `#[deny(clippy::incorrect_partial_ord_impl_on_ord_type)]` on by default
+   = note: `-D clippy::non-canonical-partial-ord-impl` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::non_canonical_partial_ord_impl)]`
 
-error: incorrect implementation of `partial_cmp` on an `Ord` type
-  --> $DIR/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs:48:1
+error: non-canonical implementation of `partial_cmp` on an `Ord` type
+  --> $DIR/non_canonical_partial_ord_impl_fully_qual.rs:46:1
    |
 LL | /  impl PartialOrd for B {
-LL | |
 LL | |      fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
    | | _____________________________________________________________-
 LL | ||         // This calls `B.cmp`, not `Ord::cmp`!
diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed
index 0fd68eb92b1..4df9be2c21d 100644
--- a/src/tools/clippy/tests/ui/rename.fixed
+++ b/src/tools/clippy/tests/ui/rename.fixed
@@ -14,6 +14,8 @@
 #![allow(clippy::mixed_read_write_in_expression)]
 #![allow(clippy::useless_conversion)]
 #![allow(clippy::match_result_ok)]
+#![allow(clippy::non_canonical_clone_impl)]
+#![allow(clippy::non_canonical_partial_ord_impl)]
 #![allow(clippy::arithmetic_side_effects)]
 #![allow(clippy::overly_complex_bool_expr)]
 #![allow(clippy::new_without_default)]
@@ -33,10 +35,10 @@
 #![allow(drop_bounds)]
 #![allow(dropping_copy_types)]
 #![allow(dropping_references)]
+#![allow(useless_ptr_null_checks)]
 #![allow(for_loops_over_fallibles)]
 #![allow(forgetting_copy_types)]
 #![allow(forgetting_references)]
-#![allow(useless_ptr_null_checks)]
 #![allow(array_into_iter)]
 #![allow(invalid_atomic_ordering)]
 #![allow(invalid_value)]
@@ -62,6 +64,8 @@
 #![warn(clippy::mixed_read_write_in_expression)]
 #![warn(clippy::useless_conversion)]
 #![warn(clippy::match_result_ok)]
+#![warn(clippy::non_canonical_clone_impl)]
+#![warn(clippy::non_canonical_partial_ord_impl)]
 #![warn(clippy::arithmetic_side_effects)]
 #![warn(clippy::overly_complex_bool_expr)]
 #![warn(clippy::new_without_default)]
@@ -85,12 +89,12 @@
 #![warn(drop_bounds)]
 #![warn(dropping_copy_types)]
 #![warn(dropping_references)]
+#![warn(useless_ptr_null_checks)]
 #![warn(for_loops_over_fallibles)]
 #![warn(for_loops_over_fallibles)]
 #![warn(for_loops_over_fallibles)]
 #![warn(forgetting_copy_types)]
 #![warn(forgetting_references)]
-#![warn(useless_ptr_null_checks)]
 #![warn(array_into_iter)]
 #![warn(invalid_atomic_ordering)]
 #![warn(invalid_value)]
diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs
index 927937fba83..940e60068e7 100644
--- a/src/tools/clippy/tests/ui/rename.rs
+++ b/src/tools/clippy/tests/ui/rename.rs
@@ -14,6 +14,8 @@
 #![allow(clippy::mixed_read_write_in_expression)]
 #![allow(clippy::useless_conversion)]
 #![allow(clippy::match_result_ok)]
+#![allow(clippy::non_canonical_clone_impl)]
+#![allow(clippy::non_canonical_partial_ord_impl)]
 #![allow(clippy::arithmetic_side_effects)]
 #![allow(clippy::overly_complex_bool_expr)]
 #![allow(clippy::new_without_default)]
@@ -33,10 +35,10 @@
 #![allow(drop_bounds)]
 #![allow(dropping_copy_types)]
 #![allow(dropping_references)]
+#![allow(useless_ptr_null_checks)]
 #![allow(for_loops_over_fallibles)]
 #![allow(forgetting_copy_types)]
 #![allow(forgetting_references)]
-#![allow(useless_ptr_null_checks)]
 #![allow(array_into_iter)]
 #![allow(invalid_atomic_ordering)]
 #![allow(invalid_value)]
@@ -62,6 +64,8 @@
 #![warn(clippy::eval_order_dependence)]
 #![warn(clippy::identity_conversion)]
 #![warn(clippy::if_let_some_result)]
+#![warn(clippy::incorrect_clone_impl_on_copy_type)]
+#![warn(clippy::incorrect_partial_ord_impl_on_ord_type)]
 #![warn(clippy::integer_arithmetic)]
 #![warn(clippy::logic_bug)]
 #![warn(clippy::new_without_default_derive)]
@@ -85,12 +89,12 @@
 #![warn(clippy::drop_bounds)]
 #![warn(clippy::drop_copy)]
 #![warn(clippy::drop_ref)]
+#![warn(clippy::fn_null_check)]
 #![warn(clippy::for_loop_over_option)]
 #![warn(clippy::for_loop_over_result)]
 #![warn(clippy::for_loops_over_fallibles)]
 #![warn(clippy::forget_copy)]
 #![warn(clippy::forget_ref)]
-#![warn(clippy::fn_null_check)]
 #![warn(clippy::into_iter_on_array)]
 #![warn(clippy::invalid_atomic_ordering)]
 #![warn(clippy::invalid_ref)]
diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr
index d98fc7b30db..30824e154b8 100644
--- a/src/tools/clippy/tests/ui/rename.stderr
+++ b/src/tools/clippy/tests/ui/rename.stderr
@@ -1,5 +1,5 @@
 error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range`
-  --> $DIR/rename.rs:52:9
+  --> $DIR/rename.rs:54:9
    |
 LL | #![warn(clippy::almost_complete_letter_range)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range`
@@ -8,322 +8,334 @@ LL | #![warn(clippy::almost_complete_letter_range)]
    = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]`
 
 error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names`
-  --> $DIR/rename.rs:53:9
+  --> $DIR/rename.rs:55:9
    |
 LL | #![warn(clippy::blacklisted_name)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names`
 
 error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
-  --> $DIR/rename.rs:54:9
+  --> $DIR/rename.rs:56:9
    |
 LL | #![warn(clippy::block_in_if_condition_expr)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
 
 error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
-  --> $DIR/rename.rs:55:9
+  --> $DIR/rename.rs:57:9
    |
 LL | #![warn(clippy::block_in_if_condition_stmt)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
 
 error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
-  --> $DIR/rename.rs:56:9
+  --> $DIR/rename.rs:58:9
    |
 LL | #![warn(clippy::box_vec)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
 
 error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
-  --> $DIR/rename.rs:57:9
+  --> $DIR/rename.rs:59:9
    |
 LL | #![warn(clippy::const_static_lifetime)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
 
 error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
-  --> $DIR/rename.rs:58:9
+  --> $DIR/rename.rs:60:9
    |
 LL | #![warn(clippy::cyclomatic_complexity)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
 
 error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq`
-  --> $DIR/rename.rs:59:9
+  --> $DIR/rename.rs:61:9
    |
 LL | #![warn(clippy::derive_hash_xor_eq)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq`
 
 error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
-  --> $DIR/rename.rs:60:9
+  --> $DIR/rename.rs:62:9
    |
 LL | #![warn(clippy::disallowed_method)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
 
 error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
-  --> $DIR/rename.rs:61:9
+  --> $DIR/rename.rs:63:9
    |
 LL | #![warn(clippy::disallowed_type)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
 
 error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression`
-  --> $DIR/rename.rs:62:9
+  --> $DIR/rename.rs:64:9
    |
 LL | #![warn(clippy::eval_order_dependence)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression`
 
 error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
-  --> $DIR/rename.rs:63:9
+  --> $DIR/rename.rs:65:9
    |
 LL | #![warn(clippy::identity_conversion)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
 
 error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
-  --> $DIR/rename.rs:64:9
+  --> $DIR/rename.rs:66:9
    |
 LL | #![warn(clippy::if_let_some_result)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
 
+error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl`
+  --> $DIR/rename.rs:67:9
+   |
+LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl`
+
+error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl`
+  --> $DIR/rename.rs:68:9
+   |
+LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl`
+
 error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects`
-  --> $DIR/rename.rs:65:9
+  --> $DIR/rename.rs:69:9
    |
 LL | #![warn(clippy::integer_arithmetic)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects`
 
 error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr`
-  --> $DIR/rename.rs:66:9
+  --> $DIR/rename.rs:70:9
    |
 LL | #![warn(clippy::logic_bug)]
    |         ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr`
 
 error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
-  --> $DIR/rename.rs:67:9
+  --> $DIR/rename.rs:71:9
    |
 LL | #![warn(clippy::new_without_default_derive)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
 
 error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
-  --> $DIR/rename.rs:68:9
+  --> $DIR/rename.rs:72:9
    |
 LL | #![warn(clippy::option_and_then_some)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
 
 error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
-  --> $DIR/rename.rs:69:9
+  --> $DIR/rename.rs:73:9
    |
 LL | #![warn(clippy::option_expect_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 
 error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
-  --> $DIR/rename.rs:70:9
+  --> $DIR/rename.rs:74:9
    |
 LL | #![warn(clippy::option_map_unwrap_or)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-  --> $DIR/rename.rs:71:9
+  --> $DIR/rename.rs:75:9
    |
 LL | #![warn(clippy::option_map_unwrap_or_else)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
-  --> $DIR/rename.rs:72:9
+  --> $DIR/rename.rs:76:9
    |
 LL | #![warn(clippy::option_unwrap_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 
 error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
-  --> $DIR/rename.rs:73:9
+  --> $DIR/rename.rs:77:9
    |
 LL | #![warn(clippy::ref_in_deref)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
 
 error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
-  --> $DIR/rename.rs:74:9
+  --> $DIR/rename.rs:78:9
    |
 LL | #![warn(clippy::result_expect_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 
 error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-  --> $DIR/rename.rs:75:9
+  --> $DIR/rename.rs:79:9
    |
 LL | #![warn(clippy::result_map_unwrap_or_else)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
-  --> $DIR/rename.rs:76:9
+  --> $DIR/rename.rs:80:9
    |
 LL | #![warn(clippy::result_unwrap_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 
 error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
-  --> $DIR/rename.rs:77:9
+  --> $DIR/rename.rs:81:9
    |
 LL | #![warn(clippy::single_char_push_str)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
 
 error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
-  --> $DIR/rename.rs:78:9
+  --> $DIR/rename.rs:82:9
    |
 LL | #![warn(clippy::stutter)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
 
 error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl`
-  --> $DIR/rename.rs:79:9
+  --> $DIR/rename.rs:83:9
    |
 LL | #![warn(clippy::to_string_in_display)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
 
 error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default`
-  --> $DIR/rename.rs:80:9
+  --> $DIR/rename.rs:84:9
    |
 LL | #![warn(clippy::unwrap_or_else_default)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default`
 
 error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
-  --> $DIR/rename.rs:81:9
+  --> $DIR/rename.rs:85:9
    |
 LL | #![warn(clippy::zero_width_space)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
 
 error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting`
-  --> $DIR/rename.rs:82:9
+  --> $DIR/rename.rs:86:9
    |
 LL | #![warn(clippy::cast_ref_to_mut)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting`
 
 error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op`
-  --> $DIR/rename.rs:83:9
+  --> $DIR/rename.rs:87:9
    |
 LL | #![warn(clippy::clone_double_ref)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op`
 
 error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons`
-  --> $DIR/rename.rs:84:9
+  --> $DIR/rename.rs:88:9
    |
 LL | #![warn(clippy::cmp_nan)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons`
 
 error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
-  --> $DIR/rename.rs:85:9
+  --> $DIR/rename.rs:89:9
    |
 LL | #![warn(clippy::drop_bounds)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
 
 error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types`
-  --> $DIR/rename.rs:86:9
+  --> $DIR/rename.rs:90:9
    |
 LL | #![warn(clippy::drop_copy)]
    |         ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types`
 
 error: lint `clippy::drop_ref` has been renamed to `dropping_references`
-  --> $DIR/rename.rs:87:9
+  --> $DIR/rename.rs:91:9
    |
 LL | #![warn(clippy::drop_ref)]
    |         ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references`
 
+error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks`
+  --> $DIR/rename.rs:92:9
+   |
+LL | #![warn(clippy::fn_null_check)]
+   |         ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks`
+
 error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles`
-  --> $DIR/rename.rs:88:9
+  --> $DIR/rename.rs:93:9
    |
 LL | #![warn(clippy::for_loop_over_option)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
 
 error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles`
-  --> $DIR/rename.rs:89:9
+  --> $DIR/rename.rs:94:9
    |
 LL | #![warn(clippy::for_loop_over_result)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
 
 error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles`
-  --> $DIR/rename.rs:90:9
+  --> $DIR/rename.rs:95:9
    |
 LL | #![warn(clippy::for_loops_over_fallibles)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
 
 error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types`
-  --> $DIR/rename.rs:91:9
+  --> $DIR/rename.rs:96:9
    |
 LL | #![warn(clippy::forget_copy)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types`
 
 error: lint `clippy::forget_ref` has been renamed to `forgetting_references`
-  --> $DIR/rename.rs:92:9
+  --> $DIR/rename.rs:97:9
    |
 LL | #![warn(clippy::forget_ref)]
    |         ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references`
 
-error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks`
-  --> $DIR/rename.rs:93:9
-   |
-LL | #![warn(clippy::fn_null_check)]
-   |         ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks`
-
 error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
-  --> $DIR/rename.rs:94:9
+  --> $DIR/rename.rs:98:9
    |
 LL | #![warn(clippy::into_iter_on_array)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
 
 error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
-  --> $DIR/rename.rs:95:9
+  --> $DIR/rename.rs:99:9
    |
 LL | #![warn(clippy::invalid_atomic_ordering)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
 
 error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
-  --> $DIR/rename.rs:96:9
+  --> $DIR/rename.rs:100:9
    |
 LL | #![warn(clippy::invalid_ref)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
 
 error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked`
-  --> $DIR/rename.rs:97:9
+  --> $DIR/rename.rs:101:9
    |
 LL | #![warn(clippy::invalid_utf8_in_unchecked)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked`
 
 error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop`
-  --> $DIR/rename.rs:98:9
+  --> $DIR/rename.rs:102:9
    |
 LL | #![warn(clippy::let_underscore_drop)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop`
 
 error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
-  --> $DIR/rename.rs:99:9
+  --> $DIR/rename.rs:103:9
    |
 LL | #![warn(clippy::mem_discriminant_non_enum)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
 
 error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
-  --> $DIR/rename.rs:100:9
+  --> $DIR/rename.rs:104:9
    |
 LL | #![warn(clippy::panic_params)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
 
 error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally`
-  --> $DIR/rename.rs:101:9
+  --> $DIR/rename.rs:105:9
    |
 LL | #![warn(clippy::positional_named_format_parameters)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally`
 
 error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
-  --> $DIR/rename.rs:102:9
+  --> $DIR/rename.rs:106:9
    |
 LL | #![warn(clippy::temporary_cstring_as_ptr)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
 
 error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops`
-  --> $DIR/rename.rs:103:9
+  --> $DIR/rename.rs:107:9
    |
 LL | #![warn(clippy::undropped_manually_drops)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops`
 
 error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
-  --> $DIR/rename.rs:104:9
+  --> $DIR/rename.rs:108:9
    |
 LL | #![warn(clippy::unknown_clippy_lints)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
 
 error: lint `clippy::unused_label` has been renamed to `unused_labels`
-  --> $DIR/rename.rs:105:9
+  --> $DIR/rename.rs:109:9
    |
 LL | #![warn(clippy::unused_label)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
 
-error: aborting due to 54 previous errors
+error: aborting due to 56 previous errors
 
diff --git a/src/tools/clippy/tests/ui/result_large_err.rs b/src/tools/clippy/tests/ui/result_large_err.rs
index 14a1f7e1db5..b25348bf996 100644
--- a/src/tools/clippy/tests/ui/result_large_err.rs
+++ b/src/tools/clippy/tests/ui/result_large_err.rs
@@ -1,3 +1,5 @@
+//@ignore-32bit
+
 #![warn(clippy::result_large_err)]
 #![allow(clippy::large_enum_variant)]
 
diff --git a/src/tools/clippy/tests/ui/result_large_err.stderr b/src/tools/clippy/tests/ui/result_large_err.stderr
index d42dd6600a3..6602f396a9c 100644
--- a/src/tools/clippy/tests/ui/result_large_err.stderr
+++ b/src/tools/clippy/tests/ui/result_large_err.stderr
@@ -1,5 +1,5 @@
 error: the `Err`-variant returned from this function is very large
-  --> $DIR/result_large_err.rs:8:23
+  --> $DIR/result_large_err.rs:10:23
    |
 LL | pub fn large_err() -> Result<(), [u8; 512]> {
    |                       ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
@@ -9,7 +9,7 @@ LL | pub fn large_err() -> Result<(), [u8; 512]> {
    = help: to override `-D warnings` add `#[allow(clippy::result_large_err)]`
 
 error: the `Err`-variant returned from this function is very large
-  --> $DIR/result_large_err.rs:20:21
+  --> $DIR/result_large_err.rs:22:21
    |
 LL |     pub fn ret() -> Result<(), Self> {
    |                     ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes
@@ -17,7 +17,7 @@ LL |     pub fn ret() -> Result<(), Self> {
    = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>`
 
 error: the `Err`-variant returned from this function is very large
-  --> $DIR/result_large_err.rs:26:26
+  --> $DIR/result_large_err.rs:28:26
    |
 LL | pub fn struct_error() -> Result<(), FullyDefinedLargeError> {
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes
@@ -25,7 +25,7 @@ LL | pub fn struct_error() -> Result<(), FullyDefinedLargeError> {
    = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>`
 
 error: the `Err`-variant returned from this function is very large
-  --> $DIR/result_large_err.rs:32:45
+  --> $DIR/result_large_err.rs:34:45
    |
 LL | pub fn large_err_via_type_alias<T>(x: T) -> Fdlr<T> {
    |                                             ^^^^^^^ the `Err`-variant is at least 240 bytes
@@ -33,7 +33,7 @@ LL | pub fn large_err_via_type_alias<T>(x: T) -> Fdlr<T> {
    = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>`
 
 error: the `Err`-variant returned from this function is very large
-  --> $DIR/result_large_err.rs:41:34
+  --> $DIR/result_large_err.rs:43:34
    |
 LL | pub fn param_large_error<R>() -> Result<(), (u128, R, FullyDefinedLargeError)> {
    |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 256 bytes
@@ -41,7 +41,7 @@ LL | pub fn param_large_error<R>() -> Result<(), (u128, R, FullyDefinedLargeErro
    = help: try reducing the size of `(u128, R, FullyDefinedLargeError)`, for example by boxing large elements or replacing it with `Box<(u128, R, FullyDefinedLargeError)>`
 
 error: the `Err`-variant returned from this function is very large
-  --> $DIR/result_large_err.rs:53:34
+  --> $DIR/result_large_err.rs:55:34
    |
 LL |     _Omg([u8; 512]),
    |     --------------- the largest variant contains at least 512 bytes
@@ -52,7 +52,7 @@ LL |     pub fn large_enum_error() -> Result<(), Self> {
    = help: try reducing the size of `LargeErrorVariants<()>`, for example by boxing large elements or replacing it with `Box<LargeErrorVariants<()>>`
 
 error: the `Err`-variant returned from this function is very large
-  --> $DIR/result_large_err.rs:66:30
+  --> $DIR/result_large_err.rs:68:30
    |
 LL |     _Biggest([u8; 1024]),
    |     -------------------- the largest variant contains at least 1024 bytes
@@ -65,7 +65,7 @@ LL |     fn large_enum_error() -> Result<(), Self> {
    = help: try reducing the size of `MultipleLargeVariants`, for example by boxing large elements or replacing it with `Box<MultipleLargeVariants>`
 
 error: the `Err`-variant returned from this function is very large
-  --> $DIR/result_large_err.rs:73:25
+  --> $DIR/result_large_err.rs:75:25
    |
 LL |     fn large_error() -> Result<(), [u8; 512]> {
    |                         ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
@@ -73,7 +73,7 @@ LL |     fn large_error() -> Result<(), [u8; 512]> {
    = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>`
 
 error: the `Err`-variant returned from this function is very large
-  --> $DIR/result_large_err.rs:93:29
+  --> $DIR/result_large_err.rs:95:29
    |
 LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> {
    |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
@@ -81,7 +81,7 @@ LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> {
    = help: try reducing the size of `FullyDefinedUnionError`, for example by boxing large elements or replacing it with `Box<FullyDefinedUnionError>`
 
 error: the `Err`-variant returned from this function is very large
-  --> $DIR/result_large_err.rs:103:40
+  --> $DIR/result_large_err.rs:105:40
    |
 LL | pub fn param_large_union<T: Copy>() -> Result<(), UnionError<T>> {
    |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
@@ -89,7 +89,7 @@ LL | pub fn param_large_union<T: Copy>() -> Result<(), UnionError<T>> {
    = help: try reducing the size of `UnionError<T>`, for example by boxing large elements or replacing it with `Box<UnionError<T>>`
 
 error: the `Err`-variant returned from this function is very large
-  --> $DIR/result_large_err.rs:113:34
+  --> $DIR/result_large_err.rs:115:34
    |
 LL | pub fn array_error_subst<U>() -> Result<(), ArrayError<i32, U>> {
    |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes
@@ -97,7 +97,7 @@ LL | pub fn array_error_subst<U>() -> Result<(), ArrayError<i32, U>> {
    = help: try reducing the size of `ArrayError<i32, U>`, for example by boxing large elements or replacing it with `Box<ArrayError<i32, U>>`
 
 error: the `Err`-variant returned from this function is very large
-  --> $DIR/result_large_err.rs:118:31
+  --> $DIR/result_large_err.rs:120:31
    |
 LL | pub fn array_error<T, U>() -> Result<(), ArrayError<(i32, T), U>> {
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes
diff --git a/src/tools/clippy/tests/ui/similar_names.rs b/src/tools/clippy/tests/ui/similar_names.rs
index c5a941316da..f46af56c6e2 100644
--- a/src/tools/clippy/tests/ui/similar_names.rs
+++ b/src/tools/clippy/tests/ui/similar_names.rs
@@ -3,6 +3,7 @@
     unused,
     clippy::println_empty_string,
     clippy::empty_loop,
+    clippy::never_loop,
     clippy::diverging_sub_expression,
     clippy::let_unit_value
 )]
diff --git a/src/tools/clippy/tests/ui/similar_names.stderr b/src/tools/clippy/tests/ui/similar_names.stderr
index 011dbe679c0..44ae3532a48 100644
--- a/src/tools/clippy/tests/ui/similar_names.stderr
+++ b/src/tools/clippy/tests/ui/similar_names.stderr
@@ -1,11 +1,11 @@
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:21:9
+  --> $DIR/similar_names.rs:22:9
    |
 LL |     let bpple: i32;
    |         ^^^^^
    |
 note: existing binding defined here
-  --> $DIR/similar_names.rs:19:9
+  --> $DIR/similar_names.rs:20:9
    |
 LL |     let apple: i32;
    |         ^^^^^
@@ -13,73 +13,73 @@ LL |     let apple: i32;
    = help: to override `-D warnings` add `#[allow(clippy::similar_names)]`
 
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:24:9
+  --> $DIR/similar_names.rs:25:9
    |
 LL |     let cpple: i32;
    |         ^^^^^
    |
 note: existing binding defined here
-  --> $DIR/similar_names.rs:19:9
+  --> $DIR/similar_names.rs:20:9
    |
 LL |     let apple: i32;
    |         ^^^^^
 
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:49:9
+  --> $DIR/similar_names.rs:50:9
    |
 LL |     let bluby: i32;
    |         ^^^^^
    |
 note: existing binding defined here
-  --> $DIR/similar_names.rs:48:9
+  --> $DIR/similar_names.rs:49:9
    |
 LL |     let blubx: i32;
    |         ^^^^^
 
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:54:9
+  --> $DIR/similar_names.rs:55:9
    |
 LL |     let coke: i32;
    |         ^^^^
    |
 note: existing binding defined here
-  --> $DIR/similar_names.rs:52:9
+  --> $DIR/similar_names.rs:53:9
    |
 LL |     let cake: i32;
    |         ^^^^
 
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:73:9
+  --> $DIR/similar_names.rs:74:9
    |
 LL |     let xyzeabc: i32;
    |         ^^^^^^^
    |
 note: existing binding defined here
-  --> $DIR/similar_names.rs:71:9
+  --> $DIR/similar_names.rs:72:9
    |
 LL |     let xyz1abc: i32;
    |         ^^^^^^^
 
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:78:9
+  --> $DIR/similar_names.rs:79:9
    |
 LL |     let parsee: i32;
    |         ^^^^^^
    |
 note: existing binding defined here
-  --> $DIR/similar_names.rs:76:9
+  --> $DIR/similar_names.rs:77:9
    |
 LL |     let parser: i32;
    |         ^^^^^^
 
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:100:16
+  --> $DIR/similar_names.rs:101:16
    |
 LL |         bpple: sprang,
    |                ^^^^^^
    |
 note: existing binding defined here
-  --> $DIR/similar_names.rs:99:16
+  --> $DIR/similar_names.rs:100:16
    |
 LL |         apple: spring,
    |                ^^^^^^
diff --git a/src/tools/clippy/tests/ui/single_call_fn.rs b/src/tools/clippy/tests/ui/single_call_fn.rs
index d6493f23413..3cc8061647d 100644
--- a/src/tools/clippy/tests/ui/single_call_fn.rs
+++ b/src/tools/clippy/tests/ui/single_call_fn.rs
@@ -1,3 +1,4 @@
+//@ignore-32bit
 //@aux-build:proc_macros.rs
 #![allow(clippy::redundant_closure_call, unused)]
 #![warn(clippy::single_call_fn)]
diff --git a/src/tools/clippy/tests/ui/single_call_fn.stderr b/src/tools/clippy/tests/ui/single_call_fn.stderr
index 4acb383c408..d5cd707754c 100644
--- a/src/tools/clippy/tests/ui/single_call_fn.stderr
+++ b/src/tools/clippy/tests/ui/single_call_fn.stderr
@@ -1,5 +1,5 @@
 error: this function is only used once
-  --> $DIR/single_call_fn.rs:33:1
+  --> $DIR/single_call_fn.rs:34:1
    |
 LL | / fn c() {
 LL | |     println!("really");
@@ -9,7 +9,7 @@ LL | | }
    | |_^
    |
 help: used here
-  --> $DIR/single_call_fn.rs:40:5
+  --> $DIR/single_call_fn.rs:41:5
    |
 LL |     c();
    |     ^
@@ -17,37 +17,37 @@ LL |     c();
    = help: to override `-D warnings` add `#[allow(clippy::single_call_fn)]`
 
 error: this function is only used once
-  --> $DIR/single_call_fn.rs:12:1
+  --> $DIR/single_call_fn.rs:13:1
    |
 LL | fn i() {}
    | ^^^^^^^^^
    |
 help: used here
-  --> $DIR/single_call_fn.rs:17:13
+  --> $DIR/single_call_fn.rs:18:13
    |
 LL |     let a = i;
    |             ^
 
 error: this function is only used once
-  --> $DIR/single_call_fn.rs:43:1
+  --> $DIR/single_call_fn.rs:44:1
    |
 LL | fn a() {}
    | ^^^^^^^^^
    |
 help: used here
-  --> $DIR/single_call_fn.rs:46:5
+  --> $DIR/single_call_fn.rs:47:5
    |
 LL |     a();
    |     ^
 
 error: this function is only used once
-  --> $DIR/single_call_fn.rs:13:1
+  --> $DIR/single_call_fn.rs:14:1
    |
 LL | fn j() {}
    | ^^^^^^^^^
    |
 help: used here
-  --> $DIR/single_call_fn.rs:24:9
+  --> $DIR/single_call_fn.rs:25:9
    |
 LL |         j();
    |         ^
diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.rs b/src/tools/clippy/tests/ui/slow_vector_initialization.rs
index f8d85ed38cd..5c3086c9d69 100644
--- a/src/tools/clippy/tests/ui/slow_vector_initialization.rs
+++ b/src/tools/clippy/tests/ui/slow_vector_initialization.rs
@@ -1,5 +1,5 @@
-use std::iter::repeat;
 //@no-rustfix
+use std::iter::repeat;
 fn main() {
     resize_vector();
     extend_vector();
@@ -86,6 +86,20 @@ fn from_empty_vec() {
     vec1 = Vec::new();
     vec1.resize(10, 0);
     //~^ ERROR: slow zero-filling initialization
+
+    vec1 = vec![];
+    vec1.resize(10, 0);
+    //~^ ERROR: slow zero-filling initialization
+
+    macro_rules! x {
+        () => {
+            vec![]
+        };
+    }
+
+    // `vec![]` comes from another macro, don't warn
+    vec1 = x!();
+    vec1.resize(10, 0);
 }
 
 fn do_stuff(vec: &mut [u8]) {}
diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr
index f4501551656..4d24400ecb5 100644
--- a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr
+++ b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr
@@ -97,8 +97,16 @@ LL |     vec1 = Vec::new();
 LL |     vec1.resize(10, 0);
    |     ^^^^^^^^^^^^^^^^^^
 
+error: slow zero-filling initialization
+  --> $DIR/slow_vector_initialization.rs:91:5
+   |
+LL |     vec1 = vec![];
+   |            ------ help: consider replacing this with: `vec![0; 10]`
+LL |     vec1.resize(10, 0);
+   |     ^^^^^^^^^^^^^^^^^^
+
 error: this argument is a mutable reference, but not used mutably
-  --> $DIR/slow_vector_initialization.rs:91:18
+  --> $DIR/slow_vector_initialization.rs:105:18
    |
 LL | fn do_stuff(vec: &mut [u8]) {}
    |                  ^^^^^^^^^ help: consider changing to: `&[u8]`
@@ -106,5 +114,5 @@ LL | fn do_stuff(vec: &mut [u8]) {}
    = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]`
 
-error: aborting due to 13 previous errors
+error: aborting due to 14 previous errors
 
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.fixed b/src/tools/clippy/tests/ui/std_instead_of_core.fixed
new file mode 100644
index 00000000000..8027c053fb5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/std_instead_of_core.fixed
@@ -0,0 +1,62 @@
+#![warn(clippy::std_instead_of_core)]
+#![allow(unused_imports)]
+
+extern crate alloc;
+
+#[warn(clippy::std_instead_of_core)]
+fn std_instead_of_core() {
+    // Regular import
+    use core::hash::Hasher;
+    //~^ ERROR: used import from `std` instead of `core`
+    // Absolute path
+    use ::core::hash::Hash;
+    //~^ ERROR: used import from `std` instead of `core`
+    // Don't lint on `env` macro
+    use std::env;
+
+    // Multiple imports
+    use core::fmt::{Debug, Result};
+    //~^ ERROR: used import from `std` instead of `core`
+
+    // Function calls
+    let ptr = core::ptr::null::<u32>();
+    //~^ ERROR: used import from `std` instead of `core`
+    let ptr_mut = ::core::ptr::null_mut::<usize>();
+    //~^ ERROR: used import from `std` instead of `core`
+
+    // Types
+    let cell = core::cell::Cell::new(8u32);
+    //~^ ERROR: used import from `std` instead of `core`
+    let cell_absolute = ::core::cell::Cell::new(8u32);
+    //~^ ERROR: used import from `std` instead of `core`
+
+    let _ = std::env!("PATH");
+
+    // do not lint until `error_in_core` is stable
+    use std::error::Error;
+
+    // lint items re-exported from private modules, `core::iter::traits::iterator::Iterator`
+    use core::iter::Iterator;
+    //~^ ERROR: used import from `std` instead of `core`
+}
+
+#[warn(clippy::std_instead_of_alloc)]
+fn std_instead_of_alloc() {
+    // Only lint once.
+    use alloc::vec;
+    //~^ ERROR: used import from `std` instead of `alloc`
+    use alloc::vec::Vec;
+    //~^ ERROR: used import from `std` instead of `alloc`
+}
+
+#[warn(clippy::alloc_instead_of_core)]
+fn alloc_instead_of_core() {
+    use core::slice::from_ref;
+    //~^ ERROR: used import from `alloc` instead of `core`
+}
+
+fn main() {
+    std_instead_of_core();
+    std_instead_of_alloc();
+    alloc_instead_of_core();
+}
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.rs b/src/tools/clippy/tests/ui/std_instead_of_core.rs
index 2e44487d77e..63a096384d7 100644
--- a/src/tools/clippy/tests/ui/std_instead_of_core.rs
+++ b/src/tools/clippy/tests/ui/std_instead_of_core.rs
@@ -17,7 +17,6 @@ fn std_instead_of_core() {
     // Multiple imports
     use std::fmt::{Debug, Result};
     //~^ ERROR: used import from `std` instead of `core`
-    //~| ERROR: used import from `std` instead of `core`
 
     // Function calls
     let ptr = std::ptr::null::<u32>();
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.stderr b/src/tools/clippy/tests/ui/std_instead_of_core.stderr
index 7435d716157..ca26f77bd37 100644
--- a/src/tools/clippy/tests/ui/std_instead_of_core.stderr
+++ b/src/tools/clippy/tests/ui/std_instead_of_core.stderr
@@ -2,103 +2,76 @@ error: used import from `std` instead of `core`
   --> $DIR/std_instead_of_core.rs:9:9
    |
 LL |     use std::hash::Hasher;
-   |         ^^^^^^^^^^^^^^^^^
+   |         ^^^ help: consider importing the item from `core`: `core`
    |
-   = help: consider importing the item from `core`
    = note: `-D clippy::std-instead-of-core` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_core)]`
 
 error: used import from `std` instead of `core`
-  --> $DIR/std_instead_of_core.rs:12:9
+  --> $DIR/std_instead_of_core.rs:12:11
    |
 LL |     use ::std::hash::Hash;
-   |         ^^^^^^^^^^^^^^^^^
-   |
-   = help: consider importing the item from `core`
+   |           ^^^ help: consider importing the item from `core`: `core`
 
 error: used import from `std` instead of `core`
-  --> $DIR/std_instead_of_core.rs:18:20
+  --> $DIR/std_instead_of_core.rs:18:9
    |
 LL |     use std::fmt::{Debug, Result};
-   |                    ^^^^^
-   |
-   = help: consider importing the item from `core`
-
-error: used import from `std` instead of `core`
-  --> $DIR/std_instead_of_core.rs:18:27
-   |
-LL |     use std::fmt::{Debug, Result};
-   |                           ^^^^^^
-   |
-   = help: consider importing the item from `core`
+   |         ^^^ help: consider importing the item from `core`: `core`
 
 error: used import from `std` instead of `core`
-  --> $DIR/std_instead_of_core.rs:23:15
+  --> $DIR/std_instead_of_core.rs:22:15
    |
 LL |     let ptr = std::ptr::null::<u32>();
-   |               ^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: consider importing the item from `core`
+   |               ^^^ help: consider importing the item from `core`: `core`
 
 error: used import from `std` instead of `core`
-  --> $DIR/std_instead_of_core.rs:25:19
+  --> $DIR/std_instead_of_core.rs:24:21
    |
 LL |     let ptr_mut = ::std::ptr::null_mut::<usize>();
-   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: consider importing the item from `core`
+   |                     ^^^ help: consider importing the item from `core`: `core`
 
 error: used import from `std` instead of `core`
-  --> $DIR/std_instead_of_core.rs:29:16
+  --> $DIR/std_instead_of_core.rs:28:16
    |
 LL |     let cell = std::cell::Cell::new(8u32);
-   |                ^^^^^^^^^^^^^^^
-   |
-   = help: consider importing the item from `core`
+   |                ^^^ help: consider importing the item from `core`: `core`
 
 error: used import from `std` instead of `core`
-  --> $DIR/std_instead_of_core.rs:31:25
+  --> $DIR/std_instead_of_core.rs:30:27
    |
 LL |     let cell_absolute = ::std::cell::Cell::new(8u32);
-   |                         ^^^^^^^^^^^^^^^^^
-   |
-   = help: consider importing the item from `core`
+   |                           ^^^ help: consider importing the item from `core`: `core`
 
 error: used import from `std` instead of `core`
-  --> $DIR/std_instead_of_core.rs:40:9
+  --> $DIR/std_instead_of_core.rs:39:9
    |
 LL |     use std::iter::Iterator;
-   |         ^^^^^^^^^^^^^^^^^^^
-   |
-   = help: consider importing the item from `core`
+   |         ^^^ help: consider importing the item from `core`: `core`
 
 error: used import from `std` instead of `alloc`
-  --> $DIR/std_instead_of_core.rs:47:9
+  --> $DIR/std_instead_of_core.rs:46:9
    |
 LL |     use std::vec;
-   |         ^^^^^^^^
+   |         ^^^ help: consider importing the item from `alloc`: `alloc`
    |
-   = help: consider importing the item from `alloc`
    = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]`
 
 error: used import from `std` instead of `alloc`
-  --> $DIR/std_instead_of_core.rs:49:9
+  --> $DIR/std_instead_of_core.rs:48:9
    |
 LL |     use std::vec::Vec;
-   |         ^^^^^^^^^^^^^
-   |
-   = help: consider importing the item from `alloc`
+   |         ^^^ help: consider importing the item from `alloc`: `alloc`
 
 error: used import from `alloc` instead of `core`
-  --> $DIR/std_instead_of_core.rs:55:9
+  --> $DIR/std_instead_of_core.rs:54:9
    |
 LL |     use alloc::slice::from_ref;
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^ help: consider importing the item from `core`: `core`
    |
-   = help: consider importing the item from `core`
    = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::alloc_instead_of_core)]`
 
-error: aborting due to 12 previous errors
+error: aborting due to 11 previous errors
 
diff --git a/src/tools/clippy/tests/ui/transmute_32bit.stderr b/src/tools/clippy/tests/ui/transmute_32bit.stderr
index 75ddca60d2a..baa819e30fd 100644
--- a/src/tools/clippy/tests/ui/transmute_32bit.stderr
+++ b/src/tools/clippy/tests/ui/transmute_32bit.stderr
@@ -1,39 +1,29 @@
-error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
+error: transmute from a `f32` to a pointer
   --> $DIR/transmute_32bit.rs:6:31
    |
 LL |         let _: *const usize = std::mem::transmute(6.0f32);
-   |                               ^^^^^^^^^^^^^^^^^^^
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: source type: `f32` (32 bits)
-   = note: target type: `*const usize` (64 bits)
+   = note: `-D clippy::wrong-transmute` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::wrong_transmute)]`
 
-error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
+error: transmute from a `f32` to a pointer
   --> $DIR/transmute_32bit.rs:8:29
    |
 LL |         let _: *mut usize = std::mem::transmute(6.0f32);
-   |                             ^^^^^^^^^^^^^^^^^^^
-   |
-   = note: source type: `f32` (32 bits)
-   = note: target type: `*mut usize` (64 bits)
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
+error: transmute from a `char` to a pointer
   --> $DIR/transmute_32bit.rs:10:31
    |
 LL |         let _: *const usize = std::mem::transmute('x');
-   |                               ^^^^^^^^^^^^^^^^^^^
-   |
-   = note: source type: `char` (32 bits)
-   = note: target type: `*const usize` (64 bits)
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
+error: transmute from a `char` to a pointer
   --> $DIR/transmute_32bit.rs:12:29
    |
 LL |         let _: *mut usize = std::mem::transmute('x');
-   |                             ^^^^^^^^^^^^^^^^^^^
-   |
-   = note: source type: `char` (32 bits)
-   = note: target type: `*mut usize` (64 bits)
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 4 previous errors
 
-For more information about this error, try `rustc --explain E0512`.
diff --git a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed
index b5dedef5f4c..f3cf65da2d6 100644
--- a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed
@@ -1,4 +1,4 @@
-#![allow(clippy::incorrect_clone_impl_on_copy_type, unused)]
+#![allow(clippy::non_canonical_clone_impl, unused)]
 #![warn(clippy::unnecessary_struct_initialization)]
 
 struct S {
diff --git a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs
index 2222c3ddf37..bd5302f9d85 100644
--- a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs
@@ -1,4 +1,4 @@
-#![allow(clippy::incorrect_clone_impl_on_copy_type, unused)]
+#![allow(clippy::non_canonical_clone_impl, unused)]
 #![warn(clippy::unnecessary_struct_initialization)]
 
 struct S {
diff --git a/src/tools/clippy/tests/ui/vec.fixed b/src/tools/clippy/tests/ui/vec.fixed
index 3ff2acbe28f..bcbca971a78 100644
--- a/src/tools/clippy/tests/ui/vec.fixed
+++ b/src/tools/clippy/tests/ui/vec.fixed
@@ -120,6 +120,7 @@ fn issue11075() {
             stringify!($e)
         };
     }
+    #[allow(clippy::never_loop)]
     for _string in [repro!(true), repro!(null)] {
         unimplemented!();
     }
diff --git a/src/tools/clippy/tests/ui/vec.rs b/src/tools/clippy/tests/ui/vec.rs
index 2ab025f424a..087425585de 100644
--- a/src/tools/clippy/tests/ui/vec.rs
+++ b/src/tools/clippy/tests/ui/vec.rs
@@ -120,6 +120,7 @@ fn issue11075() {
             stringify!($e)
         };
     }
+    #[allow(clippy::never_loop)]
     for _string in vec![repro!(true), repro!(null)] {
         unimplemented!();
     }
diff --git a/src/tools/clippy/tests/ui/vec.stderr b/src/tools/clippy/tests/ui/vec.stderr
index a28024d236a..fc261838fe3 100644
--- a/src/tools/clippy/tests/ui/vec.stderr
+++ b/src/tools/clippy/tests/ui/vec.stderr
@@ -86,31 +86,31 @@ LL |     for _ in vec![1, 2, 3] {}
    |              ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:123:20
+  --> $DIR/vec.rs:124:20
    |
 LL |     for _string in vec![repro!(true), repro!(null)] {
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can use an array directly: `[repro!(true), repro!(null)]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:140:18
+  --> $DIR/vec.rs:141:18
    |
 LL |     in_macro!(1, vec![1, 2], vec![1; 2]);
    |                  ^^^^^^^^^^ help: you can use an array directly: `[1, 2]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:140:30
+  --> $DIR/vec.rs:141:30
    |
 LL |     in_macro!(1, vec![1, 2], vec![1; 2]);
    |                              ^^^^^^^^^^ help: you can use an array directly: `[1; 2]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:159:14
+  --> $DIR/vec.rs:160:14
    |
 LL |     for a in vec![1, 2, 3] {
    |              ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:163:14
+  --> $DIR/vec.rs:164:14
    |
 LL |     for a in vec![String::new(), String::new()] {
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can use an array directly: `[String::new(), String::new()]`
diff --git a/src/tools/clippy/tests/ui/write_literal_2.stderr b/src/tools/clippy/tests/ui/write_literal_2.stderr
index fc24dba4543..6d382a267ad 100644
--- a/src/tools/clippy/tests/ui/write_literal_2.stderr
+++ b/src/tools/clippy/tests/ui/write_literal_2.stderr
@@ -2,7 +2,9 @@ error: unnecessary raw string literal
   --> $DIR/write_literal_2.rs:13:24
    |
 LL |     writeln!(v, r"{}", r"{hello}");
-   |                        ^^^^^^^^^^ help: try: `"{hello}"`
+   |                        -^^^^^^^^^
+   |                        |
+   |                        help: use a string literal instead: `"{hello}"`
    |
    = note: `-D clippy::needless-raw-strings` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::needless_raw_strings)]`
diff --git a/src/tools/compiletest/src/read2.rs b/src/tools/compiletest/src/read2.rs
index a455a1badc0..3f1921cb6bd 100644
--- a/src/tools/compiletest/src/read2.rs
+++ b/src/tools/compiletest/src/read2.rs
@@ -9,7 +9,16 @@ use std::io::{self, Write};
 use std::mem::replace;
 use std::process::{Child, Output};
 
-pub fn read2_abbreviated(mut child: Child, filter_paths_from_len: &[String]) -> io::Result<Output> {
+#[derive(Copy, Clone, Debug)]
+pub enum Truncated {
+    Yes,
+    No,
+}
+
+pub fn read2_abbreviated(
+    mut child: Child,
+    filter_paths_from_len: &[String],
+) -> io::Result<(Output, Truncated)> {
     let mut stdout = ProcOutput::new();
     let mut stderr = ProcOutput::new();
 
@@ -24,11 +33,12 @@ pub fn read2_abbreviated(mut child: Child, filter_paths_from_len: &[String]) ->
     )?;
     let status = child.wait()?;
 
-    Ok(Output { status, stdout: stdout.into_bytes(), stderr: stderr.into_bytes() })
+    let truncated =
+        if stdout.truncated() || stderr.truncated() { Truncated::Yes } else { Truncated::No };
+    Ok((Output { status, stdout: stdout.into_bytes(), stderr: stderr.into_bytes() }, truncated))
 }
 
-const HEAD_LEN: usize = 160 * 1024;
-const TAIL_LEN: usize = 256 * 1024;
+const MAX_OUT_LEN: usize = 512 * 1024;
 
 // Whenever a path is filtered when counting the length of the output, we need to add some
 // placeholder length to ensure a compiler emitting only filtered paths doesn't cause a OOM.
@@ -39,7 +49,7 @@ const FILTERED_PATHS_PLACEHOLDER_LEN: usize = 32;
 
 enum ProcOutput {
     Full { bytes: Vec<u8>, filtered_len: usize },
-    Abbreviated { head: Vec<u8>, skipped: usize, tail: Box<[u8]> },
+    Abbreviated { head: Vec<u8>, skipped: usize },
 }
 
 impl ProcOutput {
@@ -47,6 +57,10 @@ impl ProcOutput {
         ProcOutput::Full { bytes: Vec::new(), filtered_len: 0 }
     }
 
+    fn truncated(&self) -> bool {
+        matches!(self, Self::Abbreviated { .. })
+    }
+
     fn extend(&mut self, data: &[u8], filter_paths_from_len: &[String]) {
         let new_self = match *self {
             ProcOutput::Full { ref mut bytes, ref mut filtered_len } => {
@@ -83,24 +97,21 @@ impl ProcOutput {
                 }
 
                 let new_len = bytes.len();
-                if (*filtered_len).min(new_len) <= HEAD_LEN + TAIL_LEN {
+                if (*filtered_len).min(new_len) <= MAX_OUT_LEN {
                     return;
                 }
 
                 let mut head = replace(bytes, Vec::new());
-                let mut middle = head.split_off(HEAD_LEN);
-                let tail = middle.split_off(middle.len() - TAIL_LEN).into_boxed_slice();
-                let skipped = new_len - HEAD_LEN - TAIL_LEN;
-                ProcOutput::Abbreviated { head, skipped, tail }
+                // Don't truncate if this as a whole line.
+                // That should make it less likely that we cut a JSON line in half.
+                if head.last() != Some(&('\n' as u8)) {
+                    head.truncate(MAX_OUT_LEN);
+                }
+                let skipped = new_len - head.len();
+                ProcOutput::Abbreviated { head, skipped }
             }
-            ProcOutput::Abbreviated { ref mut skipped, ref mut tail, .. } => {
+            ProcOutput::Abbreviated { ref mut skipped, .. } => {
                 *skipped += data.len();
-                if data.len() <= TAIL_LEN {
-                    tail[..data.len()].copy_from_slice(data);
-                    tail.rotate_left(data.len());
-                } else {
-                    tail.copy_from_slice(&data[(data.len() - TAIL_LEN)..]);
-                }
                 return;
             }
         };
@@ -110,18 +121,12 @@ impl ProcOutput {
     fn into_bytes(self) -> Vec<u8> {
         match self {
             ProcOutput::Full { bytes, .. } => bytes,
-            ProcOutput::Abbreviated { mut head, mut skipped, tail } => {
-                let mut tail = &*tail;
-
-                // Skip over '{' at the start of the tail, so we don't later wrongfully consider this as json.
-                // See <https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp/topic/Weird.20CI.20failure/near/321797811>
-                while tail.get(0) == Some(&b'{') {
-                    tail = &tail[1..];
-                    skipped += 1;
-                }
-
-                write!(&mut head, "\n\n<<<<<< SKIPPED {} BYTES >>>>>>\n\n", skipped).unwrap();
-                head.extend_from_slice(tail);
+            ProcOutput::Abbreviated { mut head, skipped } => {
+                let head_note =
+                    format!("<<<<<< TRUNCATED, SHOWING THE FIRST {} BYTES >>>>>>\n\n", head.len());
+                head.splice(0..0, head_note.into_bytes());
+                write!(&mut head, "\n\n<<<<<< TRUNCATED, DROPPED {} BYTES >>>>>>", skipped)
+                    .unwrap();
                 head
             }
         }
diff --git a/src/tools/compiletest/src/read2/tests.rs b/src/tools/compiletest/src/read2/tests.rs
index 1ca682a46aa..5ad2db3cb83 100644
--- a/src/tools/compiletest/src/read2/tests.rs
+++ b/src/tools/compiletest/src/read2/tests.rs
@@ -1,4 +1,6 @@
-use crate::read2::{ProcOutput, FILTERED_PATHS_PLACEHOLDER_LEN, HEAD_LEN, TAIL_LEN};
+use std::io::Write;
+
+use crate::read2::{ProcOutput, FILTERED_PATHS_PLACEHOLDER_LEN, MAX_OUT_LEN};
 
 #[test]
 fn test_abbreviate_short_string() {
@@ -21,35 +23,13 @@ fn test_abbreviate_short_string_multiple_steps() {
 fn test_abbreviate_long_string() {
     let mut out = ProcOutput::new();
 
-    let data = vec![b'.'; HEAD_LEN + TAIL_LEN + 16];
+    let data = vec![b'.'; MAX_OUT_LEN + 16];
     out.extend(&data, &[]);
 
-    let mut expected = vec![b'.'; HEAD_LEN];
-    expected.extend_from_slice(b"\n\n<<<<<< SKIPPED 16 BYTES >>>>>>\n\n");
-    expected.extend_from_slice(&vec![b'.'; TAIL_LEN]);
-
-    // We first check the length to avoid endless terminal output if the length differs, since
-    // `out` is hundreds of KBs in size.
-    let out = out.into_bytes();
-    assert_eq!(expected.len(), out.len());
-    assert_eq!(expected, out);
-}
-
-#[test]
-fn test_abbreviate_long_string_multiple_steps() {
-    let mut out = ProcOutput::new();
-
-    out.extend(&vec![b'.'; HEAD_LEN], &[]);
-    out.extend(&vec![b'.'; TAIL_LEN], &[]);
-    // Also test whether the rotation works
-    out.extend(&vec![b'!'; 16], &[]);
-    out.extend(&vec![b'?'; 16], &[]);
-
-    let mut expected = vec![b'.'; HEAD_LEN];
-    expected.extend_from_slice(b"\n\n<<<<<< SKIPPED 32 BYTES >>>>>>\n\n");
-    expected.extend_from_slice(&vec![b'.'; TAIL_LEN - 32]);
-    expected.extend_from_slice(&vec![b'!'; 16]);
-    expected.extend_from_slice(&vec![b'?'; 16]);
+    let mut expected = Vec::new();
+    write!(expected, "<<<<<< TRUNCATED, SHOWING THE FIRST {MAX_OUT_LEN} BYTES >>>>>>\n\n").unwrap();
+    expected.extend_from_slice(&[b'.'; MAX_OUT_LEN]);
+    expected.extend_from_slice(b"\n\n<<<<<< TRUNCATED, DROPPED 16 BYTES >>>>>>");
 
     // We first check the length to avoid endless terminal output if the length differs, since
     // `out` is hundreds of KBs in size.
@@ -86,9 +66,8 @@ fn test_abbreviate_filters_avoid_abbreviations() {
     let mut out = ProcOutput::new();
     let filters = &[std::iter::repeat('a').take(64).collect::<String>()];
 
-    let mut expected = vec![b'.'; HEAD_LEN - FILTERED_PATHS_PLACEHOLDER_LEN as usize];
+    let mut expected = vec![b'.'; MAX_OUT_LEN - FILTERED_PATHS_PLACEHOLDER_LEN as usize];
     expected.extend_from_slice(filters[0].as_bytes());
-    expected.extend_from_slice(&vec![b'.'; TAIL_LEN]);
 
     out.extend(&expected, filters);
 
@@ -104,14 +83,13 @@ fn test_abbreviate_filters_can_still_cause_abbreviations() {
     let mut out = ProcOutput::new();
     let filters = &[std::iter::repeat('a').take(64).collect::<String>()];
 
-    let mut input = vec![b'.'; HEAD_LEN];
-    input.extend_from_slice(&vec![b'.'; TAIL_LEN]);
+    let mut input = vec![b'.'; MAX_OUT_LEN];
     input.extend_from_slice(filters[0].as_bytes());
 
-    let mut expected = vec![b'.'; HEAD_LEN];
-    expected.extend_from_slice(b"\n\n<<<<<< SKIPPED 64 BYTES >>>>>>\n\n");
-    expected.extend_from_slice(&vec![b'.'; TAIL_LEN - 64]);
-    expected.extend_from_slice(&vec![b'a'; 64]);
+    let mut expected = Vec::new();
+    write!(expected, "<<<<<< TRUNCATED, SHOWING THE FIRST {MAX_OUT_LEN} BYTES >>>>>>\n\n").unwrap();
+    expected.extend_from_slice(&[b'.'; MAX_OUT_LEN]);
+    expected.extend_from_slice(b"\n\n<<<<<< TRUNCATED, DROPPED 64 BYTES >>>>>>");
 
     out.extend(&input, filters);
 
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 670441aacbd..65af650f6e5 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -12,7 +12,7 @@ use crate::compute_diff::{write_diff, write_filtered_diff};
 use crate::errors::{self, Error, ErrorKind};
 use crate::header::TestProps;
 use crate::json;
-use crate::read2::read2_abbreviated;
+use crate::read2::{read2_abbreviated, Truncated};
 use crate::util::{add_dylib_path, dylib_env_var, logv, PathBufExt};
 use crate::ColorConfig;
 use regex::{Captures, Regex};
@@ -701,6 +701,7 @@ impl<'test> TestCx<'test> {
             status: output.status,
             stdout: String::from_utf8(output.stdout).unwrap(),
             stderr: String::from_utf8(output.stderr).unwrap(),
+            truncated: Truncated::No,
             cmdline: format!("{cmd:?}"),
         };
         self.dump_output(&proc_res.stdout, &proc_res.stderr);
@@ -1275,6 +1276,7 @@ impl<'test> TestCx<'test> {
                 status,
                 stdout: String::from_utf8(stdout).unwrap(),
                 stderr: String::from_utf8(stderr).unwrap(),
+                truncated: Truncated::No,
                 cmdline,
             };
             if adb.kill().is_err() {
@@ -1475,6 +1477,15 @@ impl<'test> TestCx<'test> {
             "^core::num::([a-z_]+::)*NonZero.+$",
         ];
 
+        // In newer versions of lldb, persistent results (the `$N =` part at the start of
+        // expressions you have evaluated that let you re-use the result) aren't printed, but lots
+        // of rustc's debuginfo tests rely on these, so re-enable this.
+        // See <https://reviews.llvm.org/rG385496385476fc9735da5fa4acabc34654e8b30d>.
+        script_str.push_str("command unalias print\n");
+        script_str.push_str("command alias print expr --\n");
+        script_str.push_str("command unalias p\n");
+        script_str.push_str("command alias p expr --\n");
+
         script_str
             .push_str(&format!("command script import {}\n", &rust_pp_module_abs_path[..])[..]);
         script_str.push_str("type synthetic add -l lldb_lookup.synthetic_lookup -x '.*' ");
@@ -1557,7 +1568,13 @@ impl<'test> TestCx<'test> {
         };
 
         self.dump_output(&out, &err);
-        ProcRes { status, stdout: out, stderr: err, cmdline: format!("{:?}", cmd) }
+        ProcRes {
+            status,
+            stdout: out,
+            stderr: err,
+            truncated: Truncated::No,
+            cmdline: format!("{:?}", cmd),
+        }
     }
 
     fn cleanup_debug_info_options(&self, options: &Vec<String>) -> Vec<String> {
@@ -2218,7 +2235,7 @@ impl<'test> TestCx<'test> {
         dylib
     }
 
-    fn read2_abbreviated(&self, child: Child) -> Output {
+    fn read2_abbreviated(&self, child: Child) -> (Output, Truncated) {
         let mut filter_paths_from_len = Vec::new();
         let mut add_path = |path: &Path| {
             let path = path.display().to_string();
@@ -2265,12 +2282,13 @@ impl<'test> TestCx<'test> {
             child.stdin.as_mut().unwrap().write_all(input.as_bytes()).unwrap();
         }
 
-        let Output { status, stdout, stderr } = self.read2_abbreviated(child);
+        let (Output { status, stdout, stderr }, truncated) = self.read2_abbreviated(child);
 
         let result = ProcRes {
             status,
             stdout: String::from_utf8_lossy(&stdout).into_owned(),
             stderr: String::from_utf8_lossy(&stderr).into_owned(),
+            truncated,
             cmdline,
         };
 
@@ -3601,12 +3619,14 @@ impl<'test> TestCx<'test> {
             }
         }
 
-        let output = self.read2_abbreviated(cmd.spawn().expect("failed to spawn `make`"));
+        let (output, truncated) =
+            self.read2_abbreviated(cmd.spawn().expect("failed to spawn `make`"));
         if !output.status.success() {
             let res = ProcRes {
                 status: output.status,
                 stdout: String::from_utf8_lossy(&output.stdout).into_owned(),
                 stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
+                truncated,
                 cmdline: format!("{:?}", cmd),
             };
             self.fatal_proc_rec("make failed", &res);
@@ -3768,6 +3788,15 @@ impl<'test> TestCx<'test> {
         let emit_metadata = self.should_emit_metadata(pm);
         let proc_res = self.compile_test(should_run, emit_metadata);
         self.check_if_test_should_compile(&proc_res, pm);
+        if matches!(proc_res.truncated, Truncated::Yes)
+            && !self.props.dont_check_compiler_stdout
+            && !self.props.dont_check_compiler_stderr
+        {
+            self.fatal_proc_rec(
+                &format!("compiler output got truncated, cannot compare with reference file"),
+                &proc_res,
+            );
+        }
 
         // if the user specified a format in the ui test
         // print the output to the stderr file, otherwise extract
@@ -4459,6 +4488,7 @@ pub struct ProcRes {
     status: ExitStatus,
     stdout: String,
     stderr: String,
+    truncated: Truncated,
     cmdline: String,
 }
 
diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml
index 5e2abdde6ac..bcbd44a5f38 100644
--- a/src/tools/miri/.github/workflows/ci.yml
+++ b/src/tools/miri/.github/workflows/ci.yml
@@ -208,7 +208,7 @@ jobs:
           git push -u origin $BRANCH
       - name: Create Pull Request
         run: |
-          PR=$(gh pr create -B master --title 'Automatic sync from rustc' --body '')
+          PR=$(gh pr create -B master --title 'Automatic sync from rustc' --body '' --label subtree-sync)
           ~/.local/bin/zulip-send --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com \
             --stream miri --subject "Cron Job Failure (miri, $(date -u +%Y-%m))" \
             --message "A PR doing a rustc-pull [has been automatically created]($PR) for your convenience."
diff --git a/src/tools/miri/miri b/src/tools/miri/miri
index 938df9799da..e21738c3618 100755
--- a/src/tools/miri/miri
+++ b/src/tools/miri/miri
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 # Instead of doing just `cargo run --manifest-path .. $@`, we invoke miri-script binary directly. Invoking `cargo run` goes through
 # rustup (that sets it's own environmental variables), which is undesirable.
diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs
index b7031d9a3ee..124acc95098 100644
--- a/src/tools/miri/miri-script/src/commands.rs
+++ b/src/tools/miri/miri-script/src/commands.rs
@@ -102,8 +102,8 @@ impl Command {
         cmd.stdout(process::Stdio::null());
         cmd.stderr(process::Stdio::null());
         let josh = cmd.spawn().context("failed to start josh-proxy, make sure it is installed")?;
-        // Give it some time so hopefully the port is open. (10ms was not enough.)
-        thread::sleep(time::Duration::from_millis(100));
+        // Give it some time so hopefully the port is open. (100ms was not enough.)
+        thread::sleep(time::Duration::from_millis(200));
 
         // Create a wrapper that stops it on drop.
         struct Josh(process::Child);
@@ -338,7 +338,11 @@ impl Command {
         println!(
             "Confirmed that the push round-trips back to Miri properly. Please create a rustc PR:"
         );
-        println!("    https://github.com/{github_user}/rust/pull/new/{branch}");
+        println!(
+            // Open PR with `subtree-sync` label to satisfy the `no-merges` triagebot check
+            // See https://github.com/rust-lang/rust/pull/114157
+            "    https://github.com/rust-lang/rust/compare/{github_user}:{branch}?quick_pull=1&labels=subtree-sync"
+        );
 
         drop(josh);
         Ok(())
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index b40e7f83f4f..ab73f33497e 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-a989e25f1b87949a886eab3da10324d14189fe95
+366dab13f711df90a6891411458544199d159cbc
diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs
index 97718f1f4a9..1e9219d4bb2 100644
--- a/src/tools/miri/src/bin/miri.rs
+++ b/src/tools/miri/src/bin/miri.rs
@@ -59,7 +59,6 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
 
     fn after_analysis<'tcx>(
         &mut self,
-        handler: &EarlyErrorHandler,
         _: &rustc_interface::interface::Compiler,
         queries: &'tcx rustc_interface::Queries<'tcx>,
     ) -> Compilation {
@@ -68,7 +67,8 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
                 tcx.sess.fatal("miri cannot be run on programs that fail compilation");
             }
 
-            init_late_loggers(handler, tcx);
+            let handler = EarlyErrorHandler::new(tcx.sess.opts.error_format);
+            init_late_loggers(&handler, tcx);
             if !tcx.crate_types().contains(&CrateType::Executable) {
                 tcx.sess.fatal("miri only makes sense on bin crates");
             }
diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs
index 513cec1f6c3..073b8b6a661 100644
--- a/src/tools/miri/src/concurrency/data_race.rs
+++ b/src/tools/miri/src/concurrency/data_race.rs
@@ -1030,8 +1030,9 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
         // <https://github.com/rust-lang/miri/pull/2464#discussion_r939636130> for details.
         // We avoid `get_ptr_alloc` since we do *not* want to run the access hooks -- the actual
         // access will happen later.
-        let (alloc_id, _offset, _prov) =
-            this.ptr_try_get_alloc_id(place.ptr()).expect("there are no zero-sized atomic accesses");
+        let (alloc_id, _offset, _prov) = this
+            .ptr_try_get_alloc_id(place.ptr())
+            .expect("there are no zero-sized atomic accesses");
         if this.get_alloc_mutability(alloc_id)? == Mutability::Not {
             // FIXME: make this prettier, once these messages have separate title/span/help messages.
             throw_ub_format!(
diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs
index 295d86fd240..ef4e7df3aba 100644
--- a/src/tools/miri/src/concurrency/thread.rs
+++ b/src/tools/miri/src/concurrency/thread.rs
@@ -7,8 +7,8 @@ use std::sync::atomic::{AtomicBool, Ordering::Relaxed};
 use std::task::Poll;
 use std::time::{Duration, SystemTime};
 
-use log::trace;
 use either::Either;
+use log::trace;
 
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def_id::DefId;
diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs
index 4f249acda1d..cb095f94f35 100644
--- a/src/tools/miri/src/diagnostics.rs
+++ b/src/tools/miri/src/diagnostics.rs
@@ -199,6 +199,7 @@ pub fn report_error<'tcx, 'mir>(
     e: InterpErrorInfo<'tcx>,
 ) -> Option<(i64, bool)> {
     use InterpError::*;
+    use UndefinedBehaviorInfo::*;
 
     let mut msg = vec![];
 
@@ -271,7 +272,7 @@ pub fn report_error<'tcx, 'mir>(
         (title, helps)
     } else {
         let title = match e.kind() {
-            UndefinedBehavior(UndefinedBehaviorInfo::ValidationError(validation_err))
+            UndefinedBehavior(ValidationError(validation_err))
                 if matches!(
                     validation_err.kind,
                     ValidationErrorKind::PointerAsInt { .. } | ValidationErrorKind::PartialPointer
@@ -299,7 +300,7 @@ pub fn report_error<'tcx, 'mir>(
         let helps = match e.kind() {
             Unsupported(_) =>
                 vec![(None, format!("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support"))],
-            UndefinedBehavior(UndefinedBehaviorInfo::AlignmentCheckFailed { .. })
+            UndefinedBehavior(AlignmentCheckFailed { .. })
                 if ecx.machine.check_alignment == AlignmentCheck::Symbolic
             =>
                 vec![
@@ -311,13 +312,20 @@ pub fn report_error<'tcx, 'mir>(
                     (None, format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior")),
                     (None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information")),
                 ];
-                if let UndefinedBehaviorInfo::PointerUseAfterFree(alloc_id, _) | UndefinedBehaviorInfo::PointerOutOfBounds { alloc_id, .. } = info {
-                    if let Some(span) = ecx.machine.allocated_span(*alloc_id) {
-                        helps.push((Some(span), format!("{:?} was allocated here:", alloc_id)));
+                match info {
+                    PointerUseAfterFree(alloc_id, _) | PointerOutOfBounds { alloc_id, .. } => {
+                        if let Some(span) = ecx.machine.allocated_span(*alloc_id) {
+                            helps.push((Some(span), format!("{:?} was allocated here:", alloc_id)));
+                        }
+                        if let Some(span) = ecx.machine.deallocated_span(*alloc_id) {
+                            helps.push((Some(span), format!("{:?} was deallocated here:", alloc_id)));
+                        }
                     }
-                    if let Some(span) = ecx.machine.deallocated_span(*alloc_id) {
-                        helps.push((Some(span), format!("{:?} was deallocated here:", alloc_id)));
+                    AbiMismatchArgument { .. } | AbiMismatchReturn { .. } => {
+                        helps.push((None, format!("this means these two types are not *guaranteed* to be ABI-compatible across all targets")));
+                        helps.push((None, format!("if you think this code should be accepted anyway, please report an issue")));
                     }
+                    _ => {},
                 }
                 helps
             }
@@ -339,7 +347,7 @@ pub fn report_error<'tcx, 'mir>(
     // We want to dump the allocation if this is `InvalidUninitBytes`. Since `format_error` consumes `e`, we compute the outut early.
     let mut extra = String::new();
     match e.kind() {
-        UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some((alloc_id, access)))) => {
+        UndefinedBehavior(InvalidUninitBytes(Some((alloc_id, access)))) => {
             writeln!(
                 extra,
                 "Uninitialized memory occurred at {alloc_id:?}{range:?}, in this allocation:",
diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs
index beb13ebdfe6..3946ce8ef9d 100644
--- a/src/tools/miri/src/eval.rs
+++ b/src/tools/miri/src/eval.rs
@@ -382,7 +382,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
             .unwrap()
             .unwrap();
 
-            let main_ptr = ecx.create_fn_alloc_ptr(FnVal::Instance(entry_instance));
+            let main_ptr = ecx.fn_ptr(FnVal::Instance(entry_instance));
 
             // Inlining of `DEFAULT` from
             // https://github.com/rust-lang/rust/blob/master/compiler/rustc_session/src/config/sigpipe.rs.
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 80f83180448..ce7f47b5b4f 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -711,7 +711,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
                 let layout = this.machine.layouts.const_raw_ptr;
                 let dlsym = Dlsym::from_str("signal".as_bytes(), &this.tcx.sess.target.os)?
                     .expect("`signal` must be an actual dlsym on android");
-                let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym));
+                let ptr = this.fn_ptr(FnVal::Other(dlsym));
                 let val = ImmTy::from_scalar(Scalar::from_pointer(ptr, this), layout);
                 Self::alloc_extern_static(this, "signal", val)?;
                 // A couple zero-initialized pointer-sized extern statics.
diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs
index c4c9f77fab7..bfec4833ac9 100644
--- a/src/tools/miri/src/shims/backtrace.rs
+++ b/src/tools/miri/src/shims/backtrace.rs
@@ -63,7 +63,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 // to reconstruct the needed frame information in `handle_miri_resolve_frame`.
                 // Note that we never actually read or write anything from/to this pointer -
                 // all of the data is represented by the pointer value itself.
-                let fn_ptr = this.create_fn_alloc_ptr(FnVal::Instance(instance));
+                let fn_ptr = this.fn_ptr(FnVal::Instance(instance));
                 fn_ptr.wrapping_offset(Size::from_bytes(pos.0), this)
             })
             .collect();
@@ -159,7 +159,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
 
         // Reconstruct the original function pointer,
         // which we pass to user code.
-        let fn_ptr = this.create_fn_alloc_ptr(FnVal::Instance(fn_instance));
+        let fn_ptr = this.fn_ptr(FnVal::Instance(fn_instance));
 
         let num_fields = dest.layout.fields.count();
 
diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs
index 47cbd4419f3..0e8eaf450e4 100644
--- a/src/tools/miri/src/shims/foreign_items.rs
+++ b/src/tools/miri/src/shims/foreign_items.rs
@@ -1037,6 +1037,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     this, link_name, abi, args, dest,
                 );
             }
+            name if name.starts_with("llvm.x86.sse2.") => {
+                return shims::x86::sse2::EvalContextExt::emulate_x86_sse2_intrinsic(
+                    this, link_name, abi, args, dest,
+                );
+            }
 
             // Platform-specific shims
             _ =>
diff --git a/src/tools/miri/src/shims/os_str.rs b/src/tools/miri/src/shims/os_str.rs
index f6e76545a4d..62ce2ee58ae 100644
--- a/src/tools/miri/src/shims/os_str.rs
+++ b/src/tools/miri/src/shims/os_str.rs
@@ -159,8 +159,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
 
         let arg_type = Ty::new_array(this.tcx.tcx, this.tcx.types.u16, size);
         let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?;
-        let (written, _) =
-            self.write_os_str_to_wide_str(os_str, arg_place.ptr(), size, /*truncate*/ false).unwrap();
+        let (written, _) = self
+            .write_os_str_to_wide_str(os_str, arg_place.ptr(), size, /*truncate*/ false)
+            .unwrap();
         assert!(written);
         Ok(arg_place.ptr())
     }
diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs
index 865f01931f7..4bcca5076ca 100644
--- a/src/tools/miri/src/shims/unix/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/foreign_items.rs
@@ -232,7 +232,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 let symbol = this.read_pointer(symbol)?;
                 let symbol_name = this.read_c_str(symbol)?;
                 if let Some(dlsym) = Dlsym::from_str(symbol_name, &this.tcx.sess.target.os)? {
-                    let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym));
+                    let ptr = this.fn_ptr(FnVal::Other(dlsym));
                     this.write_pointer(ptr, dest)?;
                 } else {
                     this.write_null(dest)?;
diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs
index 3524fd70bcf..0ee3cea05d5 100644
--- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs
@@ -112,17 +112,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             // Access to command-line arguments
             "_NSGetArgc" => {
                 let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
-                this.write_pointer(
-                    this.machine.argc.expect("machine must be initialized"),
-                    dest,
-                )?;
+                this.write_pointer(this.machine.argc.expect("machine must be initialized"), dest)?;
             }
             "_NSGetArgv" => {
                 let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
-                this.write_pointer(
-                    this.machine.argv.expect("machine must be initialized"),
-                    dest,
-                )?;
+                this.write_pointer(this.machine.argv.expect("machine must be initialized"), dest)?;
             }
             "_NSGetExecutablePath" => {
                 let [buf, bufsize] =
diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs
index 28999268ba0..d76d01b0789 100644
--- a/src/tools/miri/src/shims/windows/foreign_items.rs
+++ b/src/tools/miri/src/shims/windows/foreign_items.rs
@@ -335,7 +335,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 this.read_target_isize(hModule)?;
                 let name = this.read_c_str(this.read_pointer(lpProcName)?)?;
                 if let Some(dlsym) = Dlsym::from_str(name, &this.tcx.sess.target.os)? {
-                    let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym));
+                    let ptr = this.fn_ptr(FnVal::Other(dlsym));
                     this.write_pointer(ptr, dest)?;
                 } else {
                     this.write_null(dest)?;
diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs
index 36e673129de..62f5eb1baf7 100644
--- a/src/tools/miri/src/shims/x86/mod.rs
+++ b/src/tools/miri/src/shims/x86/mod.rs
@@ -1 +1,45 @@
+use crate::InterpResult;
+
 pub(super) mod sse;
+pub(super) mod sse2;
+
+/// Floating point comparison operation
+///
+/// <https://www.felixcloutier.com/x86/cmpss>
+/// <https://www.felixcloutier.com/x86/cmpps>
+/// <https://www.felixcloutier.com/x86/cmpsd>
+/// <https://www.felixcloutier.com/x86/cmppd>
+#[derive(Copy, Clone)]
+enum FloatCmpOp {
+    Eq,
+    Lt,
+    Le,
+    Unord,
+    Neq,
+    /// Not less-than
+    Nlt,
+    /// Not less-or-equal
+    Nle,
+    /// Ordered, i.e. neither of them is NaN
+    Ord,
+}
+
+impl FloatCmpOp {
+    /// Convert from the `imm` argument used to specify the comparison
+    /// operation in intrinsics such as `llvm.x86.sse.cmp.ss`.
+    fn from_intrinsic_imm(imm: i8, intrinsic: &str) -> InterpResult<'_, Self> {
+        match imm {
+            0 => Ok(Self::Eq),
+            1 => Ok(Self::Lt),
+            2 => Ok(Self::Le),
+            3 => Ok(Self::Unord),
+            4 => Ok(Self::Neq),
+            5 => Ok(Self::Nlt),
+            6 => Ok(Self::Nle),
+            7 => Ok(Self::Ord),
+            imm => {
+                throw_unsup_format!("invalid `imm` parameter of {intrinsic}: {imm}");
+            }
+        }
+    }
+}
diff --git a/src/tools/miri/src/shims/x86/sse.rs b/src/tools/miri/src/shims/x86/sse.rs
index b18441bb408..ff4bd369706 100644
--- a/src/tools/miri/src/shims/x86/sse.rs
+++ b/src/tools/miri/src/shims/x86/sse.rs
@@ -5,6 +5,7 @@ use rustc_target::spec::abi::Abi;
 
 use rand::Rng as _;
 
+use super::FloatCmpOp;
 use crate::*;
 use shims::foreign_items::EmulateByNameResult;
 
@@ -78,7 +79,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
 
                 unary_op_ss(this, which, op, dest)?;
             }
-            // Used to implement _mm_{sqrt,rcp,rsqrt}_ss functions.
+            // Used to implement _mm_{sqrt,rcp,rsqrt}_ps functions.
             // Performs the operations on all components of `op`.
             "sqrt.ps" | "rcp.ps" | "rsqrt.ps" => {
                 let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
@@ -100,22 +101,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 let [left, right, imm] =
                     this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
 
-                let which = match this.read_scalar(imm)?.to_i8()? {
-                    0 => FloatBinOp::Cmp(FloatCmpOp::Eq),
-                    1 => FloatBinOp::Cmp(FloatCmpOp::Lt),
-                    2 => FloatBinOp::Cmp(FloatCmpOp::Le),
-                    3 => FloatBinOp::Cmp(FloatCmpOp::Unord),
-                    4 => FloatBinOp::Cmp(FloatCmpOp::Neq),
-                    5 => FloatBinOp::Cmp(FloatCmpOp::Nlt),
-                    6 => FloatBinOp::Cmp(FloatCmpOp::Nle),
-                    7 => FloatBinOp::Cmp(FloatCmpOp::Ord),
-                    imm => {
-                        throw_unsup_format!(
-                            "invalid 3rd parameter of llvm.x86.sse.cmp.ps: {}",
-                            imm
-                        );
-                    }
-                };
+                let which = FloatBinOp::Cmp(FloatCmpOp::from_intrinsic_imm(
+                    this.read_scalar(imm)?.to_i8()?,
+                    "llvm.x86.sse.cmp.ss",
+                )?);
 
                 bin_op_ss(this, which, left, right, dest)?;
             }
@@ -127,26 +116,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 let [left, right, imm] =
                     this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
 
-                let which = match this.read_scalar(imm)?.to_i8()? {
-                    0 => FloatBinOp::Cmp(FloatCmpOp::Eq),
-                    1 => FloatBinOp::Cmp(FloatCmpOp::Lt),
-                    2 => FloatBinOp::Cmp(FloatCmpOp::Le),
-                    3 => FloatBinOp::Cmp(FloatCmpOp::Unord),
-                    4 => FloatBinOp::Cmp(FloatCmpOp::Neq),
-                    5 => FloatBinOp::Cmp(FloatCmpOp::Nlt),
-                    6 => FloatBinOp::Cmp(FloatCmpOp::Nle),
-                    7 => FloatBinOp::Cmp(FloatCmpOp::Ord),
-                    imm => {
-                        throw_unsup_format!(
-                            "invalid 3rd parameter of llvm.x86.sse.cmp.ps: {}",
-                            imm
-                        );
-                    }
-                };
+                let which = FloatBinOp::Cmp(FloatCmpOp::from_intrinsic_imm(
+                    this.read_scalar(imm)?.to_i8()?,
+                    "llvm.x86.sse.cmp.ps",
+                )?);
 
                 bin_op_ps(this, which, left, right, dest)?;
             }
-            // Used to implement _mm_{,u}comi{eq,lt,le,gt,ge,neq}_ps functions.
+            // Used to implement _mm_{,u}comi{eq,lt,le,gt,ge,neq}_ss functions.
             // Compares the first component of `left` and `right` and returns
             // a scalar value (0 or 1).
             "comieq.ss" | "comilt.ss" | "comile.ss" | "comigt.ss" | "comige.ss" | "comineq.ss"
@@ -292,6 +269,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     let op = this.read_scalar(&this.project_index(&op, i)?)?;
                     let op = op.to_u32()?;
 
+                    // Extract the highest bit of `op` and place it in the `i`-th bit of `res`
                     res |= (op >> 31) << i;
                 }
 
@@ -303,25 +281,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     }
 }
 
-/// Floating point comparison operation
-///
-/// <https://www.felixcloutier.com/x86/cmpss>
-/// <https://www.felixcloutier.com/x86/cmpps>
-#[derive(Copy, Clone)]
-enum FloatCmpOp {
-    Eq,
-    Lt,
-    Le,
-    Unord,
-    Neq,
-    /// Not less-than
-    Nlt,
-    /// Not less-or-equal
-    Nle,
-    /// Ordered, i.e. neither of them is NaN
-    Ord,
-}
-
 #[derive(Copy, Clone)]
 enum FloatBinOp {
     /// Arithmetic operation
@@ -436,8 +395,8 @@ fn bin_op_ss<'tcx>(
     Ok(())
 }
 
-/// Performs `which` operation on each component of `left`, and
-/// `right` storing the result is stored in `dest`.
+/// Performs `which` operation on each component of `left` and
+/// `right`, storing the result is stored in `dest`.
 fn bin_op_ps<'tcx>(
     this: &mut crate::MiriInterpCx<'_, 'tcx>,
     which: FloatBinOp,
diff --git a/src/tools/miri/src/shims/x86/sse2.rs b/src/tools/miri/src/shims/x86/sse2.rs
new file mode 100644
index 00000000000..5b42339e648
--- /dev/null
+++ b/src/tools/miri/src/shims/x86/sse2.rs
@@ -0,0 +1,982 @@
+use rustc_apfloat::{
+    ieee::{Double, Single},
+    Float as _, FloatConvert as _,
+};
+use rustc_middle::ty::layout::LayoutOf as _;
+use rustc_middle::ty::Ty;
+use rustc_span::Symbol;
+use rustc_target::abi::Size;
+use rustc_target::spec::abi::Abi;
+
+use super::FloatCmpOp;
+use crate::*;
+use shims::foreign_items::EmulateByNameResult;
+
+impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
+pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
+    fn emulate_x86_sse2_intrinsic(
+        &mut self,
+        link_name: Symbol,
+        abi: Abi,
+        args: &[OpTy<'tcx, Provenance>],
+        dest: &PlaceTy<'tcx, Provenance>,
+    ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
+        let this = self.eval_context_mut();
+        // Prefix should have already been checked.
+        let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sse2.").unwrap();
+
+        // These intrinsics operate on 128-bit (f32x4, f64x2, i8x16, i16x8, i32x4, i64x2) SIMD
+        // vectors unless stated otherwise.
+        // Many intrinsic names are sufixed with "ps" (packed single), "ss" (scalar signle),
+        // "pd" (packed double) or "sd" (scalar double), where single means single precision
+        // floating point (f32) and double means double precision floating point (f64). "ps"
+        // and "pd" means thet the operation is performed on each element of the vector, while
+        // "ss" and "sd" means that the operation is performed only on the first element, copying
+        // the remaining elements from the input vector (for binary operations, from the left-hand
+        // side).
+        // Intrinsincs sufixed with "epiX" or "epuX" operate with X-bit signed or unsigned
+        // vectors.
+        match unprefixed_name {
+            // Used to implement the _mm_avg_epu8 function.
+            // Averages packed unsigned 8-bit integers in `left` and `right`.
+            "pavg.b" => {
+                let [left, right] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (left, left_len) = this.operand_to_simd(left)?;
+                let (right, right_len) = this.operand_to_simd(right)?;
+                let (dest, dest_len) = this.place_to_simd(dest)?;
+
+                assert_eq!(dest_len, left_len);
+                assert_eq!(dest_len, right_len);
+
+                for i in 0..dest_len {
+                    let left = this.read_scalar(&this.project_index(&left, i)?)?.to_u8()?;
+                    let right = this.read_scalar(&this.project_index(&right, i)?)?.to_u8()?;
+                    let dest = this.project_index(&dest, i)?;
+
+                    // Values are expanded from u8 to u16, so adds cannot overflow.
+                    let res = u16::from(left)
+                        .checked_add(u16::from(right))
+                        .unwrap()
+                        .checked_add(1)
+                        .unwrap()
+                        / 2;
+                    this.write_scalar(Scalar::from_u8(res.try_into().unwrap()), &dest)?;
+                }
+            }
+            // Used to implement the _mm_avg_epu16 function.
+            // Averages packed unsigned 16-bit integers in `left` and `right`.
+            "pavg.w" => {
+                let [left, right] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (left, left_len) = this.operand_to_simd(left)?;
+                let (right, right_len) = this.operand_to_simd(right)?;
+                let (dest, dest_len) = this.place_to_simd(dest)?;
+
+                assert_eq!(dest_len, left_len);
+                assert_eq!(dest_len, right_len);
+
+                for i in 0..dest_len {
+                    let left = this.read_scalar(&this.project_index(&left, i)?)?.to_u16()?;
+                    let right = this.read_scalar(&this.project_index(&right, i)?)?.to_u16()?;
+                    let dest = this.project_index(&dest, i)?;
+
+                    // Values are expanded from u16 to u32, so adds cannot overflow.
+                    let res = u32::from(left)
+                        .checked_add(u32::from(right))
+                        .unwrap()
+                        .checked_add(1)
+                        .unwrap()
+                        / 2;
+                    this.write_scalar(Scalar::from_u16(res.try_into().unwrap()), &dest)?;
+                }
+            }
+            // Used to implement the _mm_mulhi_epi16 function.
+            "pmulh.w" => {
+                let [left, right] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (left, left_len) = this.operand_to_simd(left)?;
+                let (right, right_len) = this.operand_to_simd(right)?;
+                let (dest, dest_len) = this.place_to_simd(dest)?;
+
+                assert_eq!(dest_len, left_len);
+                assert_eq!(dest_len, right_len);
+
+                for i in 0..dest_len {
+                    let left = this.read_scalar(&this.project_index(&left, i)?)?.to_i16()?;
+                    let right = this.read_scalar(&this.project_index(&right, i)?)?.to_i16()?;
+                    let dest = this.project_index(&dest, i)?;
+
+                    // Values are expanded from i16 to i32, so multiplication cannot overflow.
+                    let res = i32::from(left).checked_mul(i32::from(right)).unwrap() >> 16;
+                    this.write_scalar(Scalar::from_int(res, Size::from_bits(16)), &dest)?;
+                }
+            }
+            // Used to implement the _mm_mulhi_epu16 function.
+            "pmulhu.w" => {
+                let [left, right] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (left, left_len) = this.operand_to_simd(left)?;
+                let (right, right_len) = this.operand_to_simd(right)?;
+                let (dest, dest_len) = this.place_to_simd(dest)?;
+
+                assert_eq!(dest_len, left_len);
+                assert_eq!(dest_len, right_len);
+
+                for i in 0..dest_len {
+                    let left = this.read_scalar(&this.project_index(&left, i)?)?.to_u16()?;
+                    let right = this.read_scalar(&this.project_index(&right, i)?)?.to_u16()?;
+                    let dest = this.project_index(&dest, i)?;
+
+                    // Values are expanded from u16 to u32, so multiplication cannot overflow.
+                    let res = u32::from(left).checked_mul(u32::from(right)).unwrap() >> 16;
+                    this.write_scalar(Scalar::from_u16(res.try_into().unwrap()), &dest)?;
+                }
+            }
+            // Used to implement the _mm_mul_epu32 function.
+            // Multiplies the the low unsigned 32-bit integers from each packed
+            // 64-bit element and stores the result as 64-bit unsigned integers.
+            "pmulu.dq" => {
+                let [left, right] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (left, left_len) = this.operand_to_simd(left)?;
+                let (right, right_len) = this.operand_to_simd(right)?;
+                let (dest, dest_len) = this.place_to_simd(dest)?;
+
+                // left and right are u32x4, dest is u64x2
+                assert_eq!(left_len, 4);
+                assert_eq!(right_len, 4);
+                assert_eq!(dest_len, 2);
+
+                for i in 0..dest_len {
+                    let op_i = i.checked_mul(2).unwrap();
+                    let left = this.read_scalar(&this.project_index(&left, op_i)?)?.to_u32()?;
+                    let right = this.read_scalar(&this.project_index(&right, op_i)?)?.to_u32()?;
+                    let dest = this.project_index(&dest, i)?;
+
+                    // The multiplication will not overflow because stripping the
+                    // operands are expanded from 32-bit to 64-bit.
+                    let res = u64::from(left).checked_mul(u64::from(right)).unwrap();
+                    this.write_scalar(Scalar::from_u64(res), &dest)?;
+                }
+            }
+            // Used to implement the _mm_sad_epu8 function.
+            // Computes the absolute differences of packed unsigned 8-bit integers in `a`
+            // and `b`, then horizontally sum each consecutive 8 differences to produce
+            // two unsigned 16-bit integers, and pack these unsigned 16-bit integers in
+            // the low 16 bits of 64-bit elements returned.
+            //
+            // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sad_epu8
+            "psad.bw" => {
+                let [left, right] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (left, left_len) = this.operand_to_simd(left)?;
+                let (right, right_len) = this.operand_to_simd(right)?;
+                let (dest, dest_len) = this.place_to_simd(dest)?;
+
+                // left and right are u8x16, dest is u64x2
+                assert_eq!(left_len, right_len);
+                assert_eq!(left_len, 16);
+                assert_eq!(dest_len, 2);
+
+                for i in 0..dest_len {
+                    let dest = this.project_index(&dest, i)?;
+
+                    let mut res: u16 = 0;
+                    let n = left_len.checked_div(dest_len).unwrap();
+                    for j in 0..n {
+                        let op_i = j.checked_add(i.checked_mul(n).unwrap()).unwrap();
+                        let left = this.read_scalar(&this.project_index(&left, op_i)?)?.to_u8()?;
+                        let right =
+                            this.read_scalar(&this.project_index(&right, op_i)?)?.to_u8()?;
+
+                        res = res.checked_add(left.abs_diff(right).into()).unwrap();
+                    }
+
+                    this.write_scalar(Scalar::from_u64(res.into()), &dest)?;
+                }
+            }
+            // Used to implement the _mm_{sll,srl,sra}_epi16 functions.
+            // Shifts 16-bit packed integers in left by the amount in right.
+            // Both operands are vectors of 16-bit integers. However, right is
+            // interpreted as a single 64-bit integer (remaining bits are ignored).
+            // For logic shifts, when right is larger than 15, zero is produced.
+            // For arithmetic shifts, when right is larger than 15, the sign bit
+            // is copied to remaining bits.
+            "psll.w" | "psrl.w" | "psra.w" => {
+                let [left, right] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (left, left_len) = this.operand_to_simd(left)?;
+                let (right, right_len) = this.operand_to_simd(right)?;
+                let (dest, dest_len) = this.place_to_simd(dest)?;
+
+                assert_eq!(dest_len, left_len);
+                assert_eq!(dest_len, right_len);
+
+                enum ShiftOp {
+                    Sll,
+                    Srl,
+                    Sra,
+                }
+                let which = match unprefixed_name {
+                    "psll.w" => ShiftOp::Sll,
+                    "psrl.w" => ShiftOp::Srl,
+                    "psra.w" => ShiftOp::Sra,
+                    _ => unreachable!(),
+                };
+
+                // Get the 64-bit shift operand and convert it to the type expected
+                // by checked_{shl,shr} (u32).
+                // It is ok to saturate the value to u32::MAX because any value
+                // above 15 will produce the same result.
+                let shift = extract_first_u64(this, &right)?.try_into().unwrap_or(u32::MAX);
+
+                for i in 0..dest_len {
+                    let left = this.read_scalar(&this.project_index(&left, i)?)?.to_u16()?;
+                    let dest = this.project_index(&dest, i)?;
+
+                    let res = match which {
+                        ShiftOp::Sll => left.checked_shl(shift).unwrap_or(0),
+                        ShiftOp::Srl => left.checked_shr(shift).unwrap_or(0),
+                        #[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
+                        ShiftOp::Sra => {
+                            // Convert u16 to i16 to use arithmetic shift
+                            let left = left as i16;
+                            // Copy the sign bit to the remaining bits
+                            left.checked_shr(shift).unwrap_or(left >> 15) as u16
+                        }
+                    };
+
+                    this.write_scalar(Scalar::from_u16(res), &dest)?;
+                }
+            }
+            // Used to implement the _mm_{sll,srl,sra}_epi32 functions.
+            // 32-bit equivalent to the shift functions above.
+            "psll.d" | "psrl.d" | "psra.d" => {
+                let [left, right] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (left, left_len) = this.operand_to_simd(left)?;
+                let (right, right_len) = this.operand_to_simd(right)?;
+                let (dest, dest_len) = this.place_to_simd(dest)?;
+
+                assert_eq!(dest_len, left_len);
+                assert_eq!(dest_len, right_len);
+
+                enum ShiftOp {
+                    Sll,
+                    Srl,
+                    Sra,
+                }
+                let which = match unprefixed_name {
+                    "psll.d" => ShiftOp::Sll,
+                    "psrl.d" => ShiftOp::Srl,
+                    "psra.d" => ShiftOp::Sra,
+                    _ => unreachable!(),
+                };
+
+                // Get the 64-bit shift operand and convert it to the type expected
+                // by checked_{shl,shr} (u32).
+                // It is ok to saturate the value to u32::MAX because any value
+                // above 31 will produce the same result.
+                let shift = extract_first_u64(this, &right)?.try_into().unwrap_or(u32::MAX);
+
+                for i in 0..dest_len {
+                    let left = this.read_scalar(&this.project_index(&left, i)?)?.to_u32()?;
+                    let dest = this.project_index(&dest, i)?;
+
+                    let res = match which {
+                        ShiftOp::Sll => left.checked_shl(shift).unwrap_or(0),
+                        ShiftOp::Srl => left.checked_shr(shift).unwrap_or(0),
+                        #[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
+                        ShiftOp::Sra => {
+                            // Convert u32 to i32 to use arithmetic shift
+                            let left = left as i32;
+                            // Copy the sign bit to the remaining bits
+                            left.checked_shr(shift).unwrap_or(left >> 31) as u32
+                        }
+                    };
+
+                    this.write_scalar(Scalar::from_u32(res), &dest)?;
+                }
+            }
+            // Used to implement the _mm_{sll,srl}_epi64 functions.
+            // 64-bit equivalent to the shift functions above, except _mm_sra_epi64,
+            // which is not available in SSE2.
+            "psll.q" | "psrl.q" => {
+                let [left, right] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (left, left_len) = this.operand_to_simd(left)?;
+                let (right, right_len) = this.operand_to_simd(right)?;
+                let (dest, dest_len) = this.place_to_simd(dest)?;
+
+                assert_eq!(dest_len, left_len);
+                assert_eq!(dest_len, right_len);
+
+                enum ShiftOp {
+                    Sll,
+                    Srl,
+                }
+                let which = match unprefixed_name {
+                    "psll.q" => ShiftOp::Sll,
+                    "psrl.q" => ShiftOp::Srl,
+                    _ => unreachable!(),
+                };
+
+                // Get the 64-bit shift operand and convert it to the type expected
+                // by checked_{shl,shr} (u32).
+                // It is ok to saturate the value to u32::MAX because any value
+                // above 63 will produce the same result.
+                let shift = this
+                    .read_scalar(&this.project_index(&right, 0)?)?
+                    .to_u64()?
+                    .try_into()
+                    .unwrap_or(u32::MAX);
+
+                for i in 0..dest_len {
+                    let left = this.read_scalar(&this.project_index(&left, i)?)?.to_u64()?;
+                    let dest = this.project_index(&dest, i)?;
+
+                    let res = match which {
+                        ShiftOp::Sll => left.checked_shl(shift).unwrap_or(0),
+                        ShiftOp::Srl => left.checked_shr(shift).unwrap_or(0),
+                    };
+
+                    this.write_scalar(Scalar::from_u64(res), &dest)?;
+                }
+            }
+            // Used to implement the _mm_cvtepi32_ps function.
+            // Converts packed i32 to packed f32.
+            // FIXME: Can we get rid of this intrinsic and just use simd_as?
+            "cvtdq2ps" => {
+                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (op, op_len) = this.operand_to_simd(op)?;
+                let (dest, dest_len) = this.place_to_simd(dest)?;
+
+                assert_eq!(dest_len, op_len);
+
+                for i in 0..dest_len {
+                    let op = this.read_scalar(&this.project_index(&op, i)?)?.to_i32()?;
+                    let dest = this.project_index(&dest, i)?;
+
+                    let res = Scalar::from_f32(Single::from_i128(op.into()).value);
+                    this.write_scalar(res, &dest)?;
+                }
+            }
+            // Used to implement the _mm_cvtps_epi32 and _mm_cvttps_epi32 functions.
+            // Converts packed f32 to packed i32.
+            "cvtps2dq" | "cvttps2dq" => {
+                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (op, op_len) = this.operand_to_simd(op)?;
+                let (dest, dest_len) = this.place_to_simd(dest)?;
+
+                assert_eq!(dest_len, op_len);
+
+                let rnd = match unprefixed_name {
+                    // "current SSE rounding mode", assume nearest
+                    // https://www.felixcloutier.com/x86/cvtps2dq
+                    "cvtps2dq" => rustc_apfloat::Round::NearestTiesToEven,
+                    // always truncate
+                    // https://www.felixcloutier.com/x86/cvttps2dq
+                    "cvttps2dq" => rustc_apfloat::Round::TowardZero,
+                    _ => unreachable!(),
+                };
+
+                for i in 0..dest_len {
+                    let op = this.read_scalar(&this.project_index(&op, i)?)?.to_f32()?;
+                    let dest = this.project_index(&dest, i)?;
+
+                    let res =
+                        this.float_to_int_checked(op, dest.layout.ty, rnd).unwrap_or_else(|| {
+                            // Fallback to minimum acording to SSE2 semantics.
+                            Scalar::from_i32(i32::MIN)
+                        });
+                    this.write_scalar(res, &dest)?;
+                }
+            }
+            // Used to implement the _mm_packs_epi16 function.
+            // Converts two 16-bit integer vectors to a single 8-bit integer
+            // vector with signed saturation.
+            "packsswb.128" => {
+                let [left, right] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (left, left_len) = this.operand_to_simd(left)?;
+                let (right, right_len) = this.operand_to_simd(right)?;
+                let (dest, dest_len) = this.place_to_simd(dest)?;
+
+                // left and right are i16x8, dest is i8x16
+                assert_eq!(left_len, 8);
+                assert_eq!(right_len, 8);
+                assert_eq!(dest_len, 16);
+
+                for i in 0..left_len {
+                    let left = this.read_scalar(&this.project_index(&left, i)?)?.to_i16()?;
+                    let right = this.read_scalar(&this.project_index(&right, i)?)?.to_i16()?;
+                    let left_dest = this.project_index(&dest, i)?;
+                    let right_dest = this.project_index(&dest, i.checked_add(left_len).unwrap())?;
+
+                    let left_res =
+                        i8::try_from(left).unwrap_or(if left < 0 { i8::MIN } else { i8::MAX });
+                    let right_res =
+                        i8::try_from(right).unwrap_or(if right < 0 { i8::MIN } else { i8::MAX });
+
+                    this.write_scalar(Scalar::from_int(left_res, Size::from_bits(8)), &left_dest)?;
+                    this.write_scalar(
+                        Scalar::from_int(right_res, Size::from_bits(8)),
+                        &right_dest,
+                    )?;
+                }
+            }
+            // Used to implement the _mm_packus_epi16 function.
+            // Converts two 16-bit signed integer vectors to a single 8-bit
+            // unsigned integer vector with saturation.
+            "packuswb.128" => {
+                let [left, right] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (left, left_len) = this.operand_to_simd(left)?;
+                let (right, right_len) = this.operand_to_simd(right)?;
+                let (dest, dest_len) = this.place_to_simd(dest)?;
+
+                // left and right are i16x8, dest is u8x16
+                assert_eq!(left_len, 8);
+                assert_eq!(right_len, 8);
+                assert_eq!(dest_len, 16);
+
+                for i in 0..left_len {
+                    let left = this.read_scalar(&this.project_index(&left, i)?)?.to_i16()?;
+                    let right = this.read_scalar(&this.project_index(&right, i)?)?.to_i16()?;
+                    let left_dest = this.project_index(&dest, i)?;
+                    let right_dest = this.project_index(&dest, i.checked_add(left_len).unwrap())?;
+
+                    let left_res = u8::try_from(left).unwrap_or(if left < 0 { 0 } else { u8::MAX });
+                    let right_res =
+                        u8::try_from(right).unwrap_or(if right < 0 { 0 } else { u8::MAX });
+
+                    this.write_scalar(Scalar::from_u8(left_res), &left_dest)?;
+                    this.write_scalar(Scalar::from_u8(right_res), &right_dest)?;
+                }
+            }
+            // Used to implement the _mm_packs_epi32 function.
+            // Converts two 16-bit integer vectors to a single 8-bit integer
+            // vector with signed saturation.
+            "packssdw.128" => {
+                let [left, right] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (left, left_len) = this.operand_to_simd(left)?;
+                let (right, right_len) = this.operand_to_simd(right)?;
+                let (dest, dest_len) = this.place_to_simd(dest)?;
+
+                // left and right are i32x4, dest is i16x8
+                assert_eq!(left_len, 4);
+                assert_eq!(right_len, 4);
+                assert_eq!(dest_len, 8);
+
+                for i in 0..left_len {
+                    let left = this.read_scalar(&this.project_index(&left, i)?)?.to_i32()?;
+                    let right = this.read_scalar(&this.project_index(&right, i)?)?.to_i32()?;
+                    let left_dest = this.project_index(&dest, i)?;
+                    let right_dest = this.project_index(&dest, i.checked_add(left_len).unwrap())?;
+
+                    let left_res =
+                        i16::try_from(left).unwrap_or(if left < 0 { i16::MIN } else { i16::MAX });
+                    let right_res =
+                        i16::try_from(right).unwrap_or(if right < 0 { i16::MIN } else { i16::MAX });
+
+                    this.write_scalar(Scalar::from_int(left_res, Size::from_bits(16)), &left_dest)?;
+                    this.write_scalar(
+                        Scalar::from_int(right_res, Size::from_bits(16)),
+                        &right_dest,
+                    )?;
+                }
+            }
+            // Used to implement _mm_min_sd and _mm_max_sd functions.
+            // Note that the semantics are a bit different from Rust simd_min
+            // and simd_max intrinsics regarding handling of NaN and -0.0: Rust
+            // matches the IEEE min/max operations, while x86 has different
+            // semantics.
+            "min.sd" | "max.sd" => {
+                let [left, right] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let which = match unprefixed_name {
+                    "min.sd" => FloatBinOp::Min,
+                    "max.sd" => FloatBinOp::Max,
+                    _ => unreachable!(),
+                };
+
+                bin_op_sd(this, which, left, right, dest)?;
+            }
+            // Used to implement _mm_min_pd and _mm_max_pd functions.
+            // Note that the semantics are a bit different from Rust simd_min
+            // and simd_max intrinsics regarding handling of NaN and -0.0: Rust
+            // matches the IEEE min/max operations, while x86 has different
+            // semantics.
+            "min.pd" | "max.pd" => {
+                let [left, right] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let which = match unprefixed_name {
+                    "min.pd" => FloatBinOp::Min,
+                    "max.pd" => FloatBinOp::Max,
+                    _ => unreachable!(),
+                };
+
+                bin_op_pd(this, which, left, right, dest)?;
+            }
+            // Used to implement _mm_sqrt_sd functions.
+            // Performs the operations on the first component of `op` and
+            // copies the remaining components from `op`.
+            "sqrt.sd" => {
+                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (op, op_len) = this.operand_to_simd(op)?;
+                let (dest, dest_len) = this.place_to_simd(dest)?;
+
+                assert_eq!(dest_len, op_len);
+
+                let op0 = this.read_scalar(&this.project_index(&op, 0)?)?.to_u64()?;
+                // FIXME using host floats
+                let res0 = Scalar::from_u64(f64::from_bits(op0).sqrt().to_bits());
+                this.write_scalar(res0, &this.project_index(&dest, 0)?)?;
+
+                for i in 1..dest_len {
+                    this.copy_op(
+                        &this.project_index(&op, i)?,
+                        &this.project_index(&dest, i)?,
+                        /*allow_transmute*/ false,
+                    )?;
+                }
+            }
+            // Used to implement _mm_sqrt_pd functions.
+            // Performs the operations on all components of `op`.
+            "sqrt.pd" => {
+                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (op, op_len) = this.operand_to_simd(op)?;
+                let (dest, dest_len) = this.place_to_simd(dest)?;
+
+                assert_eq!(dest_len, op_len);
+
+                for i in 0..dest_len {
+                    let op = this.read_scalar(&this.project_index(&op, i)?)?.to_u64()?;
+                    let dest = this.project_index(&dest, i)?;
+
+                    // FIXME using host floats
+                    let res = Scalar::from_u64(f64::from_bits(op).sqrt().to_bits());
+
+                    this.write_scalar(res, &dest)?;
+                }
+            }
+            // Used to implement the _mm_cmp*_sd function.
+            // Performs a comparison operation on the first component of `left`
+            // and `right`, returning 0 if false or `u64::MAX` if true. The remaining
+            // components are copied from `left`.
+            "cmp.sd" => {
+                let [left, right, imm] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let which = FloatBinOp::Cmp(FloatCmpOp::from_intrinsic_imm(
+                    this.read_scalar(imm)?.to_i8()?,
+                    "llvm.x86.sse2.cmp.sd",
+                )?);
+
+                bin_op_sd(this, which, left, right, dest)?;
+            }
+            // Used to implement the _mm_cmp*_pd functions.
+            // Performs a comparison operation on each component of `left`
+            // and `right`. For each component, returns 0 if false or `u64::MAX`
+            // if true.
+            "cmp.pd" => {
+                let [left, right, imm] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let which = FloatBinOp::Cmp(FloatCmpOp::from_intrinsic_imm(
+                    this.read_scalar(imm)?.to_i8()?,
+                    "llvm.x86.sse2.cmp.pd",
+                )?);
+
+                bin_op_pd(this, which, left, right, dest)?;
+            }
+            // Used to implement _mm_{,u}comi{eq,lt,le,gt,ge,neq}_sd functions.
+            // Compares the first component of `left` and `right` and returns
+            // a scalar value (0 or 1).
+            "comieq.sd" | "comilt.sd" | "comile.sd" | "comigt.sd" | "comige.sd" | "comineq.sd"
+            | "ucomieq.sd" | "ucomilt.sd" | "ucomile.sd" | "ucomigt.sd" | "ucomige.sd"
+            | "ucomineq.sd" => {
+                let [left, right] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (left, left_len) = this.operand_to_simd(left)?;
+                let (right, right_len) = this.operand_to_simd(right)?;
+
+                assert_eq!(left_len, right_len);
+
+                let left = this.read_scalar(&this.project_index(&left, 0)?)?.to_f64()?;
+                let right = this.read_scalar(&this.project_index(&right, 0)?)?.to_f64()?;
+                // The difference between the com* and *ucom variants is signaling
+                // of exceptions when either argument is a quiet NaN. We do not
+                // support accessing the SSE status register from miri (or from Rust,
+                // for that matter), so we treat equally both variants.
+                let res = match unprefixed_name {
+                    "comieq.sd" | "ucomieq.sd" => left == right,
+                    "comilt.sd" | "ucomilt.sd" => left < right,
+                    "comile.sd" | "ucomile.sd" => left <= right,
+                    "comigt.sd" | "ucomigt.sd" => left > right,
+                    "comige.sd" | "ucomige.sd" => left >= right,
+                    "comineq.sd" | "ucomineq.sd" => left != right,
+                    _ => unreachable!(),
+                };
+                this.write_scalar(Scalar::from_i32(i32::from(res)), dest)?;
+            }
+            // Used to implement the _mm_cvtpd_ps function.
+            // Converts packed f32 to packed f64.
+            "cvtpd2ps" => {
+                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (op, op_len) = this.operand_to_simd(op)?;
+                let (dest, dest_len) = this.place_to_simd(dest)?;
+
+                // op is f64x2, dest is f32x4
+                assert_eq!(op_len, 2);
+                assert_eq!(dest_len, 4);
+
+                for i in 0..op_len {
+                    let op = this.read_scalar(&this.project_index(&op, i)?)?.to_f64()?;
+                    let dest = this.project_index(&dest, i)?;
+
+                    let res = op.convert(/*loses_info*/ &mut false).value;
+                    this.write_scalar(Scalar::from_f32(res), &dest)?;
+                }
+                // Fill the remaining with zeros
+                for i in op_len..dest_len {
+                    let dest = this.project_index(&dest, i)?;
+                    this.write_scalar(Scalar::from_u32(0), &dest)?;
+                }
+            }
+            // Used to implement the _mm_cvtps_pd function.
+            // Converts packed f64 to packed f32.
+            "cvtps2pd" => {
+                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (op, op_len) = this.operand_to_simd(op)?;
+                let (dest, dest_len) = this.place_to_simd(dest)?;
+
+                // op is f32x4, dest is f64x2
+                assert_eq!(op_len, 4);
+                assert_eq!(dest_len, 2);
+
+                for i in 0..dest_len {
+                    let op = this.read_scalar(&this.project_index(&op, i)?)?.to_f32()?;
+                    let dest = this.project_index(&dest, i)?;
+
+                    let res = op.convert(/*loses_info*/ &mut false).value;
+                    this.write_scalar(Scalar::from_f64(res), &dest)?;
+                }
+                // the two remaining f32 are ignored
+            }
+            // Used to implement the _mm_cvtpd_epi32 and _mm_cvttpd_epi32 functions.
+            // Converts packed f64 to packed i32.
+            "cvtpd2dq" | "cvttpd2dq" => {
+                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (op, op_len) = this.operand_to_simd(op)?;
+                let (dest, dest_len) = this.place_to_simd(dest)?;
+
+                // op is f64x2, dest is i32x4
+                assert_eq!(op_len, 2);
+                assert_eq!(dest_len, 4);
+
+                let rnd = match unprefixed_name {
+                    // "current SSE rounding mode", assume nearest
+                    // https://www.felixcloutier.com/x86/cvtpd2dq
+                    "cvtpd2dq" => rustc_apfloat::Round::NearestTiesToEven,
+                    // always truncate
+                    // https://www.felixcloutier.com/x86/cvttpd2dq
+                    "cvttpd2dq" => rustc_apfloat::Round::TowardZero,
+                    _ => unreachable!(),
+                };
+
+                for i in 0..op_len {
+                    let op = this.read_scalar(&this.project_index(&op, i)?)?.to_f64()?;
+                    let dest = this.project_index(&dest, i)?;
+
+                    let res =
+                        this.float_to_int_checked(op, dest.layout.ty, rnd).unwrap_or_else(|| {
+                            // Fallback to minimum acording to SSE2 semantics.
+                            Scalar::from_i32(i32::MIN)
+                        });
+                    this.write_scalar(res, &dest)?;
+                }
+                // Fill the remaining with zeros
+                for i in op_len..dest_len {
+                    let dest = this.project_index(&dest, i)?;
+                    this.write_scalar(Scalar::from_i32(0), &dest)?;
+                }
+            }
+            // Use to implement the _mm_cvtsd_si32 and _mm_cvttsd_si32 functions.
+            // Converts the first component of `op` from f64 to i32.
+            "cvtsd2si" | "cvttsd2si" => {
+                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let (op, _) = this.operand_to_simd(op)?;
+
+                let op = this.read_scalar(&this.project_index(&op, 0)?)?.to_f64()?;
+
+                let rnd = match unprefixed_name {
+                    // "current SSE rounding mode", assume nearest
+                    // https://www.felixcloutier.com/x86/cvtsd2si
+                    "cvtsd2si" => rustc_apfloat::Round::NearestTiesToEven,
+                    // always truncate
+                    // https://www.felixcloutier.com/x86/cvttsd2si
+                    "cvttsd2si" => rustc_apfloat::Round::TowardZero,
+                    _ => unreachable!(),
+                };
+
+                let res = this.float_to_int_checked(op, dest.layout.ty, rnd).unwrap_or_else(|| {
+                    // Fallback to minimum acording to SSE semantics.
+                    Scalar::from_i32(i32::MIN)
+                });
+
+                this.write_scalar(res, dest)?;
+            }
+            // Use to implement the _mm_cvtsd_si64 and _mm_cvttsd_si64 functions.
+            // Converts the first component of `op` from f64 to i64.
+            "cvtsd2si64" | "cvttsd2si64" => {
+                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let (op, _) = this.operand_to_simd(op)?;
+
+                let op = this.read_scalar(&this.project_index(&op, 0)?)?.to_f64()?;
+
+                let rnd = match unprefixed_name {
+                    // "current SSE rounding mode", assume nearest
+                    // https://www.felixcloutier.com/x86/cvtsd2si
+                    "cvtsd2si64" => rustc_apfloat::Round::NearestTiesToEven,
+                    // always truncate
+                    // https://www.felixcloutier.com/x86/cvttsd2si
+                    "cvttsd2si64" => rustc_apfloat::Round::TowardZero,
+                    _ => unreachable!(),
+                };
+
+                let res = this.float_to_int_checked(op, dest.layout.ty, rnd).unwrap_or_else(|| {
+                    // Fallback to minimum acording to SSE semantics.
+                    Scalar::from_i64(i64::MIN)
+                });
+
+                this.write_scalar(res, dest)?;
+            }
+            // Used to implement the _mm_cvtsd_ss and _mm_cvtss_sd functions.
+            // Converts the first f64/f32 from `right` to f32/f64 and copies
+            // the remaining elements from `left`
+            "cvtsd2ss" | "cvtss2sd" => {
+                let [left, right] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let (left, left_len) = this.operand_to_simd(left)?;
+                let (right, _) = this.operand_to_simd(right)?;
+                let (dest, dest_len) = this.place_to_simd(dest)?;
+
+                assert_eq!(dest_len, left_len);
+
+                // Convert first element of `right`
+                let right0 = this.read_immediate(&this.project_index(&right, 0)?)?;
+                let dest0 = this.project_index(&dest, 0)?;
+                // `float_to_float_or_int` here will convert from f64 to f32 (cvtsd2ss) or
+                // from f32 to f64 (cvtss2sd).
+                let res0 = this.float_to_float_or_int(&right0, dest0.layout.ty)?;
+                this.write_immediate(res0, &dest0)?;
+
+                // Copy remianing from `left`
+                for i in 1..dest_len {
+                    this.copy_op(
+                        &this.project_index(&left, i)?,
+                        &this.project_index(&dest, i)?,
+                        /*allow_transmute*/ false,
+                    )?;
+                }
+            }
+            // Used to implement the _mm_movemask_pd function.
+            // Returns a scalar integer where the i-th bit is the highest
+            // bit of the i-th component of `op`.
+            // https://www.felixcloutier.com/x86/movmskpd
+            "movmsk.pd" => {
+                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let (op, op_len) = this.operand_to_simd(op)?;
+
+                let mut res = 0;
+                for i in 0..op_len {
+                    let op = this.read_scalar(&this.project_index(&op, i)?)?;
+                    let op = op.to_u64()?;
+
+                    // Extract the highest bit of `op` and place it in the `i`-th bit of `res`
+                    res |= (op >> 63) << i;
+                }
+
+                this.write_scalar(Scalar::from_u32(res.try_into().unwrap()), dest)?;
+            }
+            _ => return Ok(EmulateByNameResult::NotSupported),
+        }
+        Ok(EmulateByNameResult::NeedsJumping)
+    }
+}
+
+/// Takes a 128-bit vector, transmutes it to `[u64; 2]` and extracts
+/// the first value.
+fn extract_first_u64<'tcx>(
+    this: &crate::MiriInterpCx<'_, 'tcx>,
+    op: &MPlaceTy<'tcx, Provenance>,
+) -> InterpResult<'tcx, u64> {
+    // Transmute vector to `[u64; 2]`
+    let u64_array_layout = this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u64, 2))?;
+    let op = op.transmute(u64_array_layout, this)?;
+
+    // Get the first u64 from the array
+    this.read_scalar(&this.project_index(&op, 0)?)?.to_u64()
+}
+
+#[derive(Copy, Clone)]
+enum FloatBinOp {
+    /// Comparison
+    Cmp(FloatCmpOp),
+    /// Minimum value (with SSE semantics)
+    ///
+    /// <https://www.felixcloutier.com/x86/minsd>
+    /// <https://www.felixcloutier.com/x86/minpd>
+    Min,
+    /// Maximum value (with SSE semantics)
+    ///
+    /// <https://www.felixcloutier.com/x86/maxsd>
+    /// <https://www.felixcloutier.com/x86/maxpd>
+    Max,
+}
+
+/// Performs `which` scalar operation on `left` and `right` and returns
+/// the result.
+// FIXME make this generic over apfloat type to reduce code duplicaton with bin_op_f32
+fn bin_op_f64<'tcx>(
+    which: FloatBinOp,
+    left: &ImmTy<'tcx, Provenance>,
+    right: &ImmTy<'tcx, Provenance>,
+) -> InterpResult<'tcx, Scalar<Provenance>> {
+    match which {
+        FloatBinOp::Cmp(which) => {
+            let left = left.to_scalar().to_f64()?;
+            let right = right.to_scalar().to_f64()?;
+            // FIXME: Make sure that these operations match the semantics of cmppd
+            let res = match which {
+                FloatCmpOp::Eq => left == right,
+                FloatCmpOp::Lt => left < right,
+                FloatCmpOp::Le => left <= right,
+                FloatCmpOp::Unord => left.is_nan() || right.is_nan(),
+                FloatCmpOp::Neq => left != right,
+                FloatCmpOp::Nlt => !(left < right),
+                FloatCmpOp::Nle => !(left <= right),
+                FloatCmpOp::Ord => !left.is_nan() && !right.is_nan(),
+            };
+            Ok(Scalar::from_u64(if res { u64::MAX } else { 0 }))
+        }
+        FloatBinOp::Min => {
+            let left = left.to_scalar().to_f64()?;
+            let right = right.to_scalar().to_f64()?;
+            // SSE semantics to handle zero and NaN. Note that `x == Single::ZERO`
+            // is true when `x` is either +0 or -0.
+            if (left == Double::ZERO && right == Double::ZERO)
+                || left.is_nan()
+                || right.is_nan()
+                || left >= right
+            {
+                Ok(Scalar::from_f64(right))
+            } else {
+                Ok(Scalar::from_f64(left))
+            }
+        }
+        FloatBinOp::Max => {
+            let left = left.to_scalar().to_f64()?;
+            let right = right.to_scalar().to_f64()?;
+            // SSE semantics to handle zero and NaN. Note that `x == Single::ZERO`
+            // is true when `x` is either +0 or -0.
+            if (left == Double::ZERO && right == Double::ZERO)
+                || left.is_nan()
+                || right.is_nan()
+                || left <= right
+            {
+                Ok(Scalar::from_f64(right))
+            } else {
+                Ok(Scalar::from_f64(left))
+            }
+        }
+    }
+}
+
+/// Performs `which` operation on the first component of `left` and `right`
+/// and copies the other components from `left`. The result is stored in `dest`.
+fn bin_op_sd<'tcx>(
+    this: &mut crate::MiriInterpCx<'_, 'tcx>,
+    which: FloatBinOp,
+    left: &OpTy<'tcx, Provenance>,
+    right: &OpTy<'tcx, Provenance>,
+    dest: &PlaceTy<'tcx, Provenance>,
+) -> InterpResult<'tcx, ()> {
+    let (left, left_len) = this.operand_to_simd(left)?;
+    let (right, right_len) = this.operand_to_simd(right)?;
+    let (dest, dest_len) = this.place_to_simd(dest)?;
+
+    assert_eq!(dest_len, left_len);
+    assert_eq!(dest_len, right_len);
+
+    let res0 = bin_op_f64(
+        which,
+        &this.read_immediate(&this.project_index(&left, 0)?)?,
+        &this.read_immediate(&this.project_index(&right, 0)?)?,
+    )?;
+    this.write_scalar(res0, &this.project_index(&dest, 0)?)?;
+
+    for i in 1..dest_len {
+        this.copy_op(
+            &this.project_index(&left, i)?,
+            &this.project_index(&dest, i)?,
+            /*allow_transmute*/ false,
+        )?;
+    }
+
+    Ok(())
+}
+
+/// Performs `which` operation on each component of `left` and
+/// `right`, storing the result is stored in `dest`.
+fn bin_op_pd<'tcx>(
+    this: &mut crate::MiriInterpCx<'_, 'tcx>,
+    which: FloatBinOp,
+    left: &OpTy<'tcx, Provenance>,
+    right: &OpTy<'tcx, Provenance>,
+    dest: &PlaceTy<'tcx, Provenance>,
+) -> InterpResult<'tcx, ()> {
+    let (left, left_len) = this.operand_to_simd(left)?;
+    let (right, right_len) = this.operand_to_simd(right)?;
+    let (dest, dest_len) = this.place_to_simd(dest)?;
+
+    assert_eq!(dest_len, left_len);
+    assert_eq!(dest_len, right_len);
+
+    for i in 0..dest_len {
+        let left = this.read_immediate(&this.project_index(&left, i)?)?;
+        let right = this.read_immediate(&this.project_index(&right, i)?)?;
+        let dest = this.project_index(&dest, i)?;
+
+        let res = bin_op_f64(which, &left, &right)?;
+        this.write_scalar(res, &dest)?;
+    }
+
+    Ok(())
+}
diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_array_vs_struct.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_array_vs_struct.stderr
index 50d4228c111..d1ccaf89974 100644
--- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_array_vs_struct.stderr
+++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_array_vs_struct.stderr
@@ -6,6 +6,8 @@ LL |     g(Default::default())
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
+   = help: if you think this code should be accepted anyway, please report an issue
    = note: BACKTRACE:
    = note: inside `main` at $DIR/abi_mismatch_array_vs_struct.rs:LL:CC
 
diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_int_vs_float.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_int_vs_float.stderr
index a53126c733e..3875c2617bb 100644
--- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_int_vs_float.stderr
+++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_int_vs_float.stderr
@@ -6,6 +6,8 @@ LL |     g(42)
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
+   = help: if you think this code should be accepted anyway, please report an issue
    = note: BACKTRACE:
    = note: inside `main` at $DIR/abi_mismatch_int_vs_float.rs:LL:CC
 
diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_raw_pointer.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_raw_pointer.stderr
index 6eacfeece14..6d1bdfee007 100644
--- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_raw_pointer.stderr
+++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_raw_pointer.stderr
@@ -6,6 +6,8 @@ LL |     g(&42 as *const i32)
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
+   = help: if you think this code should be accepted anyway, please report an issue
    = note: BACKTRACE:
    = note: inside `main` at $DIR/abi_mismatch_raw_pointer.rs:LL:CC
 
diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr
index eedc1235773..07d76c90e5e 100644
--- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr
+++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr
@@ -6,6 +6,8 @@ LL |     g()
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
+   = help: if you think this code should be accepted anyway, please report an issue
    = note: BACKTRACE:
    = note: inside `main` at $DIR/abi_mismatch_return_type.rs:LL:CC
 
diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_simple.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_simple.stderr
index bc500a90b77..7ac2bc2739f 100644
--- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_simple.stderr
+++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_simple.stderr
@@ -6,6 +6,8 @@ LL |     g(42)
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
+   = help: if you think this code should be accepted anyway, please report an issue
    = note: BACKTRACE:
    = note: inside `main` at $DIR/abi_mismatch_simple.rs:LL:CC
 
diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_vector.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_vector.stderr
index 7dcca1e85b8..e082eb9e3e3 100644
--- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_vector.stderr
+++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_vector.stderr
@@ -6,6 +6,8 @@ LL |     g(Default::default())
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
+   = help: if you think this code should be accepted anyway, please report an issue
    = note: BACKTRACE:
    = note: inside `main` at $DIR/abi_mismatch_vector.rs:LL:CC
 
diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr1.rs b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_arg.rs
index 6ab73569c63..6ab73569c63 100644
--- a/src/tools/miri/tests/fail/validity/cast_fn_ptr1.rs
+++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_arg.rs
diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr1.stderr b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_arg.stderr
index 133e4b2c16a..21e403b47f8 100644
--- a/src/tools/miri/tests/fail/validity/cast_fn_ptr1.stderr
+++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_arg.stderr
@@ -1,5 +1,5 @@
 error: Undefined Behavior: constructing invalid value: encountered a null reference
-  --> $DIR/cast_fn_ptr1.rs:LL:CC
+  --> $DIR/cast_fn_ptr_invalid_callee_arg.rs:LL:CC
    |
 LL |     g(0usize as *const i32)
    |     ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a null reference
@@ -7,7 +7,7 @@ LL |     g(0usize as *const i32)
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
    = note: BACKTRACE:
-   = note: inside `main` at $DIR/cast_fn_ptr1.rs:LL:CC
+   = note: inside `main` at $DIR/cast_fn_ptr_invalid_callee_arg.rs:LL:CC
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.rs b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.rs
new file mode 100644
index 00000000000..7cdc15c6094
--- /dev/null
+++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.rs
@@ -0,0 +1,28 @@
+#![allow(internal_features)]
+#![feature(core_intrinsics, custom_mir)]
+
+use std::intrinsics::mir::*;
+use std::num::NonZeroU32;
+use std::ptr;
+
+// This function supposedly returns a NonZeroU32, but actually returns something invalid in a way that
+// never materializes a bad NonZeroU32 value: we take a pointer to the return place and cast the pointer
+// type. That way we never get an "invalid value constructed" error inside the function, it can
+// only possibly be detected when the return value is passed to the caller.
+#[custom_mir(dialect = "runtime", phase = "optimized")]
+fn f() -> NonZeroU32 {
+    mir! {
+        {
+            let tmp = ptr::addr_of_mut!(RET);
+            let ptr = tmp as *mut u32;
+            *ptr = 0;
+            Return()
+        }
+    }
+}
+
+fn main() {
+    let f: fn() -> u32 = unsafe { std::mem::transmute(f as fn() -> NonZeroU32) };
+    // There's a NonZeroU32-to-u32 transmute happening here
+    f(); //~ERROR: expected something greater or equal to 1
+}
diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.stderr b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.stderr
new file mode 100644
index 00000000000..ccfb8890939
--- /dev/null
+++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: constructing invalid value: encountered 0, but expected something greater or equal to 1
+  --> $DIR/cast_fn_ptr_invalid_callee_ret.rs:LL:CC
+   |
+LL |     f();
+   |     ^^^ constructing invalid value: encountered 0, but expected something greater or equal to 1
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `main` at $DIR/cast_fn_ptr_invalid_callee_ret.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to previous error
+
diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.rs b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.rs
new file mode 100644
index 00000000000..ee80186d4b5
--- /dev/null
+++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.rs
@@ -0,0 +1,34 @@
+#![allow(internal_features)]
+#![feature(core_intrinsics, custom_mir)]
+
+use std::intrinsics::mir::*;
+use std::num::NonZeroU32;
+use std::ptr;
+
+fn f(c: u32) {
+    println!("{c}");
+}
+
+// Call that function in a bad way, with an invalid NonZeroU32, but without
+// ever materializing this as a NonZeroU32 value outside the call itself.
+#[custom_mir(dialect = "runtime", phase = "optimized")]
+fn call(f: fn(NonZeroU32)) {
+    mir! {
+        let _res: ();
+        {
+            let c = 0;
+            let tmp = ptr::addr_of!(c);
+            let ptr = tmp as *const NonZeroU32;
+            // The call site now is a NonZeroU32-to-u32 transmute.
+            Call(_res = f(*ptr), retblock) //~ERROR: expected something greater or equal to 1
+        }
+        retblock = {
+            Return()
+        }
+    }
+}
+
+fn main() {
+    let f: fn(NonZeroU32) = unsafe { std::mem::transmute(f as fn(u32)) };
+    call(f);
+}
diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.stderr b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.stderr
new file mode 100644
index 00000000000..234c2804008
--- /dev/null
+++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.stderr
@@ -0,0 +1,20 @@
+error: Undefined Behavior: constructing invalid value: encountered 0, but expected something greater or equal to 1
+  --> $DIR/cast_fn_ptr_invalid_caller_arg.rs:LL:CC
+   |
+LL |             Call(_res = f(*ptr), retblock)
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0, but expected something greater or equal to 1
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `call` at $DIR/cast_fn_ptr_invalid_caller_arg.rs:LL:CC
+note: inside `main`
+  --> $DIR/cast_fn_ptr_invalid_caller_arg.rs:LL:CC
+   |
+LL |     call(f);
+   |     ^^^^^^^
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to previous error
+
diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr2.rs b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_ret.rs
index 64ddb563be5..64ddb563be5 100644
--- a/src/tools/miri/tests/fail/validity/cast_fn_ptr2.rs
+++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_ret.rs
diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr2.stderr b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_ret.stderr
index 21001f2b460..bd9866acbd4 100644
--- a/src/tools/miri/tests/fail/validity/cast_fn_ptr2.stderr
+++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_ret.stderr
@@ -1,5 +1,5 @@
 error: Undefined Behavior: constructing invalid value: encountered a null reference
-  --> $DIR/cast_fn_ptr2.rs:LL:CC
+  --> $DIR/cast_fn_ptr_invalid_caller_ret.rs:LL:CC
    |
 LL |     let _x = g();
    |              ^^^ constructing invalid value: encountered a null reference
@@ -7,7 +7,7 @@ LL |     let _x = g();
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
    = note: BACKTRACE:
-   = note: inside `main` at $DIR/cast_fn_ptr2.rs:LL:CC
+   = note: inside `main` at $DIR/cast_fn_ptr_invalid_caller_ret.rs:LL:CC
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/pass/function_calls/abi_compat.rs b/src/tools/miri/tests/pass/function_calls/abi_compat.rs
index 08be29115ca..b24fe56cad6 100644
--- a/src/tools/miri/tests/pass/function_calls/abi_compat.rs
+++ b/src/tools/miri/tests/pass/function_calls/abi_compat.rs
@@ -1,10 +1,20 @@
 use std::mem;
 use std::num;
+use std::ptr;
+use std::rc::Rc;
 
 #[derive(Copy, Clone, Default)]
 struct Zst;
 
-fn test_abi_compat<T: Copy, U: Copy>(t: T, u: U) {
+#[repr(transparent)]
+#[derive(Copy, Clone)]
+struct Wrapper<T>(T);
+
+fn id<T>(x: T) -> T {
+    x
+}
+
+fn test_abi_compat<T: Clone, U: Clone>(t: T, u: U) {
     fn id<T>(x: T) -> T {
         x
     }
@@ -16,10 +26,10 @@ fn test_abi_compat<T: Copy, U: Copy>(t: T, u: U) {
     // in both directions.
     let f: fn(T) -> T = id;
     let f: fn(U) -> U = unsafe { std::mem::transmute(f) };
-    let _val = f(u);
+    let _val = f(u.clone());
     let f: fn(U) -> U = id;
     let f: fn(T) -> T = unsafe { std::mem::transmute(f) };
-    let _val = f(t);
+    let _val = f(t.clone());
 
     // And then we do the same for `extern "C"`.
     let f: extern "C" fn(T) -> T = id_c;
@@ -34,9 +44,6 @@ fn test_abi_compat<T: Copy, U: Copy>(t: T, u: U) {
 fn test_abi_newtype<T: Copy + Default>() {
     #[repr(transparent)]
     #[derive(Copy, Clone)]
-    struct Wrapper1<T>(T);
-    #[repr(transparent)]
-    #[derive(Copy, Clone)]
     struct Wrapper2<T>(T, ());
     #[repr(transparent)]
     #[derive(Copy, Clone)]
@@ -46,7 +53,7 @@ fn test_abi_newtype<T: Copy + Default>() {
     struct Wrapper3<T>(Zst, T, [u8; 0]);
 
     let t = T::default();
-    test_abi_compat(t, Wrapper1(t));
+    test_abi_compat(t, Wrapper(t));
     test_abi_compat(t, Wrapper2(t, ()));
     test_abi_compat(t, Wrapper2a((), t));
     test_abi_compat(t, Wrapper3(Zst, t, []));
@@ -54,23 +61,32 @@ fn test_abi_newtype<T: Copy + Default>() {
 }
 
 fn main() {
-    // Here we check:
-    // - u32 vs char is allowed
-    // - u32 vs NonZeroU32/Option<NonZeroU32> is allowed
-    // - reference vs raw pointer is allowed
-    // - references to things of the same size and alignment are allowed
-    // These are very basic tests that should work on all ABIs. However it is not clear that any of
-    // these would be stably guaranteed. Code that relies on this is equivalent to code that relies
-    // on the layout of `repr(Rust)` types. They are also fragile: the same mismatches in the fields
-    // of a struct (even with `repr(C)`) will not always be accepted by Miri.
-    // Note that `bool` and `u8` are *not* compatible, at least on x86-64!
-    // One of them has `arg_ext: Zext`, the other does not.
-    // Similarly, `i32` and `u32` are not compatible on s390x due to different `arg_ext`.
-    test_abi_compat(0u32, 'x');
+    // Here we check some of the guaranteed ABI compatibilities:
+    // - Different integer types of the same size and sign.
+    if cfg!(target_pointer_width = "32") {
+        test_abi_compat(0usize, 0u32);
+        test_abi_compat(0isize, 0i32);
+    } else {
+        test_abi_compat(0usize, 0u64);
+        test_abi_compat(0isize, 0i64);
+    }
     test_abi_compat(42u32, num::NonZeroU32::new(1).unwrap());
-    test_abi_compat(0u32, Some(num::NonZeroU32::new(1).unwrap()));
+    // - Reference/pointer types with the same pointee.
     test_abi_compat(&0u32, &0u32 as *const u32);
+    test_abi_compat(&mut 0u32 as *mut u32, Box::new(0u32));
+    test_abi_compat(&(), ptr::NonNull::<()>::dangling());
+    // - Reference/pointer types with different but sized pointees.
     test_abi_compat(&0u32, &([true; 4], [0u32; 0]));
+    // - `fn` types
+    test_abi_compat(main as fn(), id::<i32> as fn(i32) -> i32);
+    // - 1-ZST
+    test_abi_compat((), [0u8; 0]);
+    // - Guaranteed null-pointer-optimizations.
+    test_abi_compat(&0u32 as *const u32, Some(&0u32));
+    test_abi_compat(main as fn(), Some(main as fn()));
+    test_abi_compat(0u32, Some(num::NonZeroU32::new(1).unwrap()));
+    test_abi_compat(&0u32 as *const u32, Some(Wrapper(&0u32)));
+    test_abi_compat(0u32, Some(Wrapper(num::NonZeroU32::new(1).unwrap())));
 
     // These must work for *any* type, since we guarantee that `repr(transparent)` is ABI-compatible
     // with the wrapped field.
@@ -83,4 +99,11 @@ fn main() {
     test_abi_newtype::<[u32; 0]>();
     test_abi_newtype::<[u32; 2]>();
     test_abi_newtype::<[u32; 32]>();
+    test_abi_newtype::<Option<i32>>();
+    test_abi_newtype::<Option<num::NonZeroU32>>();
+
+    // Extra test for assumptions made by arbitrary-self-dyn-receivers.
+    let rc = Rc::new(0);
+    let rc_ptr: *mut i32 = unsafe { mem::transmute_copy(&rc) };
+    test_abi_compat(rc, rc_ptr);
 }
diff --git a/src/tools/miri/tests/pass/intrinsics-x86-sse.rs b/src/tools/miri/tests/pass/intrinsics-x86-sse.rs
index 677d7cc030e..9b1ded94b5d 100644
--- a/src/tools/miri/tests/pass/intrinsics-x86-sse.rs
+++ b/src/tools/miri/tests/pass/intrinsics-x86-sse.rs
@@ -1,5 +1,15 @@
-//@only-target-x86_64
-
+// Ignore everything except x86 and x86_64
+// Any additional target are added to CI should be ignored here
+//@ignore-target-aarch64
+//@ignore-target-arm
+//@ignore-target-avr
+//@ignore-target-s390x
+//@ignore-target-thumbv7em
+//@ignore-target-wasm32
+
+#[cfg(target_arch = "x86")]
+use std::arch::x86::*;
+#[cfg(target_arch = "x86_64")]
 use std::arch::x86_64::*;
 use std::f32::NAN;
 use std::mem::transmute;
@@ -987,6 +997,8 @@ unsafe fn test_sse() {
     }
     test_mm_cvtsi32_ss();
 
+    // Intrinsic only available on x86_64
+    #[cfg(target_arch = "x86_64")]
     #[target_feature(enable = "sse")]
     unsafe fn test_mm_cvtss_si64() {
         let inputs = &[
@@ -1007,8 +1019,11 @@ unsafe fn test_sse() {
             assert_eq!(e, r, "TestCase #{} _mm_cvtss_si64({:?}) = {}, expected: {}", i, x, r, e);
         }
     }
+    #[cfg(target_arch = "x86_64")]
     test_mm_cvtss_si64();
 
+    // Intrinsic only available on x86_64
+    #[cfg(target_arch = "x86_64")]
     #[target_feature(enable = "sse")]
     unsafe fn test_mm_cvttss_si64() {
         let inputs = &[
@@ -1032,8 +1047,11 @@ unsafe fn test_sse() {
             assert_eq!(e, r, "TestCase #{} _mm_cvttss_si64({:?}) = {}, expected: {}", i, x, r, e);
         }
     }
+    #[cfg(target_arch = "x86_64")]
     test_mm_cvttss_si64();
 
+    // Intrinsic only available on x86_64
+    #[cfg(target_arch = "x86_64")]
     #[target_feature(enable = "sse")]
     unsafe fn test_mm_cvtsi64_ss() {
         let inputs = &[
@@ -1053,6 +1071,7 @@ unsafe fn test_sse() {
             assert_eq_m128(e, r);
         }
     }
+    #[cfg(target_arch = "x86_64")]
     test_mm_cvtsi64_ss();
 
     #[target_feature(enable = "sse")]
diff --git a/src/tools/miri/tests/pass/intrinsics-x86-sse2.rs b/src/tools/miri/tests/pass/intrinsics-x86-sse2.rs
new file mode 100644
index 00000000000..1b55a94783a
--- /dev/null
+++ b/src/tools/miri/tests/pass/intrinsics-x86-sse2.rs
@@ -0,0 +1,828 @@
+// Ignore everything except x86 and x86_64
+// Any additional target are added to CI should be ignored here
+//@ignore-target-aarch64
+//@ignore-target-arm
+//@ignore-target-avr
+//@ignore-target-s390x
+//@ignore-target-thumbv7em
+//@ignore-target-wasm32
+
+#[cfg(target_arch = "x86")]
+use std::arch::x86::*;
+#[cfg(target_arch = "x86_64")]
+use std::arch::x86_64::*;
+use std::f64::NAN;
+use std::mem::transmute;
+
+fn main() {
+    assert!(is_x86_feature_detected!("sse2"));
+
+    unsafe {
+        test_sse2();
+    }
+}
+
+#[target_feature(enable = "sse2")]
+unsafe fn _mm_setr_epi64x(a: i64, b: i64) -> __m128i {
+    _mm_set_epi64x(b, a)
+}
+
+#[target_feature(enable = "sse2")]
+unsafe fn test_sse2() {
+    // Mostly copied from library/stdarch/crates/core_arch/src/x86{,_64}/sse2.rs
+
+    unsafe fn _mm_setr_epi64x(a: i64, b: i64) -> __m128i {
+        _mm_set_epi64x(b, a)
+    }
+
+    #[track_caller]
+    #[target_feature(enable = "sse")]
+    unsafe fn assert_eq_m128(a: __m128, b: __m128) {
+        let r = _mm_cmpeq_ps(a, b);
+        if _mm_movemask_ps(r) != 0b1111 {
+            panic!("{:?} != {:?}", a, b);
+        }
+    }
+
+    #[track_caller]
+    #[target_feature(enable = "sse2")]
+    unsafe fn assert_eq_m128i(a: __m128i, b: __m128i) {
+        assert_eq!(transmute::<_, [u64; 2]>(a), transmute::<_, [u64; 2]>(b))
+    }
+
+    #[track_caller]
+    #[target_feature(enable = "sse2")]
+    unsafe fn assert_eq_m128d(a: __m128d, b: __m128d) {
+        if _mm_movemask_pd(_mm_cmpeq_pd(a, b)) != 0b11 {
+            panic!("{:?} != {:?}", a, b);
+        }
+    }
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_avg_epu8() {
+        let (a, b) = (_mm_set1_epi8(3), _mm_set1_epi8(9));
+        let r = _mm_avg_epu8(a, b);
+        assert_eq_m128i(r, _mm_set1_epi8(6));
+    }
+    test_mm_avg_epu8();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_avg_epu16() {
+        let (a, b) = (_mm_set1_epi16(3), _mm_set1_epi16(9));
+        let r = _mm_avg_epu16(a, b);
+        assert_eq_m128i(r, _mm_set1_epi16(6));
+    }
+    test_mm_avg_epu16();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_mulhi_epi16() {
+        let (a, b) = (_mm_set1_epi16(1000), _mm_set1_epi16(-1001));
+        let r = _mm_mulhi_epi16(a, b);
+        assert_eq_m128i(r, _mm_set1_epi16(-16));
+    }
+    test_mm_mulhi_epi16();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_mulhi_epu16() {
+        let (a, b) = (_mm_set1_epi16(1000), _mm_set1_epi16(1001));
+        let r = _mm_mulhi_epu16(a, b);
+        assert_eq_m128i(r, _mm_set1_epi16(15));
+    }
+    test_mm_mulhi_epu16();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_mul_epu32() {
+        let a = _mm_setr_epi64x(1_000_000_000, 1 << 34);
+        let b = _mm_setr_epi64x(1_000_000_000, 1 << 35);
+        let r = _mm_mul_epu32(a, b);
+        let e = _mm_setr_epi64x(1_000_000_000 * 1_000_000_000, 0);
+        assert_eq_m128i(r, e);
+    }
+    test_mm_mul_epu32();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_sad_epu8() {
+        #[rustfmt::skip]
+        let a = _mm_setr_epi8(
+            255u8 as i8, 254u8 as i8, 253u8 as i8, 252u8 as i8,
+            1, 2, 3, 4,
+            155u8 as i8, 154u8 as i8, 153u8 as i8, 152u8 as i8,
+            1, 2, 3, 4,
+        );
+        let b = _mm_setr_epi8(0, 0, 0, 0, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2);
+        let r = _mm_sad_epu8(a, b);
+        let e = _mm_setr_epi64x(1020, 614);
+        assert_eq_m128i(r, e);
+    }
+    test_mm_sad_epu8();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_sll_epi16() {
+        let a = _mm_setr_epi16(0xCC, -0xCC, 0xDD, -0xDD, 0xEE, -0xEE, 0xFF, -0xFF);
+        let r = _mm_sll_epi16(a, _mm_set_epi64x(0, 4));
+        assert_eq_m128i(
+            r,
+            _mm_setr_epi16(0xCC0, -0xCC0, 0xDD0, -0xDD0, 0xEE0, -0xEE0, 0xFF0, -0xFF0),
+        );
+        let r = _mm_sll_epi16(a, _mm_set_epi64x(4, 0));
+        assert_eq_m128i(r, a);
+        let r = _mm_sll_epi16(a, _mm_set_epi64x(0, 16));
+        assert_eq_m128i(r, _mm_set1_epi16(0));
+        let r = _mm_sll_epi16(a, _mm_set_epi64x(0, i64::MAX));
+        assert_eq_m128i(r, _mm_set1_epi16(0));
+    }
+    test_mm_sll_epi16();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_srl_epi16() {
+        let a = _mm_setr_epi16(0xCC, -0xCC, 0xDD, -0xDD, 0xEE, -0xEE, 0xFF, -0xFF);
+        let r = _mm_srl_epi16(a, _mm_set_epi64x(0, 4));
+        assert_eq_m128i(r, _mm_setr_epi16(0xC, 0xFF3, 0xD, 0xFF2, 0xE, 0xFF1, 0xF, 0xFF0));
+        let r = _mm_srl_epi16(a, _mm_set_epi64x(4, 0));
+        assert_eq_m128i(r, a);
+        let r = _mm_srl_epi16(a, _mm_set_epi64x(0, 16));
+        assert_eq_m128i(r, _mm_set1_epi16(0));
+        let r = _mm_srl_epi16(a, _mm_set_epi64x(0, i64::MAX));
+        assert_eq_m128i(r, _mm_set1_epi16(0));
+    }
+    test_mm_srl_epi16();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_sra_epi16() {
+        let a = _mm_setr_epi16(0xCC, -0xCC, 0xDD, -0xDD, 0xEE, -0xEE, 0xFF, -0xFF);
+        let r = _mm_sra_epi16(a, _mm_set_epi64x(0, 4));
+        assert_eq_m128i(r, _mm_setr_epi16(0xC, -0xD, 0xD, -0xE, 0xE, -0xF, 0xF, -0x10));
+        let r = _mm_sra_epi16(a, _mm_set_epi64x(4, 0));
+        assert_eq_m128i(r, a);
+        let r = _mm_sra_epi16(a, _mm_set_epi64x(0, 16));
+        assert_eq_m128i(r, _mm_setr_epi16(0, -1, 0, -1, 0, -1, 0, -1));
+        let r = _mm_sra_epi16(a, _mm_set_epi64x(0, i64::MAX));
+        assert_eq_m128i(r, _mm_setr_epi16(0, -1, 0, -1, 0, -1, 0, -1));
+    }
+    test_mm_sra_epi16();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_sll_epi32() {
+        let a = _mm_setr_epi32(0xEEEE, -0xEEEE, 0xFFFF, -0xFFFF);
+        let r = _mm_sll_epi32(a, _mm_set_epi64x(0, 4));
+        assert_eq_m128i(r, _mm_setr_epi32(0xEEEE0, -0xEEEE0, 0xFFFF0, -0xFFFF0));
+        let r = _mm_sll_epi32(a, _mm_set_epi64x(4, 0));
+        assert_eq_m128i(r, a);
+        let r = _mm_sll_epi32(a, _mm_set_epi64x(0, 32));
+        assert_eq_m128i(r, _mm_set1_epi32(0));
+        let r = _mm_sll_epi32(a, _mm_set_epi64x(0, i64::MAX));
+        assert_eq_m128i(r, _mm_set1_epi32(0));
+    }
+    test_mm_sll_epi32();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_srl_epi32() {
+        let a = _mm_setr_epi32(0xEEEE, -0xEEEE, 0xFFFF, -0xFFFF);
+        let r = _mm_srl_epi32(a, _mm_set_epi64x(0, 4));
+        assert_eq_m128i(r, _mm_setr_epi32(0xEEE, 0xFFFF111, 0xFFF, 0xFFFF000));
+        let r = _mm_srl_epi32(a, _mm_set_epi64x(4, 0));
+        assert_eq_m128i(r, a);
+        let r = _mm_srl_epi32(a, _mm_set_epi64x(0, 32));
+        assert_eq_m128i(r, _mm_set1_epi32(0));
+        let r = _mm_srl_epi32(a, _mm_set_epi64x(0, i64::MAX));
+        assert_eq_m128i(r, _mm_set1_epi32(0));
+    }
+    test_mm_srl_epi32();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_sra_epi32() {
+        let a = _mm_setr_epi32(0xEEEE, -0xEEEE, 0xFFFF, -0xFFFF);
+        let r = _mm_sra_epi32(a, _mm_set_epi64x(0, 4));
+        assert_eq_m128i(r, _mm_setr_epi32(0xEEE, -0xEEF, 0xFFF, -0x1000));
+        let r = _mm_sra_epi32(a, _mm_set_epi64x(4, 0));
+        assert_eq_m128i(r, a);
+        let r = _mm_sra_epi32(a, _mm_set_epi64x(0, 32));
+        assert_eq_m128i(r, _mm_setr_epi32(0, -1, 0, -1));
+        let r = _mm_sra_epi32(a, _mm_set_epi64x(0, i64::MAX));
+        assert_eq_m128i(r, _mm_setr_epi32(0, -1, 0, -1));
+    }
+    test_mm_sra_epi32();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_sll_epi64() {
+        let a = _mm_set_epi64x(0xFFFFFFFF, -0xFFFFFFFF);
+        let r = _mm_sll_epi64(a, _mm_set_epi64x(0, 4));
+        assert_eq_m128i(r, _mm_set_epi64x(0xFFFFFFFF0, -0xFFFFFFFF0));
+        let r = _mm_sll_epi64(a, _mm_set_epi64x(4, 0));
+        assert_eq_m128i(r, a);
+        let r = _mm_sll_epi64(a, _mm_set_epi64x(0, 64));
+        assert_eq_m128i(r, _mm_set1_epi64x(0));
+        let r = _mm_sll_epi64(a, _mm_set_epi64x(0, i64::MAX));
+        assert_eq_m128i(r, _mm_set1_epi64x(0));
+    }
+    test_mm_sll_epi64();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_srl_epi64() {
+        let a = _mm_set_epi64x(0xFFFFFFFF, -0xFFFFFFFF);
+        let r = _mm_srl_epi64(a, _mm_set_epi64x(0, 4));
+        assert_eq_m128i(r, _mm_set_epi64x(0xFFFFFFF, 0xFFFFFFFF0000000));
+        let r = _mm_srl_epi64(a, _mm_set_epi64x(4, 0));
+        assert_eq_m128i(r, a);
+        let r = _mm_srl_epi64(a, _mm_set_epi64x(0, 64));
+        assert_eq_m128i(r, _mm_set1_epi64x(0));
+        let r = _mm_srl_epi64(a, _mm_set_epi64x(0, i64::MAX));
+        assert_eq_m128i(r, _mm_set1_epi64x(0));
+    }
+    test_mm_srl_epi64();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cvtepi32_ps() {
+        let a = _mm_setr_epi32(1, 2, 3, 4);
+        let r = _mm_cvtepi32_ps(a);
+        assert_eq_m128(r, _mm_setr_ps(1.0, 2.0, 3.0, 4.0));
+    }
+    test_mm_cvtepi32_ps();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cvtps_epi32() {
+        let a = _mm_setr_ps(1.0, 2.0, 3.0, 4.0);
+        let r = _mm_cvtps_epi32(a);
+        assert_eq_m128i(r, _mm_setr_epi32(1, 2, 3, 4));
+    }
+    test_mm_cvtps_epi32();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cvttps_epi32() {
+        let a = _mm_setr_ps(-1.1, 2.2, -3.3, 6.6);
+        let r = _mm_cvttps_epi32(a);
+        assert_eq_m128i(r, _mm_setr_epi32(-1, 2, -3, 6));
+
+        let a = _mm_setr_ps(f32::NEG_INFINITY, f32::INFINITY, f32::MIN, f32::MAX);
+        let r = _mm_cvttps_epi32(a);
+        assert_eq_m128i(r, _mm_setr_epi32(i32::MIN, i32::MIN, i32::MIN, i32::MIN));
+    }
+    test_mm_cvttps_epi32();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_packs_epi16() {
+        let a = _mm_setr_epi16(0x80, -0x81, 0, 0, 0, 0, 0, 0);
+        let b = _mm_setr_epi16(0, 0, 0, 0, 0, 0, -0x81, 0x80);
+        let r = _mm_packs_epi16(a, b);
+        assert_eq_m128i(
+            r,
+            _mm_setr_epi8(0x7F, -0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0x80, 0x7F),
+        );
+    }
+    test_mm_packs_epi16();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_packus_epi16() {
+        let a = _mm_setr_epi16(0x100, -1, 0, 0, 0, 0, 0, 0);
+        let b = _mm_setr_epi16(0, 0, 0, 0, 0, 0, -1, 0x100);
+        let r = _mm_packus_epi16(a, b);
+        assert_eq_m128i(r, _mm_setr_epi8(!0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, !0));
+    }
+    test_mm_packus_epi16();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_packs_epi32() {
+        let a = _mm_setr_epi32(0x8000, -0x8001, 0, 0);
+        let b = _mm_setr_epi32(0, 0, -0x8001, 0x8000);
+        let r = _mm_packs_epi32(a, b);
+        assert_eq_m128i(r, _mm_setr_epi16(0x7FFF, -0x8000, 0, 0, 0, 0, -0x8000, 0x7FFF));
+    }
+    test_mm_packs_epi32();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_min_sd() {
+        let a = _mm_setr_pd(1.0, 2.0);
+        let b = _mm_setr_pd(5.0, 10.0);
+        let r = _mm_min_sd(a, b);
+        assert_eq_m128d(r, _mm_setr_pd(1.0, 2.0));
+    }
+    test_mm_min_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_min_pd() {
+        let a = _mm_setr_pd(-1.0, 5.0);
+        let b = _mm_setr_pd(-100.0, 20.0);
+        let r = _mm_min_pd(a, b);
+        assert_eq_m128d(r, _mm_setr_pd(-100.0, 5.0));
+
+        // `_mm_min_pd` can **not** be implemented using the `simd_min` rust intrinsic because
+        // the semantics of `simd_min` are different to those of `_mm_min_pd` regarding handling
+        // of `-0.0`.
+        let a = _mm_setr_pd(-0.0, 0.0);
+        let b = _mm_setr_pd(0.0, 0.0);
+        let r1: [u8; 16] = transmute(_mm_min_pd(a, b));
+        let r2: [u8; 16] = transmute(_mm_min_pd(b, a));
+        let a: [u8; 16] = transmute(a);
+        let b: [u8; 16] = transmute(b);
+        assert_eq!(r1, b);
+        assert_eq!(r2, a);
+        assert_ne!(a, b); // sanity check that -0.0 is actually present
+    }
+    test_mm_min_pd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_max_sd() {
+        let a = _mm_setr_pd(1.0, 2.0);
+        let b = _mm_setr_pd(5.0, 10.0);
+        let r = _mm_max_sd(a, b);
+        assert_eq_m128d(r, _mm_setr_pd(5.0, 2.0));
+    }
+    test_mm_max_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_max_pd() {
+        let a = _mm_setr_pd(-1.0, 5.0);
+        let b = _mm_setr_pd(-100.0, 20.0);
+        let r = _mm_max_pd(a, b);
+        assert_eq_m128d(r, _mm_setr_pd(-1.0, 20.0));
+
+        // `_mm_max_pd` can **not** be implemented using the `simd_max` rust intrinsic because
+        // the semantics of `simd_max` are different to those of `_mm_max_pd` regarding handling
+        // of `-0.0`.
+        let a = _mm_setr_pd(-0.0, 0.0);
+        let b = _mm_setr_pd(0.0, 0.0);
+        let r1: [u8; 16] = transmute(_mm_max_pd(a, b));
+        let r2: [u8; 16] = transmute(_mm_max_pd(b, a));
+        let a: [u8; 16] = transmute(a);
+        let b: [u8; 16] = transmute(b);
+        assert_eq!(r1, b);
+        assert_eq!(r2, a);
+        assert_ne!(a, b); // sanity check that -0.0 is actually present
+    }
+    test_mm_max_pd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_sqrt_sd() {
+        let a = _mm_setr_pd(1.0, 2.0);
+        let b = _mm_setr_pd(5.0, 10.0);
+        let r = _mm_sqrt_sd(a, b);
+        assert_eq_m128d(r, _mm_setr_pd(5.0f64.sqrt(), 2.0));
+    }
+    test_mm_sqrt_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_sqrt_pd() {
+        let r = _mm_sqrt_pd(_mm_setr_pd(1.0, 2.0));
+        assert_eq_m128d(r, _mm_setr_pd(1.0f64.sqrt(), 2.0f64.sqrt()));
+    }
+    test_mm_sqrt_pd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmpeq_sd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        let e = _mm_setr_epi64x(!0, transmute(2.0f64));
+        let r = transmute::<_, __m128i>(_mm_cmpeq_sd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmpeq_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmplt_sd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(5.0, 3.0));
+        let e = _mm_setr_epi64x(!0, transmute(2.0f64));
+        let r = transmute::<_, __m128i>(_mm_cmplt_sd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmplt_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmple_sd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        let e = _mm_setr_epi64x(!0, transmute(2.0f64));
+        let r = transmute::<_, __m128i>(_mm_cmple_sd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmple_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmpgt_sd() {
+        let (a, b) = (_mm_setr_pd(5.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        let e = _mm_setr_epi64x(!0, transmute(2.0f64));
+        let r = transmute::<_, __m128i>(_mm_cmpgt_sd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmpgt_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmpge_sd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        let e = _mm_setr_epi64x(!0, transmute(2.0f64));
+        let r = transmute::<_, __m128i>(_mm_cmpge_sd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmpge_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmpord_sd() {
+        let (a, b) = (_mm_setr_pd(NAN, 2.0), _mm_setr_pd(5.0, 3.0));
+        let e = _mm_setr_epi64x(0, transmute(2.0f64));
+        let r = transmute::<_, __m128i>(_mm_cmpord_sd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmpord_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmpunord_sd() {
+        let (a, b) = (_mm_setr_pd(NAN, 2.0), _mm_setr_pd(5.0, 3.0));
+        let e = _mm_setr_epi64x(!0, transmute(2.0f64));
+        let r = transmute::<_, __m128i>(_mm_cmpunord_sd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmpunord_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmpneq_sd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(5.0, 3.0));
+        let e = _mm_setr_epi64x(!0, transmute(2.0f64));
+        let r = transmute::<_, __m128i>(_mm_cmpneq_sd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmpneq_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmpnlt_sd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(5.0, 3.0));
+        let e = _mm_setr_epi64x(0, transmute(2.0f64));
+        let r = transmute::<_, __m128i>(_mm_cmpnlt_sd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmpnlt_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmpnle_sd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        let e = _mm_setr_epi64x(0, transmute(2.0f64));
+        let r = transmute::<_, __m128i>(_mm_cmpnle_sd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmpnle_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmpngt_sd() {
+        let (a, b) = (_mm_setr_pd(5.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        let e = _mm_setr_epi64x(0, transmute(2.0f64));
+        let r = transmute::<_, __m128i>(_mm_cmpngt_sd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmpngt_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmpnge_sd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        let e = _mm_setr_epi64x(0, transmute(2.0f64));
+        let r = transmute::<_, __m128i>(_mm_cmpnge_sd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmpnge_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmpeq_pd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        let e = _mm_setr_epi64x(!0, 0);
+        let r = transmute::<_, __m128i>(_mm_cmpeq_pd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmpeq_pd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmplt_pd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        let e = _mm_setr_epi64x(0, !0);
+        let r = transmute::<_, __m128i>(_mm_cmplt_pd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmplt_pd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmple_pd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        let e = _mm_setr_epi64x(!0, !0);
+        let r = transmute::<_, __m128i>(_mm_cmple_pd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmple_pd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmpgt_pd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        let e = _mm_setr_epi64x(0, 0);
+        let r = transmute::<_, __m128i>(_mm_cmpgt_pd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmpgt_pd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmpge_pd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        let e = _mm_setr_epi64x(!0, 0);
+        let r = transmute::<_, __m128i>(_mm_cmpge_pd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmpge_pd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmpord_pd() {
+        let (a, b) = (_mm_setr_pd(NAN, 2.0), _mm_setr_pd(5.0, 3.0));
+        let e = _mm_setr_epi64x(0, !0);
+        let r = transmute::<_, __m128i>(_mm_cmpord_pd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmpord_pd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmpunord_pd() {
+        let (a, b) = (_mm_setr_pd(NAN, 2.0), _mm_setr_pd(5.0, 3.0));
+        let e = _mm_setr_epi64x(!0, 0);
+        let r = transmute::<_, __m128i>(_mm_cmpunord_pd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmpunord_pd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmpneq_pd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(5.0, 3.0));
+        let e = _mm_setr_epi64x(!0, !0);
+        let r = transmute::<_, __m128i>(_mm_cmpneq_pd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmpneq_pd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmpnlt_pd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(5.0, 3.0));
+        let e = _mm_setr_epi64x(0, 0);
+        let r = transmute::<_, __m128i>(_mm_cmpnlt_pd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmpnlt_pd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmpnle_pd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        let e = _mm_setr_epi64x(0, 0);
+        let r = transmute::<_, __m128i>(_mm_cmpnle_pd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmpnle_pd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmpngt_pd() {
+        let (a, b) = (_mm_setr_pd(5.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        let e = _mm_setr_epi64x(0, !0);
+        let r = transmute::<_, __m128i>(_mm_cmpngt_pd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmpngt_pd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cmpnge_pd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        let e = _mm_setr_epi64x(0, !0);
+        let r = transmute::<_, __m128i>(_mm_cmpnge_pd(a, b));
+        assert_eq_m128i(r, e);
+    }
+    test_mm_cmpnge_pd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_comieq_sd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        assert!(_mm_comieq_sd(a, b) != 0);
+
+        let (a, b) = (_mm_setr_pd(NAN, 2.0), _mm_setr_pd(1.0, 3.0));
+        assert!(_mm_comieq_sd(a, b) == 0);
+    }
+    test_mm_comieq_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_comilt_sd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        assert!(_mm_comilt_sd(a, b) == 0);
+    }
+    test_mm_comilt_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_comile_sd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        assert!(_mm_comile_sd(a, b) != 0);
+    }
+    test_mm_comile_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_comigt_sd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        assert!(_mm_comigt_sd(a, b) == 0);
+    }
+    test_mm_comigt_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_comige_sd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        assert!(_mm_comige_sd(a, b) != 0);
+    }
+    test_mm_comige_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_comineq_sd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        assert!(_mm_comineq_sd(a, b) == 0);
+    }
+    test_mm_comineq_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_ucomieq_sd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        assert!(_mm_ucomieq_sd(a, b) != 0);
+
+        let (a, b) = (_mm_setr_pd(NAN, 2.0), _mm_setr_pd(NAN, 3.0));
+        assert!(_mm_ucomieq_sd(a, b) == 0);
+    }
+    test_mm_ucomieq_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_ucomilt_sd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        assert!(_mm_ucomilt_sd(a, b) == 0);
+    }
+    test_mm_ucomilt_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_ucomile_sd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        assert!(_mm_ucomile_sd(a, b) != 0);
+    }
+    test_mm_ucomile_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_ucomigt_sd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        assert!(_mm_ucomigt_sd(a, b) == 0);
+    }
+    test_mm_ucomigt_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_ucomige_sd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        assert!(_mm_ucomige_sd(a, b) != 0);
+    }
+    test_mm_ucomige_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_ucomineq_sd() {
+        let (a, b) = (_mm_setr_pd(1.0, 2.0), _mm_setr_pd(1.0, 3.0));
+        assert!(_mm_ucomineq_sd(a, b) == 0);
+    }
+    test_mm_ucomineq_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cvtpd_ps() {
+        let r = _mm_cvtpd_ps(_mm_setr_pd(-1.0, 5.0));
+        assert_eq_m128(r, _mm_setr_ps(-1.0, 5.0, 0.0, 0.0));
+
+        let r = _mm_cvtpd_ps(_mm_setr_pd(-1.0, -5.0));
+        assert_eq_m128(r, _mm_setr_ps(-1.0, -5.0, 0.0, 0.0));
+
+        let r = _mm_cvtpd_ps(_mm_setr_pd(f64::MAX, f64::MIN));
+        assert_eq_m128(r, _mm_setr_ps(f32::INFINITY, f32::NEG_INFINITY, 0.0, 0.0));
+
+        let r = _mm_cvtpd_ps(_mm_setr_pd(f32::MAX as f64, f32::MIN as f64));
+        assert_eq_m128(r, _mm_setr_ps(f32::MAX, f32::MIN, 0.0, 0.0));
+    }
+    test_mm_cvtpd_ps();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cvtps_pd() {
+        let r = _mm_cvtps_pd(_mm_setr_ps(-1.0, 2.0, -3.0, 5.0));
+        assert_eq_m128d(r, _mm_setr_pd(-1.0, 2.0));
+
+        let r = _mm_cvtps_pd(_mm_setr_ps(f32::MAX, f32::INFINITY, f32::NEG_INFINITY, f32::MIN));
+        assert_eq_m128d(r, _mm_setr_pd(f32::MAX as f64, f64::INFINITY));
+    }
+    test_mm_cvtps_pd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cvtpd_epi32() {
+        let r = _mm_cvtpd_epi32(_mm_setr_pd(-1.0, 5.0));
+        assert_eq_m128i(r, _mm_setr_epi32(-1, 5, 0, 0));
+
+        let r = _mm_cvtpd_epi32(_mm_setr_pd(-1.0, -5.0));
+        assert_eq_m128i(r, _mm_setr_epi32(-1, -5, 0, 0));
+
+        let r = _mm_cvtpd_epi32(_mm_setr_pd(f64::MAX, f64::MIN));
+        assert_eq_m128i(r, _mm_setr_epi32(i32::MIN, i32::MIN, 0, 0));
+
+        let r = _mm_cvtpd_epi32(_mm_setr_pd(f64::INFINITY, f64::NEG_INFINITY));
+        assert_eq_m128i(r, _mm_setr_epi32(i32::MIN, i32::MIN, 0, 0));
+
+        let r = _mm_cvtpd_epi32(_mm_setr_pd(f64::NAN, f64::NAN));
+        assert_eq_m128i(r, _mm_setr_epi32(i32::MIN, i32::MIN, 0, 0));
+    }
+    test_mm_cvtpd_epi32();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cvttpd_epi32() {
+        let a = _mm_setr_pd(-1.1, 2.2);
+        let r = _mm_cvttpd_epi32(a);
+        assert_eq_m128i(r, _mm_setr_epi32(-1, 2, 0, 0));
+
+        let a = _mm_setr_pd(f64::NEG_INFINITY, f64::NAN);
+        let r = _mm_cvttpd_epi32(a);
+        assert_eq_m128i(r, _mm_setr_epi32(i32::MIN, i32::MIN, 0, 0));
+    }
+    test_mm_cvttpd_epi32();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cvtsd_si32() {
+        let r = _mm_cvtsd_si32(_mm_setr_pd(-2.0, 5.0));
+        assert_eq!(r, -2);
+
+        let r = _mm_cvtsd_si32(_mm_setr_pd(f64::MAX, f64::MIN));
+        assert_eq!(r, i32::MIN);
+
+        let r = _mm_cvtsd_si32(_mm_setr_pd(f64::NAN, f64::NAN));
+        assert_eq!(r, i32::MIN);
+    }
+    test_mm_cvtsd_si32();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cvttsd_si32() {
+        let a = _mm_setr_pd(-1.1, 2.2);
+        let r = _mm_cvttsd_si32(a);
+        assert_eq!(r, -1);
+
+        let a = _mm_setr_pd(f64::NEG_INFINITY, f64::NAN);
+        let r = _mm_cvttsd_si32(a);
+        assert_eq!(r, i32::MIN);
+    }
+    test_mm_cvttsd_si32();
+
+    // Intrinsic only available on x86_64
+    #[cfg(target_arch = "x86_64")]
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cvtsd_si64() {
+        let r = _mm_cvtsd_si64(_mm_setr_pd(-2.0, 5.0));
+        assert_eq!(r, -2_i64);
+
+        let r = _mm_cvtsd_si64(_mm_setr_pd(f64::MAX, f64::MIN));
+        assert_eq!(r, i64::MIN);
+    }
+    #[cfg(target_arch = "x86_64")]
+    test_mm_cvtsd_si64();
+
+    // Intrinsic only available on x86_64
+    #[cfg(target_arch = "x86_64")]
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cvttsd_si64() {
+        let a = _mm_setr_pd(-1.1, 2.2);
+        let r = _mm_cvttsd_si64(a);
+        assert_eq!(r, -1_i64);
+    }
+    #[cfg(target_arch = "x86_64")]
+    test_mm_cvttsd_si64();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cvtsd_ss() {
+        let a = _mm_setr_ps(-1.1, -2.2, 3.3, 4.4);
+        let b = _mm_setr_pd(2.0, -5.0);
+
+        let r = _mm_cvtsd_ss(a, b);
+
+        assert_eq_m128(r, _mm_setr_ps(2.0, -2.2, 3.3, 4.4));
+
+        let a = _mm_setr_ps(-1.1, f32::NEG_INFINITY, f32::MAX, f32::NEG_INFINITY);
+        let b = _mm_setr_pd(f64::INFINITY, -5.0);
+
+        let r = _mm_cvtsd_ss(a, b);
+
+        assert_eq_m128(
+            r,
+            _mm_setr_ps(f32::INFINITY, f32::NEG_INFINITY, f32::MAX, f32::NEG_INFINITY),
+        );
+    }
+    test_mm_cvtsd_ss();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_cvtss_sd() {
+        let a = _mm_setr_pd(-1.1, 2.2);
+        let b = _mm_setr_ps(1.0, 2.0, 3.0, 4.0);
+
+        let r = _mm_cvtss_sd(a, b);
+        assert_eq_m128d(r, _mm_setr_pd(1.0, 2.2));
+
+        let a = _mm_setr_pd(-1.1, f64::INFINITY);
+        let b = _mm_setr_ps(f32::NEG_INFINITY, 2.0, 3.0, 4.0);
+
+        let r = _mm_cvtss_sd(a, b);
+        assert_eq_m128d(r, _mm_setr_pd(f64::NEG_INFINITY, f64::INFINITY));
+    }
+    test_mm_cvtss_sd();
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn test_mm_movemask_pd() {
+        let r = _mm_movemask_pd(_mm_setr_pd(-1.0, 5.0));
+        assert_eq!(r, 0b01);
+
+        let r = _mm_movemask_pd(_mm_setr_pd(-1.0, -5.0));
+        assert_eq!(r, 0b11);
+    }
+    test_mm_movemask_pd();
+}
diff --git a/src/tools/miri/triagebot.toml b/src/tools/miri/triagebot.toml
index 21a154cafd4..69f3cd2f810 100644
--- a/src/tools/miri/triagebot.toml
+++ b/src/tools/miri/triagebot.toml
@@ -9,3 +9,6 @@ allow-unauthenticated = [
 
 # Gives us the commands 'ready', 'author', 'blocked'
 [shortcut]
+
+[no-merges]
+exclude_labels = ["rollup", "subtree-sync"]
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
index cead64a3374..9bd0c30b105 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
@@ -239,7 +239,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
         template!(List: "address, kcfi, memory, thread"), DuplicatesOk,
         experimental!(no_sanitize)
     ),
-    gated!(no_coverage, Normal, template!(Word), WarnFollowing, experimental!(no_coverage)),
+    gated!(coverage, Normal, template!(Word, List: "on|off"), WarnFollowing, experimental!(coverage)),
 
     ungated!(
         doc, Normal, template!(List: "hidden|inline|...", NameValueStr: "string"), DuplicatesOk
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
index 49b37024a5e..57563a17483 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
@@ -3505,8 +3505,8 @@ This serves two purposes:
 "##,
     },
     Lint {
-        label: "no_coverage",
-        description: r##"# `no_coverage`
+        label: "coverage",
+        description: r##"# `coverage`
 
 The tracking issue for this feature is: [#84605]
 
@@ -3514,7 +3514,7 @@ The tracking issue for this feature is: [#84605]
 
 ---
 
-The `no_coverage` attribute can be used to selectively disable coverage
+The `coverage` attribute can be used to selectively disable coverage
 instrumentation in an annotated function. This might be useful to:
 
 -   Avoid instrumentation overhead in a performance critical function
@@ -3524,14 +3524,14 @@ instrumentation in an annotated function. This might be useful to:
 ## Example
 
 ```rust
-#![feature(no_coverage)]
+#![feature(coverage)]
 
 // `foo()` will get coverage instrumentation (by default)
 fn foo() {
   // ...
 }
 
-#[no_coverage]
+#[coverage(off)]
 fn bar() {
   // ...
 }
diff --git a/src/tools/rustdoc-themes/main.rs b/src/tools/rustdoc-themes/main.rs
index 7cac985a9a3..cc13df1f5ba 100644
--- a/src/tools/rustdoc-themes/main.rs
+++ b/src/tools/rustdoc-themes/main.rs
@@ -1,25 +1,37 @@
 use std::env::args;
-use std::fs::read_dir;
+use std::fs::File;
+use std::io::{BufRead, BufReader, BufWriter, Write};
 use std::path::Path;
 use std::process::{exit, Command};
 
-const FILES_TO_IGNORE: &[&str] = &["light.css"];
-
-fn get_folders<P: AsRef<Path>>(folder_path: P) -> Vec<String> {
+fn get_themes<P: AsRef<Path>>(style_path: P) -> Vec<String> {
     let mut ret = Vec::with_capacity(10);
 
-    for entry in read_dir(folder_path.as_ref()).expect("read_dir failed") {
-        let entry = entry.expect("Couldn't unwrap entry");
-        let path = entry.path();
+    const BEGIN_THEME_MARKER: &'static str = "/* Begin theme: ";
+    const END_THEME_MARKER: &'static str = "/* End theme: ";
+
+    let timestamp =
+        std::time::SystemTime::UNIX_EPOCH.elapsed().expect("time is after UNIX epoch").as_millis();
 
-        if !path.is_file() {
-            continue;
+    let mut in_theme = None;
+    for line in BufReader::new(File::open(style_path).expect("read rustdoc.css failed")).lines() {
+        let line = line.expect("read line from rustdoc.css failed");
+        let line = line.trim();
+        if line.starts_with(BEGIN_THEME_MARKER) {
+            let theme_name = &line[BEGIN_THEME_MARKER.len()..].trim().trim_end_matches("*/").trim();
+            let filename = format!("build/tmp/rustdoc.bootstrap.{timestamp}.{theme_name}.css");
+            in_theme = Some(BufWriter::new(
+                File::create(&filename).expect("failed to create temporary test css file"),
+            ));
+            ret.push(filename);
+        }
+        if let Some(in_theme) = in_theme.as_mut() {
+            in_theme.write_all(line.as_bytes()).expect("write to temporary test css file");
+            in_theme.write_all(b"\n").expect("write to temporary test css file");
         }
-        let filename = path.file_name().expect("file_name failed");
-        if FILES_TO_IGNORE.iter().any(|x| x == &filename) {
-            continue;
+        if line.starts_with(END_THEME_MARKER) {
+            in_theme = None;
         }
-        ret.push(format!("{}", path.display()));
     }
     ret
 }
@@ -32,10 +44,10 @@ fn main() {
         exit(1);
     }
     let rustdoc_bin = &argv[1];
-    let themes_folder = &argv[2];
-    let themes = get_folders(&themes_folder);
+    let style_path = &argv[2];
+    let themes = get_themes(&style_path);
     if themes.is_empty() {
-        eprintln!("No theme found in \"{}\"...", themes_folder);
+        eprintln!("No themes found in \"{}\"...", style_path);
         exit(1);
     }
     let arg_name = "--check-theme".to_owned();
diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs
index c3c07f310bf..03cdddc4140 100644
--- a/src/tools/rustfmt/src/expr.rs
+++ b/src/tools/rustfmt/src/expr.rs
@@ -664,7 +664,7 @@ struct ControlFlow<'a> {
 
 fn extract_pats_and_cond(expr: &ast::Expr) -> (Option<&ast::Pat>, &ast::Expr) {
     match expr.kind {
-        ast::ExprKind::Let(ref pat, ref cond, _) => (Some(pat), cond),
+        ast::ExprKind::Let(ref pat, ref cond, _, _) => (Some(pat), cond),
         _ => (None, expr),
     }
 }
diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs
index 9b19b8eecc7..ca160017202 100644
--- a/src/tools/tidy/src/lib.rs
+++ b/src/tools/tidy/src/lib.rs
@@ -64,6 +64,7 @@ pub mod fluent_alphabetical;
 pub mod mir_opt_tests;
 pub mod pal;
 pub mod primitive_docs;
+pub mod rustdoc_css_themes;
 pub mod rustdoc_gui_tests;
 pub mod style;
 pub mod target_specific_tests;
diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs
index 5fa91715a07..5585e125f2d 100644
--- a/src/tools/tidy/src/main.rs
+++ b/src/tools/tidy/src/main.rs
@@ -104,6 +104,7 @@ fn main() {
         check!(ui_tests, &tests_path);
         check!(mir_opt_tests, &tests_path, bless);
         check!(rustdoc_gui_tests, &tests_path);
+        check!(rustdoc_css_themes, &librustdoc_path);
 
         // Checks that only make sense for the compiler.
         check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], verbose);
diff --git a/src/tools/tidy/src/rustdoc_css_themes.rs b/src/tools/tidy/src/rustdoc_css_themes.rs
new file mode 100644
index 00000000000..852d6e14e91
--- /dev/null
+++ b/src/tools/tidy/src/rustdoc_css_themes.rs
@@ -0,0 +1,99 @@
+//! Tidy check to make sure light and dark themes are synchronized between
+//! JS-controlled rustdoc.css and noscript.css
+
+use std::path::Path;
+
+pub fn check(librustdoc_path: &Path, bad: &mut bool) {
+    let rustdoc_css = "html/static/css/rustdoc.css";
+    let noscript_css = "html/static/css/noscript.css";
+    let rustdoc_css_contents = std::fs::read_to_string(librustdoc_path.join(rustdoc_css))
+        .unwrap_or_else(|e| panic!("failed to read librustdoc/{rustdoc_css}: {e}"));
+    let noscript_css_contents = std::fs::read_to_string(librustdoc_path.join(noscript_css))
+        .unwrap_or_else(|e| panic!("failed to read librustdoc/{noscript_css}: {e}"));
+    compare_themes_from_files(
+        "light",
+        rustdoc_css_contents.lines().enumerate().map(|(i, l)| (i + 1, l.trim())),
+        noscript_css_contents.lines().enumerate().map(|(i, l)| (i + 1, l.trim())),
+        bad,
+    );
+    compare_themes_from_files(
+        "dark",
+        rustdoc_css_contents.lines().enumerate(),
+        noscript_css_contents.lines().enumerate(),
+        bad,
+    );
+}
+
+fn compare_themes_from_files<'a>(
+    name: &str,
+    mut rustdoc_css_lines: impl Iterator<Item = (usize, &'a str)>,
+    mut noscript_css_lines: impl Iterator<Item = (usize, &'a str)>,
+    bad: &mut bool,
+) {
+    let begin_theme_pat = format!("/* Begin theme: {name}");
+    let mut found_theme = None;
+    let mut found_theme_noscript = None;
+    while let Some((rustdoc_css_line_number, rustdoc_css_line)) = rustdoc_css_lines.next() {
+        if !rustdoc_css_line.starts_with(&begin_theme_pat) {
+            continue;
+        }
+        if let Some(found_theme) = found_theme {
+            tidy_error!(
+                bad,
+                "rustdoc.css contains two {name} themes on lines {rustdoc_css_line_number} and {found_theme}",
+            );
+            return;
+        }
+        found_theme = Some(rustdoc_css_line_number);
+        while let Some((noscript_css_line_number, noscript_css_line)) = noscript_css_lines.next() {
+            if !noscript_css_line.starts_with(&begin_theme_pat) {
+                continue;
+            }
+            if let Some(found_theme_noscript) = found_theme_noscript {
+                tidy_error!(
+                    bad,
+                    "noscript.css contains two {name} themes on lines {noscript_css_line_number} and {found_theme_noscript}",
+                );
+                return;
+            }
+            found_theme_noscript = Some(noscript_css_line_number);
+            compare_themes(name, &mut rustdoc_css_lines, &mut noscript_css_lines, bad);
+        }
+    }
+}
+
+fn compare_themes<'a>(
+    name: &str,
+    rustdoc_css_lines: impl Iterator<Item = (usize, &'a str)>,
+    noscript_css_lines: impl Iterator<Item = (usize, &'a str)>,
+    bad: &mut bool,
+) {
+    let end_theme_pat = format!("/* End theme: {name}");
+    for (
+        (rustdoc_css_line_number, rustdoc_css_line),
+        (noscript_css_line_number, noscript_css_line),
+    ) in rustdoc_css_lines.zip(noscript_css_lines)
+    {
+        if noscript_css_line.starts_with(":root {")
+            && rustdoc_css_line.starts_with(&format!(r#":root[data-theme="{name}"] {{"#))
+        {
+            // selectors are different between rustdoc.css and noscript.css
+            // that's why they both exist: one uses JS, the other uses media queries
+            continue;
+        }
+        if noscript_css_line.starts_with(&end_theme_pat)
+            && rustdoc_css_line.starts_with(&end_theme_pat)
+        {
+            break;
+        }
+        if rustdoc_css_line != noscript_css_line {
+            tidy_error!(
+                bad,
+                "noscript.css:{noscript_css_line_number} and rustdoc.css:{rustdoc_css_line_number} contain copies of {name} theme that are not the same",
+            );
+            eprintln!("- {noscript_css_line}");
+            eprintln!("+ {rustdoc_css_line}");
+            return;
+        }
+    }
+}
diff --git a/tests/codegen/debuginfo-inline-callsite-location.rs b/tests/codegen/debuginfo-inline-callsite-location.rs
new file mode 100644
index 00000000000..b1475ee7931
--- /dev/null
+++ b/tests/codegen/debuginfo-inline-callsite-location.rs
@@ -0,0 +1,28 @@
+// compile-flags: -g -O
+
+// Check that each inline call site for the same function uses the same "sub-program" so that LLVM
+// can correctly merge the debug info if it merges the inlined code (e.g., for merging of tail
+// calls to panic.
+
+// CHECK:       tail call void @_ZN4core9panicking5panic17h{{([0-9a-z]{16})}}E
+// CHECK-SAME:  !dbg ![[#first_dbg:]]
+// CHECK:       tail call void @_ZN4core9panicking5panic17h{{([0-9a-z]{16})}}E
+// CHECK-SAME:  !dbg ![[#second_dbg:]]
+
+// CHECK-DAG:   ![[#func_dbg:]] = distinct !DISubprogram(name: "unwrap<i32>"
+// CHECK-DAG:   ![[#first_scope:]] = distinct !DILexicalBlock(scope: ![[#func_dbg]],
+// CHECK:       ![[#second_scope:]] = distinct !DILexicalBlock(scope: ![[#func_dbg]],
+// CHECK:       ![[#first_dbg]] = !DILocation(line: [[#]]
+// CHECK-SAME:  scope: ![[#first_scope]], inlinedAt: ![[#]])
+// CHECK:       ![[#second_dbg]] = !DILocation(line: [[#]]
+// CHECK-SAME:  scope: ![[#second_scope]], inlinedAt: ![[#]])
+
+#![crate_type = "lib"]
+
+#[no_mangle]
+extern "C" fn add_numbers(x: &Option<i32>, y: &Option<i32>) -> i32 {
+    let x1 = x.unwrap();
+    let y1 = y.unwrap();
+
+    x1 + y1
+}
diff --git a/tests/codegen/issues/issue-115385-llvm-jump-threading.rs b/tests/codegen/issues/issue-115385-llvm-jump-threading.rs
new file mode 100644
index 00000000000..142e3596d96
--- /dev/null
+++ b/tests/codegen/issues/issue-115385-llvm-jump-threading.rs
@@ -0,0 +1,46 @@
+// compile-flags: -O -Ccodegen-units=1
+
+#![crate_type = "lib"]
+
+#[repr(i64)]
+pub enum Boolean {
+    False = 0,
+    True = 1,
+}
+
+impl Clone for Boolean {
+    fn clone(&self) -> Self {
+        *self
+    }
+}
+
+impl Copy for Boolean {}
+
+extern "C" {
+    fn set_value(foo: *mut i64);
+    fn bar();
+}
+
+pub fn foo(x: bool) {
+    let mut foo = core::mem::MaybeUninit::<i64>::uninit();
+    unsafe {
+        set_value(foo.as_mut_ptr());
+    }
+
+    if x {
+        let l1 = unsafe { *foo.as_mut_ptr().cast::<Boolean>() };
+        if matches!(l1, Boolean::False) {
+            unsafe {
+                *foo.as_mut_ptr() = 0;
+            }
+        }
+    }
+
+    let l2 = unsafe { *foo.as_mut_ptr() };
+    if l2 == 2 {
+        // CHECK: call void @bar
+        unsafe {
+            bar();
+        }
+    }
+}
diff --git a/tests/codegen/repr/transparent.rs b/tests/codegen/repr/transparent.rs
index b140fc719da..c5974248bb3 100644
--- a/tests/codegen/repr/transparent.rs
+++ b/tests/codegen/repr/transparent.rs
@@ -43,7 +43,6 @@ pub extern "C" fn test_WithZst(_: WithZst) -> WithZst { loop {} }
 #[repr(transparent)]
 pub struct WithZeroSizedArray(*const f32, [i8; 0]);
 
-// Apparently we use i32* when newtype-unwrapping f32 pointers. Whatever.
 // CHECK: define{{.*}}ptr @test_WithZeroSizedArray(ptr noundef %_1)
 #[no_mangle]
 pub extern "C" fn test_WithZeroSizedArray(_: WithZeroSizedArray) -> WithZeroSizedArray { loop {} }
diff --git a/tests/codegen/sanitizer/address-sanitizer-globals-tracking.rs b/tests/codegen/sanitizer/address-sanitizer-globals-tracking.rs
new file mode 100644
index 00000000000..a70ef7751b6
--- /dev/null
+++ b/tests/codegen/sanitizer/address-sanitizer-globals-tracking.rs
@@ -0,0 +1,43 @@
+// Verifies that AddressSanitizer symbols show up as expected in LLVM IR with `-Zsanitizer`.
+// This is a regression test for https://github.com/rust-lang/rust/issues/113404
+//
+// Notes about the `compile-flags` below:
+//
+// * The original issue only reproed with LTO - this is why this angle has
+//   extra test coverage via different `revisions`
+// * To observe the failure/repro at LLVM-IR level we need to use `staticlib`
+//   which necessitates `-C prefer-dynamic=false` - without the latter flag,
+//   we would have run into "cannot prefer dynamic linking when performing LTO".
+//
+// The test is restricted to `only-linux`, because the sanitizer-related instrumentation is target
+// specific.  In particular, `___asan_globals_registered` is only used in the
+// `InstrumentGlobalsELF` and `InstrumentGlobalsMachO` code paths.  The `only-linux` filter is
+// narrower than really needed (i.e. narrower than ELF-or-MachO), but this seems ok - having a
+// linux-only regression test should be sufficient here.
+//
+// needs-sanitizer-address
+// only-linux
+//
+// revisions:ASAN ASAN-FAT-LTO
+//[ASAN]          compile-flags: -Zsanitizer=address
+//[ASAN-FAT-LTO]  compile-flags: -Zsanitizer=address -Cprefer-dynamic=false -Clto=fat
+
+#![crate_type="staticlib"]
+
+// The test below mimics `CACHED_POW10` from `library/core/src/num/flt2dec/strategy/grisu.rs` which
+// (because of incorrect handling of `___asan_globals_registered` during LTO) was incorrectly
+// reported as an ODR violation in https://crbug.com/1459233#c1.  Before this bug was fixed,
+// `___asan_globals_registered` would show up as `internal global i64` rather than `common hidden
+// global i64`.  (The test expectations ignore the exact type because on `arm-android` the type
+// is `i32` rather than `i64`.)
+//
+// CHECK: @___asan_globals_registered = common hidden global
+// CHECK: @__start_asan_globals = extern_weak hidden global
+// CHECK: @__stop_asan_globals = extern_weak hidden global
+#[no_mangle]
+pub static CACHED_POW10: [(u64, i16, i16); 4] = [
+    (0xe61acf033d1a45df, -1087, -308),
+    (0xab70fe17c79ac6ca, -1060, -300),
+    (0xff77b1fcbebcdc4f, -1034, -292),
+    (0xbe5691ef416bd60c, -1007, -284),
+];
diff --git a/tests/coverage-map/status-quo/closure_macro.rs b/tests/coverage-map/status-quo/closure_macro.rs
index 5e3b00d1ef5..9b289141c2e 100644
--- a/tests/coverage-map/status-quo/closure_macro.rs
+++ b/tests/coverage-map/status-quo/closure_macro.rs
@@ -1,5 +1,5 @@
 // compile-flags: --edition=2018
-#![feature(no_coverage)]
+#![feature(coverage_attribute)]
 
 macro_rules! bail {
     ($msg:literal $(,)?) => {
diff --git a/tests/coverage-map/status-quo/closure_macro_async.rs b/tests/coverage-map/status-quo/closure_macro_async.rs
index 3d6bdb38a2a..b4275599e59 100644
--- a/tests/coverage-map/status-quo/closure_macro_async.rs
+++ b/tests/coverage-map/status-quo/closure_macro_async.rs
@@ -1,5 +1,5 @@
 // compile-flags: --edition=2018
-#![feature(no_coverage)]
+#![feature(coverage_attribute)]
 
 macro_rules! bail {
     ($msg:literal $(,)?) => {
@@ -39,7 +39,7 @@ pub async fn test() -> Result<(), String> {
     Ok(())
 }
 
-#[no_coverage]
+#[coverage(off)]
 fn main() {
     executor::block_on(test()).unwrap();
 }
@@ -51,18 +51,18 @@ mod executor {
         task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
     };
 
-    #[no_coverage]
+    #[coverage(off)]
     pub fn block_on<F: Future>(mut future: F) -> F::Output {
         let mut future = unsafe { Pin::new_unchecked(&mut future) };
         use std::hint::unreachable_unchecked;
         static VTABLE: RawWakerVTable = RawWakerVTable::new(
-            #[no_coverage]
+            #[coverage(off)]
             |_| unsafe { unreachable_unchecked() }, // clone
-            #[no_coverage]
+            #[coverage(off)]
             |_| unsafe { unreachable_unchecked() }, // wake
-            #[no_coverage]
+            #[coverage(off)]
             |_| unsafe { unreachable_unchecked() }, // wake_by_ref
-            #[no_coverage]
+            #[coverage(off)]
             |_| (),
         );
         let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
diff --git a/tests/coverage-map/status-quo/no_cov_crate.rs b/tests/coverage-map/status-quo/no_cov_crate.rs
index 5b748aeefb7..e12e4bc55e3 100644
--- a/tests/coverage-map/status-quo/no_cov_crate.rs
+++ b/tests/coverage-map/status-quo/no_cov_crate.rs
@@ -1,17 +1,17 @@
-// Enables `no_coverage` on the entire crate
-#![feature(no_coverage)]
+// Enables `coverage(off)` on the entire crate
+#![feature(coverage_attribute)]
 
-#[no_coverage]
+#[coverage(off)]
 fn do_not_add_coverage_1() {
     println!("called but not covered");
 }
 
 fn do_not_add_coverage_2() {
-    #![no_coverage]
+    #![coverage(off)]
     println!("called but not covered");
 }
 
-#[no_coverage]
+#[coverage(off)]
 #[allow(dead_code)]
 fn do_not_add_coverage_not_called() {
     println!("not called and not covered");
@@ -33,7 +33,7 @@ fn add_coverage_not_called() {
 // FIXME: These test-cases illustrate confusing results of nested functions.
 // See https://github.com/rust-lang/rust/issues/93319
 mod nested_fns {
-    #[no_coverage]
+    #[coverage(off)]
     pub fn outer_not_covered(is_true: bool) {
         fn inner(is_true: bool) {
             if is_true {
@@ -50,7 +50,7 @@ mod nested_fns {
         println!("called and covered");
         inner_not_covered(is_true);
 
-        #[no_coverage]
+        #[coverage(off)]
         fn inner_not_covered(is_true: bool) {
             if is_true {
                 println!("called but not covered");
diff --git a/tests/debuginfo/pretty-std-collections.rs b/tests/debuginfo/pretty-std-collections.rs
index 93597aa7e23..93a0dff6848 100644
--- a/tests/debuginfo/pretty-std-collections.rs
+++ b/tests/debuginfo/pretty-std-collections.rs
@@ -1,7 +1,6 @@
 // ignore-windows failing on win32 bot
 // ignore-freebsd: gdb package too new
 // ignore-android: FIXME(#10381)
-// ignore-macos: FIXME(#78665)
 // compile-flags:-g
 
 // The pretty printers being tested here require the patch from
diff --git a/tests/mir-opt/building/async_await.b-{closure#0}.generator_resume.0.mir b/tests/mir-opt/building/async_await.b-{closure#0}.generator_resume.0.mir
index 80ac92d59f3..b06666c9dd7 100644
--- a/tests/mir-opt/building/async_await.b-{closure#0}.generator_resume.0.mir
+++ b/tests/mir-opt/building/async_await.b-{closure#0}.generator_resume.0.mir
@@ -2,7 +2,14 @@
 /* generator_layout = GeneratorLayout {
     field_tys: {
         _0: GeneratorSavedTy {
-            ty: impl std::future::Future<Output = ()>,
+            ty: Alias(
+                Opaque,
+                AliasTy {
+                    args: [
+                    ],
+                    def_id: DefId(0:7 ~ async_await[ccf8]::a::{opaque#0}),
+                },
+            ),
             source_info: SourceInfo {
                 span: $DIR/async_await.rs:15:9: 15:14 (#8),
                 scope: scope[0],
@@ -10,7 +17,14 @@
             ignore_for_traits: false,
         },
         _1: GeneratorSavedTy {
-            ty: impl std::future::Future<Output = ()>,
+            ty: Alias(
+                Opaque,
+                AliasTy {
+                    args: [
+                    ],
+                    def_id: DefId(0:7 ~ async_await[ccf8]::a::{opaque#0}),
+                },
+            ),
             source_info: SourceInfo {
                 span: $DIR/async_await.rs:16:9: 16:14 (#10),
                 scope: scope[0],
diff --git a/tests/mir-opt/const_debuginfo.main.ConstDebugInfo.diff b/tests/mir-opt/const_debuginfo.main.ConstDebugInfo.diff
index 255ec94816c..ed47baa67da 100644
--- a/tests/mir-opt/const_debuginfo.main.ConstDebugInfo.diff
+++ b/tests/mir-opt/const_debuginfo.main.ConstDebugInfo.diff
@@ -33,14 +33,22 @@
                           let _15: bool;
                           let _16: u32;
                           scope 6 {
-                              debug f => (bool, bool, u32){ .0 => _14, .1 => _15, .2 => _16, };
+-                             debug ((f: (bool, bool, u32)).0: bool) => _14;
+-                             debug ((f: (bool, bool, u32)).1: bool) => _15;
+-                             debug ((f: (bool, bool, u32)).2: u32) => _16;
++                             debug ((f: (bool, bool, u32)).0: bool) => const true;
++                             debug ((f: (bool, bool, u32)).1: bool) => const false;
++                             debug ((f: (bool, bool, u32)).2: u32) => const 123_u32;
                               let _10: std::option::Option<u16>;
                               scope 7 {
                                   debug o => _10;
                                   let _17: u32;
                                   let _18: u32;
                                   scope 8 {
-                                      debug p => Point{ .0 => _17, .1 => _18, };
+-                                     debug ((p: Point).0: u32) => _17;
+-                                     debug ((p: Point).1: u32) => _18;
++                                     debug ((p: Point).0: u32) => const 32_u32;
++                                     debug ((p: Point).1: u32) => const 32_u32;
                                       let _11: u32;
                                       scope 9 {
 -                                         debug a => _11;
diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-abort.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-abort.diff
index 55c774d555d..e443c8991f9 100644
--- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-abort.diff
+++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-abort.diff
@@ -34,11 +34,11 @@
           StorageLive(_5);
           StorageLive(_6);
           _6 = const 3_usize;
-          _7 = const 3_usize;
+          _7 = Len((*_1));
 -         _8 = Lt(_6, _7);
 -         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
-+         _8 = const false;
-+         assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind unreachable];
++         _8 = Lt(const 3_usize, _7);
++         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 3_usize) -> [success: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-unwind.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-unwind.diff
index dcab570ea77..592f43f4739 100644
--- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-unwind.diff
@@ -34,11 +34,11 @@
           StorageLive(_5);
           StorageLive(_6);
           _6 = const 3_usize;
-          _7 = const 3_usize;
+          _7 = Len((*_1));
 -         _8 = Lt(_6, _7);
 -         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
-+         _8 = const false;
-+         assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind continue];
++         _8 = Lt(const 3_usize, _7);
++         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 3_usize) -> [success: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-abort.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-abort.diff
index 55c774d555d..e443c8991f9 100644
--- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-abort.diff
+++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-abort.diff
@@ -34,11 +34,11 @@
           StorageLive(_5);
           StorageLive(_6);
           _6 = const 3_usize;
-          _7 = const 3_usize;
+          _7 = Len((*_1));
 -         _8 = Lt(_6, _7);
 -         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
-+         _8 = const false;
-+         assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind unreachable];
++         _8 = Lt(const 3_usize, _7);
++         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 3_usize) -> [success: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-unwind.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-unwind.diff
index dcab570ea77..592f43f4739 100644
--- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-unwind.diff
@@ -34,11 +34,11 @@
           StorageLive(_5);
           StorageLive(_6);
           _6 = const 3_usize;
-          _7 = const 3_usize;
+          _7 = Len((*_1));
 -         _8 = Lt(_6, _7);
 -         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
-+         _8 = const false;
-+         assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind continue];
++         _8 = Lt(const 3_usize, _7);
++         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 3_usize) -> [success: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs
index 7931c4f02ae..d6b1a93f304 100644
--- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs
+++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs
@@ -1,8 +1,7 @@
 // unit-test: ConstProp
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-// compile-flags: -Zmir-enable-passes=+NormalizeArrayLen
-
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
+
 // EMIT_MIR bad_op_unsafe_oob_for_slices.main.ConstProp.diff
 #[allow(unconditional_panic)]
 fn main() {
diff --git a/tests/mir-opt/const_prop/checked_add.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/checked_add.main.ConstProp.panic-abort.diff
index 6daef87dd2c..c2fd7f65f5e 100644
--- a/tests/mir-opt/const_prop/checked_add.main.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/checked_add.main.ConstProp.panic-abort.diff
@@ -24,5 +24,9 @@
           StorageDead(_1);
           return;
       }
++ }
++ 
++ alloc3 (size: 8, align: 4) {
++     02 00 00 00 00 __ __ __                         │ .....░░░
   }
   
diff --git a/tests/mir-opt/const_prop/checked_add.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/checked_add.main.ConstProp.panic-unwind.diff
index 125407bf285..21a31f9aba3 100644
--- a/tests/mir-opt/const_prop/checked_add.main.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/checked_add.main.ConstProp.panic-unwind.diff
@@ -24,5 +24,9 @@
           StorageDead(_1);
           return;
       }
++ }
++ 
++ alloc3 (size: 8, align: 4) {
++     02 00 00 00 00 __ __ __                         │ .....░░░
   }
   
diff --git a/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-abort.diff
index ca0ce2888cd..c0efc873029 100644
--- a/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-abort.diff
@@ -29,5 +29,9 @@
           StorageDead(_1);
           return;
       }
++ }
++ 
++ alloc3 (size: 2, align: 1) {
++     03 00                                           │ ..
   }
   
diff --git a/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-unwind.diff
index d63fb9255a3..2aee6f164ae 100644
--- a/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-unwind.diff
@@ -29,5 +29,9 @@
           StorageDead(_1);
           return;
       }
++ }
++ 
++ alloc3 (size: 2, align: 1) {
++     03 00                                           │ ..
   }
   
diff --git a/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-abort.diff
index 51e17cf690a..7ba51ccdbf6 100644
--- a/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-abort.diff
@@ -35,5 +35,9 @@
           _0 = const ();
           return;
       }
++ }
++ 
++ alloc3 (size: 2, align: 1) {
++     00 01                                           │ ..
   }
   
diff --git a/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-unwind.diff
index 5ef201497fb..545b7f22f6e 100644
--- a/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-unwind.diff
@@ -35,5 +35,9 @@
           _0 = const ();
           return;
       }
++ }
++ 
++ alloc3 (size: 2, align: 1) {
++     00 01                                           │ ..
   }
   
diff --git a/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-abort.diff
index 170c019782d..18341ba7db9 100644
--- a/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-abort.diff
@@ -18,5 +18,13 @@
           StorageDead(_2);
           return;
       }
++ }
++ 
++ alloc8 (size: 2, align: 1) {
++     00 00                                           │ ..
++ }
++ 
++ alloc7 (size: 2, align: 1) {
++     00 00                                           │ ..
   }
   
diff --git a/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-unwind.diff
index 64227dfd78c..50763c10f0c 100644
--- a/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-unwind.diff
@@ -18,5 +18,13 @@
           StorageDead(_2);
           return;
       }
++ }
++ 
++ alloc8 (size: 2, align: 1) {
++     00 00                                           │ ..
++ }
++ 
++ alloc7 (size: 2, align: 1) {
++     00 00                                           │ ..
   }
   
diff --git a/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-abort.diff
index e1f3f37b370..015180db896 100644
--- a/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-abort.diff
@@ -23,5 +23,17 @@
           StorageDead(_2);
           return;
       }
++ }
++ 
++ alloc12 (size: 2, align: 1) {
++     01 02                                           │ ..
++ }
++ 
++ alloc11 (size: 2, align: 1) {
++     01 02                                           │ ..
++ }
++ 
++ alloc8 (size: 2, align: 1) {
++     01 02                                           │ ..
   }
   
diff --git a/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-unwind.diff
index aaa376a95cf..8e41705c1af 100644
--- a/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-unwind.diff
@@ -23,5 +23,17 @@
           StorageDead(_2);
           return;
       }
++ }
++ 
++ alloc12 (size: 2, align: 1) {
++     01 02                                           │ ..
++ }
++ 
++ alloc11 (size: 2, align: 1) {
++     01 02                                           │ ..
++ }
++ 
++ alloc8 (size: 2, align: 1) {
++     01 02                                           │ ..
   }
   
diff --git a/tests/mir-opt/const_prop/large_array_index.rs b/tests/mir-opt/const_prop/large_array_index.rs
index 6c03fe9d9c2..d226bd54671 100644
--- a/tests/mir-opt/const_prop/large_array_index.rs
+++ b/tests/mir-opt/const_prop/large_array_index.rs
@@ -1,6 +1,5 @@
 // unit-test: ConstProp
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-// compile-flags: -Zmir-enable-passes=+NormalizeArrayLen
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 
 // EMIT_MIR large_array_index.main.ConstProp.diff
diff --git a/tests/mir-opt/const_prop/mutable_variable_aggregate.main.ConstProp.diff b/tests/mir-opt/const_prop/mutable_variable_aggregate.main.ConstProp.diff
index 0f118c7f59f..56a127ae31e 100644
--- a/tests/mir-opt/const_prop/mutable_variable_aggregate.main.ConstProp.diff
+++ b/tests/mir-opt/const_prop/mutable_variable_aggregate.main.ConstProp.diff
@@ -25,5 +25,13 @@
           StorageDead(_1);
           return;
       }
++ }
++ 
++ alloc7 (size: 8, align: 4) {
++     2a 00 00 00 63 00 00 00                         │ *...c...
++ }
++ 
++ alloc5 (size: 8, align: 4) {
++     2a 00 00 00 2b 00 00 00                         │ *...+...
   }
   
diff --git a/tests/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.panic-abort.diff
index a85dcf9c7ed..a1b433716c8 100644
--- a/tests/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.panic-abort.diff
@@ -46,5 +46,9 @@
           StorageDead(_1);
           return;
       }
++ }
++ 
++ alloc7 (size: 8, align: 4) {
++     01 00 00 00 02 00 00 00                         │ ........
   }
   
diff --git a/tests/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.panic-unwind.diff
index 15ef0fa4dff..2dc514194bc 100644
--- a/tests/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.panic-unwind.diff
@@ -46,5 +46,9 @@
           StorageDead(_1);
           return;
       }
++ }
++ 
++ alloc7 (size: 8, align: 4) {
++     01 00 00 00 02 00 00 00                         │ ........
   }
   
diff --git a/tests/mir-opt/const_prop/repeat.rs b/tests/mir-opt/const_prop/repeat.rs
index 21dba84af37..fb8b825ee36 100644
--- a/tests/mir-opt/const_prop/repeat.rs
+++ b/tests/mir-opt/const_prop/repeat.rs
@@ -1,6 +1,5 @@
 // unit-test: ConstProp
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-// compile-flags: -Zmir-enable-passes=+NormalizeArrayLen
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 
 // EMIT_MIR repeat.main.ConstProp.diff
diff --git a/tests/mir-opt/const_prop/return_place.add.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/return_place.add.ConstProp.panic-abort.diff
index f3b30e0dcde..6c9de476465 100644
--- a/tests/mir-opt/const_prop/return_place.add.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/return_place.add.ConstProp.panic-abort.diff
@@ -17,5 +17,9 @@
 +         _0 = const 4_u32;
           return;
       }
++ }
++ 
++ alloc5 (size: 8, align: 4) {
++     04 00 00 00 00 __ __ __                         │ .....░░░
   }
   
diff --git a/tests/mir-opt/const_prop/return_place.add.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/return_place.add.ConstProp.panic-unwind.diff
index 79f85fcef11..0f079278c43 100644
--- a/tests/mir-opt/const_prop/return_place.add.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/return_place.add.ConstProp.panic-unwind.diff
@@ -17,5 +17,9 @@
 +         _0 = const 4_u32;
           return;
       }
++ }
++ 
++ alloc5 (size: 8, align: 4) {
++     04 00 00 00 00 __ __ __                         │ .....░░░
   }
   
diff --git a/tests/mir-opt/const_prop/return_place.add.PreCodegen.before.panic-abort.mir b/tests/mir-opt/const_prop/return_place.add.PreCodegen.before.panic-abort.mir
index c8f3f641a6d..c2488f3944c 100644
--- a/tests/mir-opt/const_prop/return_place.add.PreCodegen.before.panic-abort.mir
+++ b/tests/mir-opt/const_prop/return_place.add.PreCodegen.before.panic-abort.mir
@@ -14,3 +14,7 @@ fn add() -> u32 {
         return;
     }
 }
+
+alloc5 (size: 8, align: 4) {
+    04 00 00 00 00 __ __ __                         │ .....░░░
+}
diff --git a/tests/mir-opt/const_prop/return_place.add.PreCodegen.before.panic-unwind.mir b/tests/mir-opt/const_prop/return_place.add.PreCodegen.before.panic-unwind.mir
index 9a064697463..fa0b9c77eaf 100644
--- a/tests/mir-opt/const_prop/return_place.add.PreCodegen.before.panic-unwind.mir
+++ b/tests/mir-opt/const_prop/return_place.add.PreCodegen.before.panic-unwind.mir
@@ -14,3 +14,7 @@ fn add() -> u32 {
         return;
     }
 }
+
+alloc5 (size: 8, align: 4) {
+    04 00 00 00 00 __ __ __                         │ .....░░░
+}
diff --git a/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-abort.diff
index 9e705695ac0..988ef7dd225 100644
--- a/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-abort.diff
@@ -29,5 +29,17 @@
           StorageDead(_1);
           return;
       }
++ }
++ 
++ alloc9 (size: 8, align: 4) {
++     01 00 00 00 02 00 00 00                         │ ........
++ }
++ 
++ alloc8 (size: 8, align: 4) {
++     01 00 00 00 02 00 00 00                         │ ........
++ }
++ 
++ alloc6 (size: 8, align: 4) {
++     01 00 00 00 02 00 00 00                         │ ........
   }
   
diff --git a/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-unwind.diff
index 882dd97cc16..29844619720 100644
--- a/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-unwind.diff
@@ -29,5 +29,17 @@
           StorageDead(_1);
           return;
       }
++ }
++ 
++ alloc9 (size: 8, align: 4) {
++     01 00 00 00 02 00 00 00                         │ ........
++ }
++ 
++ alloc8 (size: 8, align: 4) {
++     01 00 00 00 02 00 00 00                         │ ........
++ }
++ 
++ alloc6 (size: 8, align: 4) {
++     01 00 00 00 02 00 00 00                         │ ........
   }
   
diff --git a/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-abort.diff
new file mode 100644
index 00000000000..212ddc5b154
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-abort.diff
@@ -0,0 +1,39 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: [u32; 4];
+      let _3: usize;
+      let mut _4: usize;
+      let mut _5: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32];
+          StorageLive(_3);
+          _3 = const 2_usize;
+-         _4 = Len(_2);
+-         _5 = Lt(_3, _4);
+-         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable];
++         _4 = const 4_usize;
++         _5 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+-         _1 = _2[_3];
++         _1 = _2[2 of 3];
+          StorageDead(_3);
+          StorageDead(_2);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-unwind.diff
new file mode 100644
index 00000000000..5c53d4f4461
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-unwind.diff
@@ -0,0 +1,39 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: [u32; 4];
+      let _3: usize;
+      let mut _4: usize;
+      let mut _5: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32];
+          StorageLive(_3);
+          _3 = const 2_usize;
+-         _4 = Len(_2);
+-         _5 = Lt(_3, _4);
+-         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue];
++         _4 = const 4_usize;
++         _5 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind continue];
+      }
+  
+      bb1: {
+-         _1 = _2[_3];
++         _1 = _2[2 of 3];
+          StorageDead(_3);
+          StorageDead(_2);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-abort.diff
new file mode 100644
index 00000000000..212ddc5b154
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-abort.diff
@@ -0,0 +1,39 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: [u32; 4];
+      let _3: usize;
+      let mut _4: usize;
+      let mut _5: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32];
+          StorageLive(_3);
+          _3 = const 2_usize;
+-         _4 = Len(_2);
+-         _5 = Lt(_3, _4);
+-         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable];
++         _4 = const 4_usize;
++         _5 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+-         _1 = _2[_3];
++         _1 = _2[2 of 3];
+          StorageDead(_3);
+          StorageDead(_2);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-unwind.diff
new file mode 100644
index 00000000000..5c53d4f4461
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-unwind.diff
@@ -0,0 +1,39 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: [u32; 4];
+      let _3: usize;
+      let mut _4: usize;
+      let mut _5: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32];
+          StorageLive(_3);
+          _3 = const 2_usize;
+-         _4 = Len(_2);
+-         _5 = Lt(_3, _4);
+-         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue];
++         _4 = const 4_usize;
++         _5 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind continue];
+      }
+  
+      bb1: {
+-         _1 = _2[_3];
++         _1 = _2[2 of 3];
+          StorageDead(_3);
+          StorageDead(_2);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/array_index.rs b/tests/mir-opt/dataflow-const-prop/array_index.rs
new file mode 100644
index 00000000000..ddb3646ca9b
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/array_index.rs
@@ -0,0 +1,8 @@
+// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
+// unit-test: DataflowConstProp
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
+
+// EMIT_MIR array_index.main.DataflowConstProp.diff
+fn main() {
+    let x: u32 = [0, 1, 2, 3][2];
+}
diff --git a/tests/mir-opt/dataflow-const-prop/boolean_identities.rs b/tests/mir-opt/dataflow-const-prop/boolean_identities.rs
new file mode 100644
index 00000000000..9e911e85b88
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/boolean_identities.rs
@@ -0,0 +1,10 @@
+// unit-test: DataflowConstProp
+
+// EMIT_MIR boolean_identities.test.DataflowConstProp.diff
+pub fn test(x: bool, y: bool) -> bool {
+    (y | true) & (x & false)
+}
+
+fn main() {
+    test(true, false);
+}
diff --git a/tests/mir-opt/dataflow-const-prop/boolean_identities.test.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/boolean_identities.test.DataflowConstProp.diff
new file mode 100644
index 00000000000..5440c38ce4b
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/boolean_identities.test.DataflowConstProp.diff
@@ -0,0 +1,33 @@
+- // MIR for `test` before DataflowConstProp
++ // MIR for `test` after DataflowConstProp
+  
+  fn test(_1: bool, _2: bool) -> bool {
+      debug x => _1;
+      debug y => _2;
+      let mut _0: bool;
+      let mut _3: bool;
+      let mut _4: bool;
+      let mut _5: bool;
+      let mut _6: bool;
+  
+      bb0: {
+          StorageLive(_3);
+          StorageLive(_4);
+          _4 = _2;
+-         _3 = BitOr(move _4, const true);
++         _3 = const true;
+          StorageDead(_4);
+          StorageLive(_5);
+          StorageLive(_6);
+          _6 = _1;
+-         _5 = BitAnd(move _6, const false);
++         _5 = const false;
+          StorageDead(_6);
+-         _0 = BitAnd(move _3, move _5);
++         _0 = const false;
+          StorageDead(_5);
+          StorageDead(_3);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/enum.constant.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/enum.constant.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..07ac5b72e24
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/enum.constant.DataflowConstProp.32bit.diff
@@ -0,0 +1,63 @@
+- // MIR for `constant` before DataflowConstProp
++ // MIR for `constant` after DataflowConstProp
+  
+  fn constant() -> () {
+      let mut _0: ();
+      let _1: E;
+      let mut _3: isize;
+      scope 1 {
+          debug e => _1;
+          let _2: i32;
+          let _4: i32;
+          let _5: i32;
+          scope 2 {
+              debug x => _2;
+          }
+          scope 3 {
+              debug x => _4;
+          }
+          scope 4 {
+              debug x => _5;
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          _1 = const _;
+          StorageLive(_2);
+-         _3 = discriminant(_1);
+-         switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2];
++         _3 = const 0_isize;
++         switchInt(const 0_isize) -> [0: bb3, 1: bb1, otherwise: bb2];
+      }
+  
+      bb1: {
+          StorageLive(_5);
+          _5 = ((_1 as V2).0: i32);
+          _2 = _5;
+          StorageDead(_5);
+          goto -> bb4;
+      }
+  
+      bb2: {
+          unreachable;
+      }
+  
+      bb3: {
+          StorageLive(_4);
+-         _4 = ((_1 as V1).0: i32);
+-         _2 = _4;
++         _4 = const 0_i32;
++         _2 = const 0_i32;
+          StorageDead(_4);
+          goto -> bb4;
+      }
+  
+      bb4: {
+          _0 = const ();
+          StorageDead(_2);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/enum.constant.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/enum.constant.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..07ac5b72e24
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/enum.constant.DataflowConstProp.64bit.diff
@@ -0,0 +1,63 @@
+- // MIR for `constant` before DataflowConstProp
++ // MIR for `constant` after DataflowConstProp
+  
+  fn constant() -> () {
+      let mut _0: ();
+      let _1: E;
+      let mut _3: isize;
+      scope 1 {
+          debug e => _1;
+          let _2: i32;
+          let _4: i32;
+          let _5: i32;
+          scope 2 {
+              debug x => _2;
+          }
+          scope 3 {
+              debug x => _4;
+          }
+          scope 4 {
+              debug x => _5;
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          _1 = const _;
+          StorageLive(_2);
+-         _3 = discriminant(_1);
+-         switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2];
++         _3 = const 0_isize;
++         switchInt(const 0_isize) -> [0: bb3, 1: bb1, otherwise: bb2];
+      }
+  
+      bb1: {
+          StorageLive(_5);
+          _5 = ((_1 as V2).0: i32);
+          _2 = _5;
+          StorageDead(_5);
+          goto -> bb4;
+      }
+  
+      bb2: {
+          unreachable;
+      }
+  
+      bb3: {
+          StorageLive(_4);
+-         _4 = ((_1 as V1).0: i32);
+-         _2 = _4;
++         _4 = const 0_i32;
++         _2 = const 0_i32;
+          StorageDead(_4);
+          goto -> bb4;
+      }
+  
+      bb4: {
+          _0 = const ();
+          StorageDead(_2);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/enum.multiple.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/enum.multiple.DataflowConstProp.32bit.diff
index 775325c4d06..775325c4d06 100644
--- a/tests/mir-opt/dataflow-const-prop/enum.multiple.DataflowConstProp.diff
+++ b/tests/mir-opt/dataflow-const-prop/enum.multiple.DataflowConstProp.32bit.diff
diff --git a/tests/mir-opt/dataflow-const-prop/enum.multiple.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/enum.multiple.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..775325c4d06
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/enum.multiple.DataflowConstProp.64bit.diff
@@ -0,0 +1,82 @@
+- // MIR for `multiple` before DataflowConstProp
++ // MIR for `multiple` after DataflowConstProp
+  
+  fn multiple(_1: bool, _2: u8) -> () {
+      debug x => _1;
+      debug i => _2;
+      let mut _0: ();
+      let _3: std::option::Option<u8>;
+      let mut _4: bool;
+      let mut _5: u8;
+      let mut _7: isize;
+      scope 1 {
+          debug e => _3;
+          let _6: u8;
+          let _8: u8;
+          scope 2 {
+              debug x => _6;
+              let _9: u8;
+              scope 4 {
+                  debug y => _9;
+              }
+          }
+          scope 3 {
+              debug i => _8;
+          }
+      }
+  
+      bb0: {
+          StorageLive(_3);
+          StorageLive(_4);
+          _4 = _1;
+          switchInt(move _4) -> [0: bb2, otherwise: bb1];
+      }
+  
+      bb1: {
+          StorageLive(_5);
+          _5 = _2;
+          _3 = Option::<u8>::Some(move _5);
+          StorageDead(_5);
+          goto -> bb3;
+      }
+  
+      bb2: {
+          _3 = Option::<u8>::None;
+          goto -> bb3;
+      }
+  
+      bb3: {
+          StorageDead(_4);
+          StorageLive(_6);
+          _7 = discriminant(_3);
+          switchInt(move _7) -> [0: bb4, 1: bb6, otherwise: bb5];
+      }
+  
+      bb4: {
+          _6 = const 0_u8;
+          goto -> bb7;
+      }
+  
+      bb5: {
+          unreachable;
+      }
+  
+      bb6: {
+          StorageLive(_8);
+          _8 = ((_3 as Some).0: u8);
+          _6 = _8;
+          StorageDead(_8);
+          goto -> bb7;
+      }
+  
+      bb7: {
+          StorageLive(_9);
+          _9 = _6;
+          _0 = const ();
+          StorageDead(_9);
+          StorageDead(_6);
+          StorageDead(_3);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/enum.mutate_discriminant.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/enum.mutate_discriminant.DataflowConstProp.32bit.diff
index 960e69ee916..960e69ee916 100644
--- a/tests/mir-opt/dataflow-const-prop/enum.mutate_discriminant.DataflowConstProp.diff
+++ b/tests/mir-opt/dataflow-const-prop/enum.mutate_discriminant.DataflowConstProp.32bit.diff
diff --git a/tests/mir-opt/dataflow-const-prop/enum.mutate_discriminant.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/enum.mutate_discriminant.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..960e69ee916
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/enum.mutate_discriminant.DataflowConstProp.64bit.diff
@@ -0,0 +1,26 @@
+- // MIR for `mutate_discriminant` before DataflowConstProp
++ // MIR for `mutate_discriminant` after DataflowConstProp
+  
+  fn mutate_discriminant() -> u8 {
+      let mut _0: u8;
+      let mut _1: std::option::Option<NonZeroUsize>;
+      let mut _2: isize;
+  
+      bb0: {
+          discriminant(_1) = 1;
+          (((_1 as variant#1).0: NonZeroUsize).0: usize) = const 0_usize;
+          _2 = discriminant(_1);
+          switchInt(_2) -> [0: bb1, otherwise: bb2];
+      }
+  
+      bb1: {
+          _0 = const 1_u8;
+          return;
+      }
+  
+      bb2: {
+          _0 = const 2_u8;
+          unreachable;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/enum.rs b/tests/mir-opt/dataflow-const-prop/enum.rs
index 79a20d7ef45..5a10e9e883d 100644
--- a/tests/mir-opt/dataflow-const-prop/enum.rs
+++ b/tests/mir-opt/dataflow-const-prop/enum.rs
@@ -1,9 +1,11 @@
 // unit-test: DataflowConstProp
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
 
 #![feature(custom_mir, core_intrinsics, rustc_attrs)]
 
 use std::intrinsics::mir::*;
 
+#[derive(Copy, Clone)]
 enum E {
     V1(i32),
     V2(i32)
@@ -15,6 +17,24 @@ fn simple() {
     let x = match e { E::V1(x) => x, E::V2(x) => x };
 }
 
+// EMIT_MIR enum.constant.DataflowConstProp.diff
+fn constant() {
+    const C: E = E::V1(0);
+    let e = C;
+    let x = match e { E::V1(x) => x, E::V2(x) => x };
+}
+
+// EMIT_MIR enum.statics.DataflowConstProp.diff
+fn statics() {
+    static C: E = E::V1(0);
+    let e = C;
+    let x = match e { E::V1(x) => x, E::V2(x) => x };
+
+    static RC: &E = &E::V2(4);
+    let e = RC;
+    let x = match e { E::V1(x) => x, E::V2(x) => x };
+}
+
 #[rustc_layout_scalar_valid_range_start(1)]
 #[rustc_nonnull_optimization_guaranteed]
 struct NonZeroUsize(usize);
@@ -63,6 +83,8 @@ fn multiple(x: bool, i: u8) {
 
 fn main() {
     simple();
+    constant();
+    statics();
     mutate_discriminant();
     multiple(false, 5);
 }
diff --git a/tests/mir-opt/dataflow-const-prop/enum.simple.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/enum.simple.DataflowConstProp.32bit.diff
index 3946e7c7d96..3946e7c7d96 100644
--- a/tests/mir-opt/dataflow-const-prop/enum.simple.DataflowConstProp.diff
+++ b/tests/mir-opt/dataflow-const-prop/enum.simple.DataflowConstProp.32bit.diff
diff --git a/tests/mir-opt/dataflow-const-prop/enum.simple.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/enum.simple.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..3946e7c7d96
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/enum.simple.DataflowConstProp.64bit.diff
@@ -0,0 +1,63 @@
+- // MIR for `simple` before DataflowConstProp
++ // MIR for `simple` after DataflowConstProp
+  
+  fn simple() -> () {
+      let mut _0: ();
+      let _1: E;
+      let mut _3: isize;
+      scope 1 {
+          debug e => _1;
+          let _2: i32;
+          let _4: i32;
+          let _5: i32;
+          scope 2 {
+              debug x => _2;
+          }
+          scope 3 {
+              debug x => _4;
+          }
+          scope 4 {
+              debug x => _5;
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          _1 = E::V1(const 0_i32);
+          StorageLive(_2);
+-         _3 = discriminant(_1);
+-         switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2];
++         _3 = const 0_isize;
++         switchInt(const 0_isize) -> [0: bb3, 1: bb1, otherwise: bb2];
+      }
+  
+      bb1: {
+          StorageLive(_5);
+          _5 = ((_1 as V2).0: i32);
+          _2 = _5;
+          StorageDead(_5);
+          goto -> bb4;
+      }
+  
+      bb2: {
+          unreachable;
+      }
+  
+      bb3: {
+          StorageLive(_4);
+-         _4 = ((_1 as V1).0: i32);
+-         _2 = _4;
++         _4 = const 0_i32;
++         _2 = const 0_i32;
+          StorageDead(_4);
+          goto -> bb4;
+      }
+  
+      bb4: {
+          _0 = const ();
+          StorageDead(_2);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/enum.statics.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/enum.statics.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..ae8b44c953e
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/enum.statics.DataflowConstProp.32bit.diff
@@ -0,0 +1,126 @@
+- // MIR for `statics` before DataflowConstProp
++ // MIR for `statics` after DataflowConstProp
+  
+  fn statics() -> () {
+      let mut _0: ();
+      let _1: E;
+      let mut _2: &E;
+      let mut _4: isize;
+      let mut _8: &&E;
+      let mut _10: isize;
+      scope 1 {
+          debug e => _1;
+          let _3: i32;
+          let _5: i32;
+          let _6: i32;
+          scope 2 {
+              debug x => _3;
+              let _7: &E;
+              scope 5 {
+                  debug e => _7;
+                  let _9: &i32;
+                  let _11: &i32;
+                  let _12: &i32;
+                  scope 6 {
+                      debug x => _9;
+                  }
+                  scope 7 {
+                      debug x => _11;
+                  }
+                  scope 8 {
+                      debug x => _12;
+                  }
+              }
+          }
+          scope 3 {
+              debug x => _5;
+          }
+          scope 4 {
+              debug x => _6;
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = const {alloc1: &E};
+          _1 = (*_2);
+          StorageDead(_2);
+          StorageLive(_3);
+-         _4 = discriminant(_1);
+-         switchInt(move _4) -> [0: bb3, 1: bb1, otherwise: bb2];
++         _4 = const 0_isize;
++         switchInt(const 0_isize) -> [0: bb3, 1: bb1, otherwise: bb2];
+      }
+  
+      bb1: {
+          StorageLive(_6);
+          _6 = ((_1 as V2).0: i32);
+          _3 = _6;
+          StorageDead(_6);
+          goto -> bb4;
+      }
+  
+      bb2: {
+          unreachable;
+      }
+  
+      bb3: {
+          StorageLive(_5);
+-         _5 = ((_1 as V1).0: i32);
+-         _3 = _5;
++         _5 = const 0_i32;
++         _3 = const 0_i32;
+          StorageDead(_5);
+          goto -> bb4;
+      }
+  
+      bb4: {
+          StorageLive(_7);
+          StorageLive(_8);
+          _8 = const {alloc2: &&E};
+          _7 = (*_8);
+          StorageDead(_8);
+          StorageLive(_9);
+          _10 = discriminant((*_7));
+          switchInt(move _10) -> [0: bb6, 1: bb5, otherwise: bb2];
+      }
+  
+      bb5: {
+          StorageLive(_12);
+          _12 = &(((*_7) as V2).0: i32);
+          _9 = &(*_12);
+          StorageDead(_12);
+          goto -> bb7;
+      }
+  
+      bb6: {
+          StorageLive(_11);
+          _11 = &(((*_7) as V1).0: i32);
+          _9 = _11;
+          StorageDead(_11);
+          goto -> bb7;
+      }
+  
+      bb7: {
+          _0 = const ();
+          StorageDead(_9);
+          StorageDead(_7);
+          StorageDead(_3);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
+  alloc2 (static: RC, size: 4, align: 4) {
+      ╾─alloc14─╼                                     │ ╾──╼
+  }
+  
+  alloc14 (size: 8, align: 4) {
+      01 00 00 00 04 00 00 00                         │ ........
+  }
+  
+  alloc1 (static: statics::C, size: 8, align: 4) {
+      00 00 00 00 00 00 00 00                         │ ........
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/enum.statics.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/enum.statics.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..63799b3bac3
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/enum.statics.DataflowConstProp.64bit.diff
@@ -0,0 +1,126 @@
+- // MIR for `statics` before DataflowConstProp
++ // MIR for `statics` after DataflowConstProp
+  
+  fn statics() -> () {
+      let mut _0: ();
+      let _1: E;
+      let mut _2: &E;
+      let mut _4: isize;
+      let mut _8: &&E;
+      let mut _10: isize;
+      scope 1 {
+          debug e => _1;
+          let _3: i32;
+          let _5: i32;
+          let _6: i32;
+          scope 2 {
+              debug x => _3;
+              let _7: &E;
+              scope 5 {
+                  debug e => _7;
+                  let _9: &i32;
+                  let _11: &i32;
+                  let _12: &i32;
+                  scope 6 {
+                      debug x => _9;
+                  }
+                  scope 7 {
+                      debug x => _11;
+                  }
+                  scope 8 {
+                      debug x => _12;
+                  }
+              }
+          }
+          scope 3 {
+              debug x => _5;
+          }
+          scope 4 {
+              debug x => _6;
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = const {alloc1: &E};
+          _1 = (*_2);
+          StorageDead(_2);
+          StorageLive(_3);
+-         _4 = discriminant(_1);
+-         switchInt(move _4) -> [0: bb3, 1: bb1, otherwise: bb2];
++         _4 = const 0_isize;
++         switchInt(const 0_isize) -> [0: bb3, 1: bb1, otherwise: bb2];
+      }
+  
+      bb1: {
+          StorageLive(_6);
+          _6 = ((_1 as V2).0: i32);
+          _3 = _6;
+          StorageDead(_6);
+          goto -> bb4;
+      }
+  
+      bb2: {
+          unreachable;
+      }
+  
+      bb3: {
+          StorageLive(_5);
+-         _5 = ((_1 as V1).0: i32);
+-         _3 = _5;
++         _5 = const 0_i32;
++         _3 = const 0_i32;
+          StorageDead(_5);
+          goto -> bb4;
+      }
+  
+      bb4: {
+          StorageLive(_7);
+          StorageLive(_8);
+          _8 = const {alloc2: &&E};
+          _7 = (*_8);
+          StorageDead(_8);
+          StorageLive(_9);
+          _10 = discriminant((*_7));
+          switchInt(move _10) -> [0: bb6, 1: bb5, otherwise: bb2];
+      }
+  
+      bb5: {
+          StorageLive(_12);
+          _12 = &(((*_7) as V2).0: i32);
+          _9 = &(*_12);
+          StorageDead(_12);
+          goto -> bb7;
+      }
+  
+      bb6: {
+          StorageLive(_11);
+          _11 = &(((*_7) as V1).0: i32);
+          _9 = _11;
+          StorageDead(_11);
+          goto -> bb7;
+      }
+  
+      bb7: {
+          _0 = const ();
+          StorageDead(_9);
+          StorageDead(_7);
+          StorageDead(_3);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
+  alloc2 (static: RC, size: 8, align: 8) {
+      ╾───────alloc14───────╼                         │ ╾──────╼
+  }
+  
+  alloc14 (size: 8, align: 4) {
+      01 00 00 00 04 00 00 00                         │ ........
+  }
+  
+  alloc1 (static: statics::C, size: 8, align: 4) {
+      00 00 00 00 00 00 00 00                         │ ........
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-abort.diff
new file mode 100644
index 00000000000..6c612d46725
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-abort.diff
@@ -0,0 +1,39 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u8;
+      let mut _2: [u8; 5000];
+      let _3: usize;
+      let mut _4: usize;
+      let mut _5: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = [const 0_u8; 5000];
+          StorageLive(_3);
+          _3 = const 2_usize;
+-         _4 = Len(_2);
+-         _5 = Lt(_3, _4);
+-         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable];
++         _4 = const 5000_usize;
++         _5 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+-         _1 = _2[_3];
++         _1 = _2[2 of 3];
+          StorageDead(_3);
+          StorageDead(_2);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-unwind.diff
new file mode 100644
index 00000000000..87024da2628
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-unwind.diff
@@ -0,0 +1,39 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u8;
+      let mut _2: [u8; 5000];
+      let _3: usize;
+      let mut _4: usize;
+      let mut _5: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = [const 0_u8; 5000];
+          StorageLive(_3);
+          _3 = const 2_usize;
+-         _4 = Len(_2);
+-         _5 = Lt(_3, _4);
+-         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue];
++         _4 = const 5000_usize;
++         _5 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind continue];
+      }
+  
+      bb1: {
+-         _1 = _2[_3];
++         _1 = _2[2 of 3];
+          StorageDead(_3);
+          StorageDead(_2);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-abort.diff
new file mode 100644
index 00000000000..6c612d46725
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-abort.diff
@@ -0,0 +1,39 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u8;
+      let mut _2: [u8; 5000];
+      let _3: usize;
+      let mut _4: usize;
+      let mut _5: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = [const 0_u8; 5000];
+          StorageLive(_3);
+          _3 = const 2_usize;
+-         _4 = Len(_2);
+-         _5 = Lt(_3, _4);
+-         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable];
++         _4 = const 5000_usize;
++         _5 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+-         _1 = _2[_3];
++         _1 = _2[2 of 3];
+          StorageDead(_3);
+          StorageDead(_2);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-unwind.diff
new file mode 100644
index 00000000000..87024da2628
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-unwind.diff
@@ -0,0 +1,39 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u8;
+      let mut _2: [u8; 5000];
+      let _3: usize;
+      let mut _4: usize;
+      let mut _5: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = [const 0_u8; 5000];
+          StorageLive(_3);
+          _3 = const 2_usize;
+-         _4 = Len(_2);
+-         _5 = Lt(_3, _4);
+-         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue];
++         _4 = const 5000_usize;
++         _5 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind continue];
+      }
+  
+      bb1: {
+-         _1 = _2[_3];
++         _1 = _2[2 of 3];
+          StorageDead(_3);
+          StorageDead(_2);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/large_array_index.rs b/tests/mir-opt/dataflow-const-prop/large_array_index.rs
new file mode 100644
index 00000000000..af13c7d1020
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/large_array_index.rs
@@ -0,0 +1,9 @@
+// unit-test: DataflowConstProp
+// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
+
+// EMIT_MIR large_array_index.main.DataflowConstProp.diff
+fn main() {
+    // check that we don't propagate this, because it's too large
+    let x: u8 = [0_u8; 5000][2];
+}
diff --git a/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs b/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs
new file mode 100644
index 00000000000..dbea1480445
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs
@@ -0,0 +1,10 @@
+// unit-test: DataflowConstProp
+
+// EMIT_MIR mult_by_zero.test.DataflowConstProp.diff
+fn test(x : i32) -> i32 {
+  x * 0
+}
+
+fn main() {
+    test(10);
+}
diff --git a/tests/mir-opt/dataflow-const-prop/mult_by_zero.test.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/mult_by_zero.test.DataflowConstProp.diff
new file mode 100644
index 00000000000..91bc10a562f
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/mult_by_zero.test.DataflowConstProp.diff
@@ -0,0 +1,18 @@
+- // MIR for `test` before DataflowConstProp
++ // MIR for `test` after DataflowConstProp
+  
+  fn test(_1: i32) -> i32 {
+      debug x => _1;
+      let mut _0: i32;
+      let mut _2: i32;
+  
+      bb0: {
+          StorageLive(_2);
+          _2 = _1;
+-         _0 = Mul(move _2, const 0_i32);
++         _0 = const 0_i32;
+          StorageDead(_2);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-abort.diff
new file mode 100644
index 00000000000..c61414b6541
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-abort.diff
@@ -0,0 +1,76 @@
+- // MIR for `concrete` before DataflowConstProp
++ // MIR for `concrete` after DataflowConstProp
+  
+  fn concrete() -> () {
+      let mut _0: ();
+      let _1: usize;
+      let mut _2: usize;
+      let mut _4: usize;
+      let mut _6: usize;
+      let mut _8: usize;
+      scope 1 {
+          debug x => _1;
+          let _3: usize;
+          scope 2 {
+              debug y => _3;
+              let _5: usize;
+              scope 3 {
+                  debug z0 => _5;
+                  let _7: usize;
+                  scope 4 {
+                      debug z1 => _7;
+                  }
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+-         _2 = OffsetOf(Alpha, [0]);
+-         _1 = must_use::<usize>(move _2) -> [return: bb1, unwind unreachable];
++         _2 = const 4_usize;
++         _1 = must_use::<usize>(const 4_usize) -> [return: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+          StorageDead(_2);
+          StorageLive(_3);
+          StorageLive(_4);
+-         _4 = OffsetOf(Alpha, [1]);
+-         _3 = must_use::<usize>(move _4) -> [return: bb2, unwind unreachable];
++         _4 = const 0_usize;
++         _3 = must_use::<usize>(const 0_usize) -> [return: bb2, unwind unreachable];
+      }
+  
+      bb2: {
+          StorageDead(_4);
+          StorageLive(_5);
+          StorageLive(_6);
+-         _6 = OffsetOf(Alpha, [2, 0]);
+-         _5 = must_use::<usize>(move _6) -> [return: bb3, unwind unreachable];
++         _6 = const 2_usize;
++         _5 = must_use::<usize>(const 2_usize) -> [return: bb3, unwind unreachable];
+      }
+  
+      bb3: {
+          StorageDead(_6);
+          StorageLive(_7);
+          StorageLive(_8);
+-         _8 = OffsetOf(Alpha, [2, 1]);
+-         _7 = must_use::<usize>(move _8) -> [return: bb4, unwind unreachable];
++         _8 = const 3_usize;
++         _7 = must_use::<usize>(const 3_usize) -> [return: bb4, unwind unreachable];
+      }
+  
+      bb4: {
+          StorageDead(_8);
+          _0 = const ();
+          StorageDead(_7);
+          StorageDead(_5);
+          StorageDead(_3);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-unwind.diff
new file mode 100644
index 00000000000..0c3939a3456
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-unwind.diff
@@ -0,0 +1,76 @@
+- // MIR for `concrete` before DataflowConstProp
++ // MIR for `concrete` after DataflowConstProp
+  
+  fn concrete() -> () {
+      let mut _0: ();
+      let _1: usize;
+      let mut _2: usize;
+      let mut _4: usize;
+      let mut _6: usize;
+      let mut _8: usize;
+      scope 1 {
+          debug x => _1;
+          let _3: usize;
+          scope 2 {
+              debug y => _3;
+              let _5: usize;
+              scope 3 {
+                  debug z0 => _5;
+                  let _7: usize;
+                  scope 4 {
+                      debug z1 => _7;
+                  }
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+-         _2 = OffsetOf(Alpha, [0]);
+-         _1 = must_use::<usize>(move _2) -> [return: bb1, unwind continue];
++         _2 = const 4_usize;
++         _1 = must_use::<usize>(const 4_usize) -> [return: bb1, unwind continue];
+      }
+  
+      bb1: {
+          StorageDead(_2);
+          StorageLive(_3);
+          StorageLive(_4);
+-         _4 = OffsetOf(Alpha, [1]);
+-         _3 = must_use::<usize>(move _4) -> [return: bb2, unwind continue];
++         _4 = const 0_usize;
++         _3 = must_use::<usize>(const 0_usize) -> [return: bb2, unwind continue];
+      }
+  
+      bb2: {
+          StorageDead(_4);
+          StorageLive(_5);
+          StorageLive(_6);
+-         _6 = OffsetOf(Alpha, [2, 0]);
+-         _5 = must_use::<usize>(move _6) -> [return: bb3, unwind continue];
++         _6 = const 2_usize;
++         _5 = must_use::<usize>(const 2_usize) -> [return: bb3, unwind continue];
+      }
+  
+      bb3: {
+          StorageDead(_6);
+          StorageLive(_7);
+          StorageLive(_8);
+-         _8 = OffsetOf(Alpha, [2, 1]);
+-         _7 = must_use::<usize>(move _8) -> [return: bb4, unwind continue];
++         _8 = const 3_usize;
++         _7 = must_use::<usize>(const 3_usize) -> [return: bb4, unwind continue];
+      }
+  
+      bb4: {
+          StorageDead(_8);
+          _0 = const ();
+          StorageDead(_7);
+          StorageDead(_5);
+          StorageDead(_3);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-abort.diff
new file mode 100644
index 00000000000..d54d4687060
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-abort.diff
@@ -0,0 +1,72 @@
+- // MIR for `generic` before DataflowConstProp
++ // MIR for `generic` after DataflowConstProp
+  
+  fn generic() -> () {
+      let mut _0: ();
+      let _1: usize;
+      let mut _2: usize;
+      let mut _4: usize;
+      let mut _6: usize;
+      let mut _8: usize;
+      scope 1 {
+          debug gx => _1;
+          let _3: usize;
+          scope 2 {
+              debug gy => _3;
+              let _5: usize;
+              scope 3 {
+                  debug dx => _5;
+                  let _7: usize;
+                  scope 4 {
+                      debug dy => _7;
+                  }
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = OffsetOf(Gamma<T>, [0]);
+          _1 = must_use::<usize>(move _2) -> [return: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+          StorageDead(_2);
+          StorageLive(_3);
+          StorageLive(_4);
+          _4 = OffsetOf(Gamma<T>, [1]);
+          _3 = must_use::<usize>(move _4) -> [return: bb2, unwind unreachable];
+      }
+  
+      bb2: {
+          StorageDead(_4);
+          StorageLive(_5);
+          StorageLive(_6);
+-         _6 = OffsetOf(Delta<T>, [1]);
+-         _5 = must_use::<usize>(move _6) -> [return: bb3, unwind unreachable];
++         _6 = const 0_usize;
++         _5 = must_use::<usize>(const 0_usize) -> [return: bb3, unwind unreachable];
+      }
+  
+      bb3: {
+          StorageDead(_6);
+          StorageLive(_7);
+          StorageLive(_8);
+-         _8 = OffsetOf(Delta<T>, [2]);
+-         _7 = must_use::<usize>(move _8) -> [return: bb4, unwind unreachable];
++         _8 = const 2_usize;
++         _7 = must_use::<usize>(const 2_usize) -> [return: bb4, unwind unreachable];
+      }
+  
+      bb4: {
+          StorageDead(_8);
+          _0 = const ();
+          StorageDead(_7);
+          StorageDead(_5);
+          StorageDead(_3);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-unwind.diff
new file mode 100644
index 00000000000..6032a2274ef
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-unwind.diff
@@ -0,0 +1,72 @@
+- // MIR for `generic` before DataflowConstProp
++ // MIR for `generic` after DataflowConstProp
+  
+  fn generic() -> () {
+      let mut _0: ();
+      let _1: usize;
+      let mut _2: usize;
+      let mut _4: usize;
+      let mut _6: usize;
+      let mut _8: usize;
+      scope 1 {
+          debug gx => _1;
+          let _3: usize;
+          scope 2 {
+              debug gy => _3;
+              let _5: usize;
+              scope 3 {
+                  debug dx => _5;
+                  let _7: usize;
+                  scope 4 {
+                      debug dy => _7;
+                  }
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = OffsetOf(Gamma<T>, [0]);
+          _1 = must_use::<usize>(move _2) -> [return: bb1, unwind continue];
+      }
+  
+      bb1: {
+          StorageDead(_2);
+          StorageLive(_3);
+          StorageLive(_4);
+          _4 = OffsetOf(Gamma<T>, [1]);
+          _3 = must_use::<usize>(move _4) -> [return: bb2, unwind continue];
+      }
+  
+      bb2: {
+          StorageDead(_4);
+          StorageLive(_5);
+          StorageLive(_6);
+-         _6 = OffsetOf(Delta<T>, [1]);
+-         _5 = must_use::<usize>(move _6) -> [return: bb3, unwind continue];
++         _6 = const 0_usize;
++         _5 = must_use::<usize>(const 0_usize) -> [return: bb3, unwind continue];
+      }
+  
+      bb3: {
+          StorageDead(_6);
+          StorageLive(_7);
+          StorageLive(_8);
+-         _8 = OffsetOf(Delta<T>, [2]);
+-         _7 = must_use::<usize>(move _8) -> [return: bb4, unwind continue];
++         _8 = const 2_usize;
++         _7 = must_use::<usize>(const 2_usize) -> [return: bb4, unwind continue];
+      }
+  
+      bb4: {
+          StorageDead(_8);
+          _0 = const ();
+          StorageDead(_7);
+          StorageDead(_5);
+          StorageDead(_3);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/offset_of.rs b/tests/mir-opt/dataflow-const-prop/offset_of.rs
new file mode 100644
index 00000000000..ccc90790e52
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/offset_of.rs
@@ -0,0 +1,49 @@
+// unit-test: DataflowConstProp
+// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
+
+#![feature(offset_of)]
+
+use std::marker::PhantomData;
+use std::mem::offset_of;
+
+struct Alpha {
+    x: u8,
+    y: u16,
+    z: Beta,
+}
+
+struct Beta(u8, u8);
+
+struct Gamma<T> {
+    x: u8,
+    y: u16,
+    _t: T,
+}
+
+#[repr(C)]
+struct Delta<T> {
+    _phantom: PhantomData<T>,
+    x: u8,
+    y: u16,
+}
+
+// EMIT_MIR offset_of.concrete.DataflowConstProp.diff
+fn concrete() {
+    let x = offset_of!(Alpha, x);
+    let y = offset_of!(Alpha, y);
+    let z0 = offset_of!(Alpha, z.0);
+    let z1 = offset_of!(Alpha, z.1);
+}
+
+// EMIT_MIR offset_of.generic.DataflowConstProp.diff
+fn generic<T>() {
+    let gx = offset_of!(Gamma<T>, x);
+    let gy = offset_of!(Gamma<T>, y);
+    let dx = offset_of!(Delta<T>, x);
+    let dy = offset_of!(Delta<T>, y);
+}
+
+fn main() {
+    concrete();
+    generic::<()>();
+}
diff --git a/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-abort.diff
new file mode 100644
index 00000000000..a18ef6c9db7
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-abort.diff
@@ -0,0 +1,43 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: u32;
+      let mut _3: [u32; 8];
+      let _4: usize;
+      let mut _5: usize;
+      let mut _6: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          StorageLive(_3);
+          _3 = [const 42_u32; 8];
+          StorageLive(_4);
+          _4 = const 2_usize;
+-         _5 = Len(_3);
+-         _6 = Lt(_4, _5);
+-         assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind unreachable];
++         _5 = const 8_usize;
++         _6 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+-         _2 = _3[_4];
++         _2 = _3[2 of 3];
+          _1 = Add(move _2, const 0_u32);
+          StorageDead(_2);
+          StorageDead(_4);
+          StorageDead(_3);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-unwind.diff
new file mode 100644
index 00000000000..3356ef98b14
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-unwind.diff
@@ -0,0 +1,43 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: u32;
+      let mut _3: [u32; 8];
+      let _4: usize;
+      let mut _5: usize;
+      let mut _6: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          StorageLive(_3);
+          _3 = [const 42_u32; 8];
+          StorageLive(_4);
+          _4 = const 2_usize;
+-         _5 = Len(_3);
+-         _6 = Lt(_4, _5);
+-         assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind continue];
++         _5 = const 8_usize;
++         _6 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind continue];
+      }
+  
+      bb1: {
+-         _2 = _3[_4];
++         _2 = _3[2 of 3];
+          _1 = Add(move _2, const 0_u32);
+          StorageDead(_2);
+          StorageDead(_4);
+          StorageDead(_3);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-abort.diff
new file mode 100644
index 00000000000..a18ef6c9db7
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-abort.diff
@@ -0,0 +1,43 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: u32;
+      let mut _3: [u32; 8];
+      let _4: usize;
+      let mut _5: usize;
+      let mut _6: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          StorageLive(_3);
+          _3 = [const 42_u32; 8];
+          StorageLive(_4);
+          _4 = const 2_usize;
+-         _5 = Len(_3);
+-         _6 = Lt(_4, _5);
+-         assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind unreachable];
++         _5 = const 8_usize;
++         _6 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+-         _2 = _3[_4];
++         _2 = _3[2 of 3];
+          _1 = Add(move _2, const 0_u32);
+          StorageDead(_2);
+          StorageDead(_4);
+          StorageDead(_3);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-unwind.diff
new file mode 100644
index 00000000000..3356ef98b14
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-unwind.diff
@@ -0,0 +1,43 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: u32;
+      let mut _3: [u32; 8];
+      let _4: usize;
+      let mut _5: usize;
+      let mut _6: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          StorageLive(_3);
+          _3 = [const 42_u32; 8];
+          StorageLive(_4);
+          _4 = const 2_usize;
+-         _5 = Len(_3);
+-         _6 = Lt(_4, _5);
+-         assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind continue];
++         _5 = const 8_usize;
++         _6 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind continue];
+      }
+  
+      bb1: {
+-         _2 = _3[_4];
++         _2 = _3[2 of 3];
+          _1 = Add(move _2, const 0_u32);
+          StorageDead(_2);
+          StorageDead(_4);
+          StorageDead(_3);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/repeat.rs b/tests/mir-opt/dataflow-const-prop/repeat.rs
new file mode 100644
index 00000000000..9fa353e44c5
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/repeat.rs
@@ -0,0 +1,8 @@
+// unit-test: DataflowConstProp
+// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
+
+// EMIT_MIR repeat.main.DataflowConstProp.diff
+fn main() {
+    let x: u32 = [42; 8][2] + 0;
+}
diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff
new file mode 100644
index 00000000000..e99b413f708
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff
@@ -0,0 +1,77 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: &[u32];
+      let mut _3: &[u32; 3];
+      let _4: &[u32; 3];
+      let _5: [u32; 3];
+      let _6: usize;
+      let mut _7: usize;
+      let mut _8: bool;
+      let mut _10: &[u32];
+      let _11: usize;
+      let mut _12: usize;
+      let mut _13: bool;
+      let mut _14: &[u32; 3];
+      scope 1 {
+          debug local => _1;
+          let _9: u32;
+          scope 2 {
+              debug constant => _9;
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          StorageLive(_3);
+          StorageLive(_4);
+          _14 = const _;
+          _4 = _14;
+          _3 = _4;
+          _2 = move _3 as &[u32] (PointerCoercion(Unsize));
+          StorageDead(_3);
+          StorageLive(_6);
+          _6 = const 1_usize;
+-         _7 = Len((*_2));
+-         _8 = Lt(_6, _7);
+-         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
++         _7 = const 3_usize;
++         _8 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+-         _1 = (*_2)[_6];
++         _1 = (*_2)[1 of 2];
+          StorageDead(_6);
+          StorageDead(_4);
+          StorageDead(_2);
+          StorageLive(_9);
+          StorageLive(_10);
+          _10 = const _;
+          StorageLive(_11);
+          _11 = const 1_usize;
+-         _12 = Len((*_10));
+-         _13 = Lt(_11, _12);
+-         assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb2, unwind unreachable];
++         _12 = const 3_usize;
++         _13 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind unreachable];
+      }
+  
+      bb2: {
+-         _9 = (*_10)[_11];
++         _9 = (*_10)[1 of 2];
+          StorageDead(_11);
+          StorageDead(_10);
+          _0 = const ();
+          StorageDead(_9);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff
new file mode 100644
index 00000000000..759a793fbf3
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff
@@ -0,0 +1,77 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: &[u32];
+      let mut _3: &[u32; 3];
+      let _4: &[u32; 3];
+      let _5: [u32; 3];
+      let _6: usize;
+      let mut _7: usize;
+      let mut _8: bool;
+      let mut _10: &[u32];
+      let _11: usize;
+      let mut _12: usize;
+      let mut _13: bool;
+      let mut _14: &[u32; 3];
+      scope 1 {
+          debug local => _1;
+          let _9: u32;
+          scope 2 {
+              debug constant => _9;
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          StorageLive(_3);
+          StorageLive(_4);
+          _14 = const _;
+          _4 = _14;
+          _3 = _4;
+          _2 = move _3 as &[u32] (PointerCoercion(Unsize));
+          StorageDead(_3);
+          StorageLive(_6);
+          _6 = const 1_usize;
+-         _7 = Len((*_2));
+-         _8 = Lt(_6, _7);
+-         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
++         _7 = const 3_usize;
++         _8 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind continue];
+      }
+  
+      bb1: {
+-         _1 = (*_2)[_6];
++         _1 = (*_2)[1 of 2];
+          StorageDead(_6);
+          StorageDead(_4);
+          StorageDead(_2);
+          StorageLive(_9);
+          StorageLive(_10);
+          _10 = const _;
+          StorageLive(_11);
+          _11 = const 1_usize;
+-         _12 = Len((*_10));
+-         _13 = Lt(_11, _12);
+-         assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb2, unwind continue];
++         _12 = const 3_usize;
++         _13 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind continue];
+      }
+  
+      bb2: {
+-         _9 = (*_10)[_11];
++         _9 = (*_10)[1 of 2];
+          StorageDead(_11);
+          StorageDead(_10);
+          _0 = const ();
+          StorageDead(_9);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff
new file mode 100644
index 00000000000..e99b413f708
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff
@@ -0,0 +1,77 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: &[u32];
+      let mut _3: &[u32; 3];
+      let _4: &[u32; 3];
+      let _5: [u32; 3];
+      let _6: usize;
+      let mut _7: usize;
+      let mut _8: bool;
+      let mut _10: &[u32];
+      let _11: usize;
+      let mut _12: usize;
+      let mut _13: bool;
+      let mut _14: &[u32; 3];
+      scope 1 {
+          debug local => _1;
+          let _9: u32;
+          scope 2 {
+              debug constant => _9;
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          StorageLive(_3);
+          StorageLive(_4);
+          _14 = const _;
+          _4 = _14;
+          _3 = _4;
+          _2 = move _3 as &[u32] (PointerCoercion(Unsize));
+          StorageDead(_3);
+          StorageLive(_6);
+          _6 = const 1_usize;
+-         _7 = Len((*_2));
+-         _8 = Lt(_6, _7);
+-         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
++         _7 = const 3_usize;
++         _8 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+-         _1 = (*_2)[_6];
++         _1 = (*_2)[1 of 2];
+          StorageDead(_6);
+          StorageDead(_4);
+          StorageDead(_2);
+          StorageLive(_9);
+          StorageLive(_10);
+          _10 = const _;
+          StorageLive(_11);
+          _11 = const 1_usize;
+-         _12 = Len((*_10));
+-         _13 = Lt(_11, _12);
+-         assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb2, unwind unreachable];
++         _12 = const 3_usize;
++         _13 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind unreachable];
+      }
+  
+      bb2: {
+-         _9 = (*_10)[_11];
++         _9 = (*_10)[1 of 2];
+          StorageDead(_11);
+          StorageDead(_10);
+          _0 = const ();
+          StorageDead(_9);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff
new file mode 100644
index 00000000000..759a793fbf3
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff
@@ -0,0 +1,77 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: &[u32];
+      let mut _3: &[u32; 3];
+      let _4: &[u32; 3];
+      let _5: [u32; 3];
+      let _6: usize;
+      let mut _7: usize;
+      let mut _8: bool;
+      let mut _10: &[u32];
+      let _11: usize;
+      let mut _12: usize;
+      let mut _13: bool;
+      let mut _14: &[u32; 3];
+      scope 1 {
+          debug local => _1;
+          let _9: u32;
+          scope 2 {
+              debug constant => _9;
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          StorageLive(_3);
+          StorageLive(_4);
+          _14 = const _;
+          _4 = _14;
+          _3 = _4;
+          _2 = move _3 as &[u32] (PointerCoercion(Unsize));
+          StorageDead(_3);
+          StorageLive(_6);
+          _6 = const 1_usize;
+-         _7 = Len((*_2));
+-         _8 = Lt(_6, _7);
+-         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
++         _7 = const 3_usize;
++         _8 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind continue];
+      }
+  
+      bb1: {
+-         _1 = (*_2)[_6];
++         _1 = (*_2)[1 of 2];
+          StorageDead(_6);
+          StorageDead(_4);
+          StorageDead(_2);
+          StorageLive(_9);
+          StorageLive(_10);
+          _10 = const _;
+          StorageLive(_11);
+          _11 = const 1_usize;
+-         _12 = Len((*_10));
+-         _13 = Lt(_11, _12);
+-         assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb2, unwind continue];
++         _12 = const 3_usize;
++         _13 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind continue];
+      }
+  
+      bb2: {
+-         _9 = (*_10)[_11];
++         _9 = (*_10)[1 of 2];
+          StorageDead(_11);
+          StorageDead(_10);
+          _0 = const ();
+          StorageDead(_9);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.rs b/tests/mir-opt/dataflow-const-prop/slice_len.rs
new file mode 100644
index 00000000000..41367e48497
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/slice_len.rs
@@ -0,0 +1,12 @@
+// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
+// unit-test: DataflowConstProp
+// compile-flags: -Zmir-enable-passes=+InstSimplify
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
+
+// EMIT_MIR slice_len.main.DataflowConstProp.diff
+fn main() {
+    let local = (&[1u32, 2, 3] as &[u32])[1];
+
+    const SLICE: &[u32] = &[1, 2, 3];
+    let constant = SLICE[1];
+}
diff --git a/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..2de6ba307d5
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.32bit.diff
@@ -0,0 +1,129 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let mut _1: S;
+      let mut _3: i32;
+      let mut _5: i32;
+      let mut _6: i32;
+      let mut _11: BigStruct;
+      let mut _16: &&BigStruct;
+      let mut _17: &BigStruct;
+      let mut _18: &BigStruct;
+      let mut _19: &BigStruct;
+      let mut _20: &BigStruct;
+      let mut _21: &BigStruct;
+      scope 1 {
+          debug s => _1;
+          let _2: i32;
+          scope 2 {
+              debug a => _2;
+              let _4: i32;
+              scope 3 {
+                  debug b => _4;
+                  let _7: S;
+                  let _8: u8;
+                  let _9: f32;
+                  let _10: S;
+                  scope 4 {
+                      debug a => _7;
+                      debug b => _8;
+                      debug c => _9;
+                      debug d => _10;
+                      let _12: S;
+                      let _13: u8;
+                      let _14: f32;
+                      let _15: S;
+                      scope 5 {
+                          debug a => _12;
+                          debug b => _13;
+                          debug c => _14;
+                          debug d => _15;
+                      }
+                  }
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          _1 = S(const 1_i32);
+          StorageLive(_2);
+          StorageLive(_3);
+-         _3 = (_1.0: i32);
+-         _2 = Add(move _3, const 2_i32);
++         _3 = const 1_i32;
++         _2 = const 3_i32;
+          StorageDead(_3);
+          (_1.0: i32) = const 3_i32;
+          StorageLive(_4);
+          StorageLive(_5);
+-         _5 = _2;
++         _5 = const 3_i32;
+          StorageLive(_6);
+-         _6 = (_1.0: i32);
+-         _4 = Add(move _5, move _6);
++         _6 = const 3_i32;
++         _4 = const 6_i32;
+          StorageDead(_6);
+          StorageDead(_5);
+          StorageLive(_11);
+          _11 = const _;
+          StorageLive(_7);
+-         _7 = (_11.0: S);
++         _7 = const S(1_i32);
+          StorageLive(_8);
+-         _8 = (_11.1: u8);
++         _8 = const 5_u8;
+          StorageLive(_9);
+-         _9 = (_11.2: f32);
++         _9 = const 7f32;
+          StorageLive(_10);
+-         _10 = (_11.3: S);
++         _10 = const S(13_i32);
+          StorageDead(_11);
+          StorageLive(_16);
+          _16 = const {alloc1: &&BigStruct};
+          _17 = deref_copy (*_16);
+          StorageLive(_12);
+          _18 = deref_copy (*_16);
+-         _12 = ((*_18).0: S);
++         _12 = const S(1_i32);
+          StorageLive(_13);
+          _19 = deref_copy (*_16);
+-         _13 = ((*_19).1: u8);
++         _13 = const 5_u8;
+          StorageLive(_14);
+          _20 = deref_copy (*_16);
+-         _14 = ((*_20).2: f32);
++         _14 = const 7f32;
+          StorageLive(_15);
+          _21 = deref_copy (*_16);
+-         _15 = ((*_21).3: S);
++         _15 = const S(13_i32);
+          StorageDead(_16);
+          _0 = const ();
+          StorageDead(_15);
+          StorageDead(_14);
+          StorageDead(_13);
+          StorageDead(_12);
+          StorageDead(_10);
+          StorageDead(_9);
+          StorageDead(_8);
+          StorageDead(_7);
+          StorageDead(_4);
+          StorageDead(_2);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
+  alloc1 (static: STAT, size: 4, align: 4) {
+      ╾─alloc15─╼                                     │ ╾──╼
+  }
+  
+  alloc15 (size: 16, align: 4) {
+      01 00 00 00 00 00 e0 40 0d 00 00 00 05 __ __ __ │ .......@.....░░░
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..71a28f2165b
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.64bit.diff
@@ -0,0 +1,129 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let mut _1: S;
+      let mut _3: i32;
+      let mut _5: i32;
+      let mut _6: i32;
+      let mut _11: BigStruct;
+      let mut _16: &&BigStruct;
+      let mut _17: &BigStruct;
+      let mut _18: &BigStruct;
+      let mut _19: &BigStruct;
+      let mut _20: &BigStruct;
+      let mut _21: &BigStruct;
+      scope 1 {
+          debug s => _1;
+          let _2: i32;
+          scope 2 {
+              debug a => _2;
+              let _4: i32;
+              scope 3 {
+                  debug b => _4;
+                  let _7: S;
+                  let _8: u8;
+                  let _9: f32;
+                  let _10: S;
+                  scope 4 {
+                      debug a => _7;
+                      debug b => _8;
+                      debug c => _9;
+                      debug d => _10;
+                      let _12: S;
+                      let _13: u8;
+                      let _14: f32;
+                      let _15: S;
+                      scope 5 {
+                          debug a => _12;
+                          debug b => _13;
+                          debug c => _14;
+                          debug d => _15;
+                      }
+                  }
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          _1 = S(const 1_i32);
+          StorageLive(_2);
+          StorageLive(_3);
+-         _3 = (_1.0: i32);
+-         _2 = Add(move _3, const 2_i32);
++         _3 = const 1_i32;
++         _2 = const 3_i32;
+          StorageDead(_3);
+          (_1.0: i32) = const 3_i32;
+          StorageLive(_4);
+          StorageLive(_5);
+-         _5 = _2;
++         _5 = const 3_i32;
+          StorageLive(_6);
+-         _6 = (_1.0: i32);
+-         _4 = Add(move _5, move _6);
++         _6 = const 3_i32;
++         _4 = const 6_i32;
+          StorageDead(_6);
+          StorageDead(_5);
+          StorageLive(_11);
+          _11 = const _;
+          StorageLive(_7);
+-         _7 = (_11.0: S);
++         _7 = const S(1_i32);
+          StorageLive(_8);
+-         _8 = (_11.1: u8);
++         _8 = const 5_u8;
+          StorageLive(_9);
+-         _9 = (_11.2: f32);
++         _9 = const 7f32;
+          StorageLive(_10);
+-         _10 = (_11.3: S);
++         _10 = const S(13_i32);
+          StorageDead(_11);
+          StorageLive(_16);
+          _16 = const {alloc1: &&BigStruct};
+          _17 = deref_copy (*_16);
+          StorageLive(_12);
+          _18 = deref_copy (*_16);
+-         _12 = ((*_18).0: S);
++         _12 = const S(1_i32);
+          StorageLive(_13);
+          _19 = deref_copy (*_16);
+-         _13 = ((*_19).1: u8);
++         _13 = const 5_u8;
+          StorageLive(_14);
+          _20 = deref_copy (*_16);
+-         _14 = ((*_20).2: f32);
++         _14 = const 7f32;
+          StorageLive(_15);
+          _21 = deref_copy (*_16);
+-         _15 = ((*_21).3: S);
++         _15 = const S(13_i32);
+          StorageDead(_16);
+          _0 = const ();
+          StorageDead(_15);
+          StorageDead(_14);
+          StorageDead(_13);
+          StorageDead(_12);
+          StorageDead(_10);
+          StorageDead(_9);
+          StorageDead(_8);
+          StorageDead(_7);
+          StorageDead(_4);
+          StorageDead(_2);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
+  alloc1 (static: STAT, size: 8, align: 8) {
+      ╾───────alloc15───────╼                         │ ╾──────╼
+  }
+  
+  alloc15 (size: 16, align: 4) {
+      01 00 00 00 00 00 e0 40 0d 00 00 00 05 __ __ __ │ .......@.....░░░
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff
deleted file mode 100644
index 914bc8ac47e..00000000000
--- a/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff
+++ /dev/null
@@ -1,51 +0,0 @@
-- // MIR for `main` before DataflowConstProp
-+ // MIR for `main` after DataflowConstProp
-  
-  fn main() -> () {
-      let mut _0: ();
-      let mut _1: S;
-      let mut _3: i32;
-      let mut _5: i32;
-      let mut _6: i32;
-      scope 1 {
-          debug s => _1;
-          let _2: i32;
-          scope 2 {
-              debug a => _2;
-              let _4: i32;
-              scope 3 {
-                  debug b => _4;
-              }
-          }
-      }
-  
-      bb0: {
-          StorageLive(_1);
-          _1 = S(const 1_i32);
-          StorageLive(_2);
-          StorageLive(_3);
--         _3 = (_1.0: i32);
--         _2 = Add(move _3, const 2_i32);
-+         _3 = const 1_i32;
-+         _2 = const 3_i32;
-          StorageDead(_3);
-          (_1.0: i32) = const 3_i32;
-          StorageLive(_4);
-          StorageLive(_5);
--         _5 = _2;
-+         _5 = const 3_i32;
-          StorageLive(_6);
--         _6 = (_1.0: i32);
--         _4 = Add(move _5, move _6);
-+         _6 = const 3_i32;
-+         _4 = const 6_i32;
-          StorageDead(_6);
-          StorageDead(_5);
-          _0 = const ();
-          StorageDead(_4);
-          StorageDead(_2);
-          StorageDead(_1);
-          return;
-      }
-  }
-  
diff --git a/tests/mir-opt/dataflow-const-prop/struct.rs b/tests/mir-opt/dataflow-const-prop/struct.rs
index 841b279e03e..e92a1676d3f 100644
--- a/tests/mir-opt/dataflow-const-prop/struct.rs
+++ b/tests/mir-opt/dataflow-const-prop/struct.rs
@@ -1,11 +1,22 @@
 // unit-test: DataflowConstProp
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
 
+#[derive(Copy, Clone)]
 struct S(i32);
 
+#[derive(Copy, Clone)]
+struct BigStruct(S, u8, f32, S);
+
 // EMIT_MIR struct.main.DataflowConstProp.diff
 fn main() {
     let mut s = S(1);
     let a = s.0 + 2;
     s.0 = 3;
     let b = a + s.0;
+
+    const VAL: BigStruct = BigStruct(S(1), 5, 7., S(13));
+    let BigStruct(a, b, c, d) = VAL;
+
+    static STAT: &BigStruct = &BigStruct(S(1), 5, 7., S(13));
+    let BigStruct(a, b, c, d) = *STAT;
 }
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.from_char.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.from_char.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..52f096ac0e4
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.from_char.DataflowConstProp.32bit.diff
@@ -0,0 +1,15 @@
+- // MIR for `from_char` before DataflowConstProp
++ // MIR for `from_char` after DataflowConstProp
+  
+  fn from_char() -> i32 {
+      let mut _0: i32;
+      scope 1 {
+      }
+  
+      bb0: {
+-         _0 = const 'R' as i32 (Transmute);
++         _0 = const 82_i32;
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.from_char.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.from_char.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..52f096ac0e4
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.from_char.DataflowConstProp.64bit.diff
@@ -0,0 +1,15 @@
+- // MIR for `from_char` before DataflowConstProp
++ // MIR for `from_char` after DataflowConstProp
+  
+  fn from_char() -> i32 {
+      let mut _0: i32;
+      scope 1 {
+      }
+  
+      bb0: {
+-         _0 = const 'R' as i32 (Transmute);
++         _0 = const 82_i32;
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.invalid_bool.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.invalid_bool.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..3972eb209a1
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.invalid_bool.DataflowConstProp.32bit.diff
@@ -0,0 +1,15 @@
+- // MIR for `invalid_bool` before DataflowConstProp
++ // MIR for `invalid_bool` after DataflowConstProp
+  
+  fn invalid_bool() -> bool {
+      let mut _0: bool;
+      scope 1 {
+      }
+  
+      bb0: {
+-         _0 = const -1_i8 as bool (Transmute);
++         _0 = const {transmute(0xff): bool};
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.invalid_bool.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.invalid_bool.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..3972eb209a1
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.invalid_bool.DataflowConstProp.64bit.diff
@@ -0,0 +1,15 @@
+- // MIR for `invalid_bool` before DataflowConstProp
++ // MIR for `invalid_bool` after DataflowConstProp
+  
+  fn invalid_bool() -> bool {
+      let mut _0: bool;
+      scope 1 {
+      }
+  
+      bb0: {
+-         _0 = const -1_i8 as bool (Transmute);
++         _0 = const {transmute(0xff): bool};
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.invalid_char.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.invalid_char.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..837dabde42a
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.invalid_char.DataflowConstProp.32bit.diff
@@ -0,0 +1,15 @@
+- // MIR for `invalid_char` before DataflowConstProp
++ // MIR for `invalid_char` after DataflowConstProp
+  
+  fn invalid_char() -> char {
+      let mut _0: char;
+      scope 1 {
+      }
+  
+      bb0: {
+-         _0 = const _ as char (Transmute);
++         _0 = const {transmute(0x7fffffff): char};
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.invalid_char.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.invalid_char.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..837dabde42a
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.invalid_char.DataflowConstProp.64bit.diff
@@ -0,0 +1,15 @@
+- // MIR for `invalid_char` before DataflowConstProp
++ // MIR for `invalid_char` after DataflowConstProp
+  
+  fn invalid_char() -> char {
+      let mut _0: char;
+      scope 1 {
+      }
+  
+      bb0: {
+-         _0 = const _ as char (Transmute);
++         _0 = const {transmute(0x7fffffff): char};
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.less_as_i8.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.less_as_i8.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..6091e169e8e
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.less_as_i8.DataflowConstProp.32bit.diff
@@ -0,0 +1,18 @@
+- // MIR for `less_as_i8` before DataflowConstProp
++ // MIR for `less_as_i8` after DataflowConstProp
+  
+  fn less_as_i8() -> i8 {
+      let mut _0: i8;
+      let mut _1: std::cmp::Ordering;
+      scope 1 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          _1 = Less;
+          _0 = move _1 as i8 (Transmute);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.less_as_i8.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.less_as_i8.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..6091e169e8e
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.less_as_i8.DataflowConstProp.64bit.diff
@@ -0,0 +1,18 @@
+- // MIR for `less_as_i8` before DataflowConstProp
++ // MIR for `less_as_i8` after DataflowConstProp
+  
+  fn less_as_i8() -> i8 {
+      let mut _0: i8;
+      let mut _1: std::cmp::Ordering;
+      scope 1 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          _1 = Less;
+          _0 = move _1 as i8 (Transmute);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.rs b/tests/mir-opt/dataflow-const-prop/transmute.rs
new file mode 100644
index 00000000000..c25e33ab0b6
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.rs
@@ -0,0 +1,63 @@
+// unit-test: DataflowConstProp
+// compile-flags: -O --crate-type=lib
+// ignore-endian-big
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
+
+use std::mem::transmute;
+
+// EMIT_MIR transmute.less_as_i8.DataflowConstProp.diff
+pub fn less_as_i8() -> i8 {
+    unsafe { transmute(std::cmp::Ordering::Less) }
+}
+
+// EMIT_MIR transmute.from_char.DataflowConstProp.diff
+pub fn from_char() -> i32 {
+    unsafe { transmute('R') }
+}
+
+// EMIT_MIR transmute.valid_char.DataflowConstProp.diff
+pub fn valid_char() -> char {
+    unsafe { transmute(0x52_u32) }
+}
+
+// EMIT_MIR transmute.invalid_char.DataflowConstProp.diff
+pub unsafe fn invalid_char() -> char {
+    unsafe { transmute(i32::MAX) }
+}
+
+// EMIT_MIR transmute.invalid_bool.DataflowConstProp.diff
+pub unsafe fn invalid_bool() -> bool {
+    unsafe { transmute(-1_i8) }
+}
+
+// EMIT_MIR transmute.undef_union_as_integer.DataflowConstProp.diff
+pub unsafe fn undef_union_as_integer() -> u32 {
+    union Union32 { value: u32, unit: () }
+    unsafe { transmute(Union32 { unit: () }) }
+}
+
+// EMIT_MIR transmute.unreachable_direct.DataflowConstProp.diff
+pub unsafe fn unreachable_direct() -> ! {
+    let x: Never = unsafe { transmute(()) };
+    match x {}
+}
+
+// EMIT_MIR transmute.unreachable_ref.DataflowConstProp.diff
+pub unsafe fn unreachable_ref() -> ! {
+    let x: &Never = unsafe { transmute(1_usize) };
+    match *x {}
+}
+
+// EMIT_MIR transmute.unreachable_mut.DataflowConstProp.diff
+pub unsafe fn unreachable_mut() -> ! {
+    let x: &mut Never = unsafe { transmute(1_usize) };
+    match *x {}
+}
+
+// EMIT_MIR transmute.unreachable_box.DataflowConstProp.diff
+pub unsafe fn unreachable_box() -> ! {
+    let x: Box<Never> = unsafe { transmute(1_usize) };
+    match *x {}
+}
+
+enum Never {}
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.undef_union_as_integer.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.undef_union_as_integer.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..fc0634b1f8f
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.undef_union_as_integer.DataflowConstProp.32bit.diff
@@ -0,0 +1,22 @@
+- // MIR for `undef_union_as_integer` before DataflowConstProp
++ // MIR for `undef_union_as_integer` after DataflowConstProp
+  
+  fn undef_union_as_integer() -> u32 {
+      let mut _0: u32;
+      let mut _1: undef_union_as_integer::Union32;
+      let mut _2: ();
+      scope 1 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = ();
+          _1 = Union32 { value: move _2 };
+          StorageDead(_2);
+          _0 = move _1 as u32 (Transmute);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.undef_union_as_integer.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.undef_union_as_integer.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..fc0634b1f8f
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.undef_union_as_integer.DataflowConstProp.64bit.diff
@@ -0,0 +1,22 @@
+- // MIR for `undef_union_as_integer` before DataflowConstProp
++ // MIR for `undef_union_as_integer` after DataflowConstProp
+  
+  fn undef_union_as_integer() -> u32 {
+      let mut _0: u32;
+      let mut _1: undef_union_as_integer::Union32;
+      let mut _2: ();
+      scope 1 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = ();
+          _1 = Union32 { value: move _2 };
+          StorageDead(_2);
+          _0 = move _1 as u32 (Transmute);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..d0c298ba233
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.32bit.diff
@@ -0,0 +1,20 @@
+- // MIR for `unreachable_box` before DataflowConstProp
++ // MIR for `unreachable_box` after DataflowConstProp
+  
+  fn unreachable_box() -> ! {
+      let mut _0: !;
+      let _1: std::boxed::Box<Never>;
+      scope 1 {
+          debug x => _1;
+      }
+      scope 2 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+-         _1 = const 1_usize as std::boxed::Box<Never> (Transmute);
++         _1 = const Box::<Never>(Unique::<Never> {{ pointer: NonNull::<Never> {{ pointer: {0x1 as *const Never} }}, _marker: PhantomData::<Never> }}, std::alloc::Global);
+          unreachable;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..d0c298ba233
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.64bit.diff
@@ -0,0 +1,20 @@
+- // MIR for `unreachable_box` before DataflowConstProp
++ // MIR for `unreachable_box` after DataflowConstProp
+  
+  fn unreachable_box() -> ! {
+      let mut _0: !;
+      let _1: std::boxed::Box<Never>;
+      scope 1 {
+          debug x => _1;
+      }
+      scope 2 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+-         _1 = const 1_usize as std::boxed::Box<Never> (Transmute);
++         _1 = const Box::<Never>(Unique::<Never> {{ pointer: NonNull::<Never> {{ pointer: {0x1 as *const Never} }}, _marker: PhantomData::<Never> }}, std::alloc::Global);
+          unreachable;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_direct.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_direct.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..acbb5cd1bc7
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_direct.DataflowConstProp.32bit.diff
@@ -0,0 +1,22 @@
+- // MIR for `unreachable_direct` before DataflowConstProp
++ // MIR for `unreachable_direct` after DataflowConstProp
+  
+  fn unreachable_direct() -> ! {
+      let mut _0: !;
+      let _1: Never;
+      let mut _2: ();
+      scope 1 {
+          debug x => _1;
+      }
+      scope 2 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = ();
+          _1 = move _2 as Never (Transmute);
+          unreachable;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_direct.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_direct.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..acbb5cd1bc7
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_direct.DataflowConstProp.64bit.diff
@@ -0,0 +1,22 @@
+- // MIR for `unreachable_direct` before DataflowConstProp
++ // MIR for `unreachable_direct` after DataflowConstProp
+  
+  fn unreachable_direct() -> ! {
+      let mut _0: !;
+      let _1: Never;
+      let mut _2: ();
+      scope 1 {
+          debug x => _1;
+      }
+      scope 2 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = ();
+          _1 = move _2 as Never (Transmute);
+          unreachable;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_mut.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_mut.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..2ffaeea72db
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_mut.DataflowConstProp.32bit.diff
@@ -0,0 +1,24 @@
+- // MIR for `unreachable_mut` before DataflowConstProp
++ // MIR for `unreachable_mut` after DataflowConstProp
+  
+  fn unreachable_mut() -> ! {
+      let mut _0: !;
+      let _1: &mut Never;
+      let mut _2: &mut Never;
+      scope 1 {
+          debug x => _1;
+      }
+      scope 2 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+-         _2 = const 1_usize as &mut Never (Transmute);
++         _2 = const {0x1 as &mut Never};
+          _1 = &mut (*_2);
+          StorageDead(_2);
+          unreachable;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_mut.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_mut.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..2ffaeea72db
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_mut.DataflowConstProp.64bit.diff
@@ -0,0 +1,24 @@
+- // MIR for `unreachable_mut` before DataflowConstProp
++ // MIR for `unreachable_mut` after DataflowConstProp
+  
+  fn unreachable_mut() -> ! {
+      let mut _0: !;
+      let _1: &mut Never;
+      let mut _2: &mut Never;
+      scope 1 {
+          debug x => _1;
+      }
+      scope 2 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+-         _2 = const 1_usize as &mut Never (Transmute);
++         _2 = const {0x1 as &mut Never};
+          _1 = &mut (*_2);
+          StorageDead(_2);
+          unreachable;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_ref.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_ref.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..31fcaafc5bc
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_ref.DataflowConstProp.32bit.diff
@@ -0,0 +1,20 @@
+- // MIR for `unreachable_ref` before DataflowConstProp
++ // MIR for `unreachable_ref` after DataflowConstProp
+  
+  fn unreachable_ref() -> ! {
+      let mut _0: !;
+      let _1: &Never;
+      scope 1 {
+          debug x => _1;
+      }
+      scope 2 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+-         _1 = const 1_usize as &Never (Transmute);
++         _1 = const {0x1 as &Never};
+          unreachable;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_ref.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_ref.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..31fcaafc5bc
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_ref.DataflowConstProp.64bit.diff
@@ -0,0 +1,20 @@
+- // MIR for `unreachable_ref` before DataflowConstProp
++ // MIR for `unreachable_ref` after DataflowConstProp
+  
+  fn unreachable_ref() -> ! {
+      let mut _0: !;
+      let _1: &Never;
+      scope 1 {
+          debug x => _1;
+      }
+      scope 2 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+-         _1 = const 1_usize as &Never (Transmute);
++         _1 = const {0x1 as &Never};
+          unreachable;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.valid_char.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.valid_char.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..402ef754a64
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.valid_char.DataflowConstProp.32bit.diff
@@ -0,0 +1,15 @@
+- // MIR for `valid_char` before DataflowConstProp
++ // MIR for `valid_char` after DataflowConstProp
+  
+  fn valid_char() -> char {
+      let mut _0: char;
+      scope 1 {
+      }
+  
+      bb0: {
+-         _0 = const 82_u32 as char (Transmute);
++         _0 = const 'R';
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.valid_char.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.valid_char.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..402ef754a64
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.valid_char.DataflowConstProp.64bit.diff
@@ -0,0 +1,15 @@
+- // MIR for `valid_char` before DataflowConstProp
++ // MIR for `valid_char` after DataflowConstProp
+  
+  fn valid_char() -> char {
+      let mut _0: char;
+      scope 1 {
+      }
+  
+      bb0: {
+-         _0 = const 82_u32 as char (Transmute);
++         _0 = const 'R';
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/enum_opt.cand.EnumSizeOpt.32bit.diff b/tests/mir-opt/enum_opt.cand.EnumSizeOpt.32bit.diff
index 9d9a7a1e485..ec5f5c1f1fc 100644
--- a/tests/mir-opt/enum_opt.cand.EnumSizeOpt.32bit.diff
+++ b/tests/mir-opt/enum_opt.cand.EnumSizeOpt.32bit.diff
@@ -64,5 +64,9 @@
           StorageDead(_1);
           return;
       }
++ }
++ 
++ alloc15 (size: 8, align: 4) {
++     02 00 00 00 05 20 00 00                         │ ..... ..
   }
   
diff --git a/tests/mir-opt/enum_opt.cand.EnumSizeOpt.64bit.diff b/tests/mir-opt/enum_opt.cand.EnumSizeOpt.64bit.diff
index 9d9a7a1e485..9bf8637ec76 100644
--- a/tests/mir-opt/enum_opt.cand.EnumSizeOpt.64bit.diff
+++ b/tests/mir-opt/enum_opt.cand.EnumSizeOpt.64bit.diff
@@ -64,5 +64,9 @@
           StorageDead(_1);
           return;
       }
++ }
++ 
++ alloc15 (size: 16, align: 8) {
++     02 00 00 00 00 00 00 00 05 20 00 00 00 00 00 00 │ ......... ......
   }
   
diff --git a/tests/mir-opt/enum_opt.unin.EnumSizeOpt.32bit.diff b/tests/mir-opt/enum_opt.unin.EnumSizeOpt.32bit.diff
index 4306f38b82a..7dc6d21a907 100644
--- a/tests/mir-opt/enum_opt.unin.EnumSizeOpt.32bit.diff
+++ b/tests/mir-opt/enum_opt.unin.EnumSizeOpt.32bit.diff
@@ -64,5 +64,9 @@
           StorageDead(_1);
           return;
       }
++ }
++ 
++ alloc14 (size: 8, align: 4) {
++     05 20 00 00 01 00 00 00                         │ . ......
   }
   
diff --git a/tests/mir-opt/enum_opt.unin.EnumSizeOpt.64bit.diff b/tests/mir-opt/enum_opt.unin.EnumSizeOpt.64bit.diff
index 4306f38b82a..0b000876a86 100644
--- a/tests/mir-opt/enum_opt.unin.EnumSizeOpt.64bit.diff
+++ b/tests/mir-opt/enum_opt.unin.EnumSizeOpt.64bit.diff
@@ -64,5 +64,9 @@
           StorageDead(_1);
           return;
       }
++ }
++ 
++ alloc14 (size: 16, align: 8) {
++     05 20 00 00 00 00 00 00 01 00 00 00 00 00 00 00 │ . ..............
   }
   
diff --git a/tests/mir-opt/funky_arms.rs b/tests/mir-opt/funky_arms.rs
index 6b4f4c80560..79fd9457ce1 100644
--- a/tests/mir-opt/funky_arms.rs
+++ b/tests/mir-opt/funky_arms.rs
@@ -9,7 +9,7 @@ use core::num::flt2dec;
 use std::fmt::{Formatter, Result};
 
 // EMIT_MIR funky_arms.float_to_exponential_common.ConstProp.diff
-fn float_to_exponential_common<T>(fmt: &mut Formatter<'_>, num: &T, upper: bool) -> Result
+pub fn float_to_exponential_common<T>(fmt: &mut Formatter<'_>, num: &T, upper: bool) -> Result
 where
     T: flt2dec::DecodableFloat,
 {
diff --git a/tests/mir-opt/generator_drop_cleanup.main-{closure#0}.generator_drop.0.panic-abort.mir b/tests/mir-opt/generator_drop_cleanup.main-{closure#0}.generator_drop.0.panic-abort.mir
index 958078b9706..acbb7904985 100644
--- a/tests/mir-opt/generator_drop_cleanup.main-{closure#0}.generator_drop.0.panic-abort.mir
+++ b/tests/mir-opt/generator_drop_cleanup.main-{closure#0}.generator_drop.0.panic-abort.mir
@@ -2,7 +2,11 @@
 /* generator_layout = GeneratorLayout {
     field_tys: {
         _0: GeneratorSavedTy {
-            ty: std::string::String,
+            ty: Adt(
+                std::string::String,
+                [
+                ],
+            ),
             source_info: SourceInfo {
                 span: $DIR/generator_drop_cleanup.rs:11:13: 11:15 (#0),
                 scope: scope[0],
diff --git a/tests/mir-opt/generator_drop_cleanup.main-{closure#0}.generator_drop.0.panic-unwind.mir b/tests/mir-opt/generator_drop_cleanup.main-{closure#0}.generator_drop.0.panic-unwind.mir
index 7e050e585b1..c17d4421542 100644
--- a/tests/mir-opt/generator_drop_cleanup.main-{closure#0}.generator_drop.0.panic-unwind.mir
+++ b/tests/mir-opt/generator_drop_cleanup.main-{closure#0}.generator_drop.0.panic-unwind.mir
@@ -2,7 +2,11 @@
 /* generator_layout = GeneratorLayout {
     field_tys: {
         _0: GeneratorSavedTy {
-            ty: std::string::String,
+            ty: Adt(
+                std::string::String,
+                [
+                ],
+            ),
             source_info: SourceInfo {
                 span: $DIR/generator_drop_cleanup.rs:11:13: 11:15 (#0),
                 scope: scope[0],
diff --git a/tests/mir-opt/generator_tiny.main-{closure#0}.generator_resume.0.mir b/tests/mir-opt/generator_tiny.main-{closure#0}.generator_resume.0.mir
index 13d703b908c..e33f5f59de1 100644
--- a/tests/mir-opt/generator_tiny.main-{closure#0}.generator_resume.0.mir
+++ b/tests/mir-opt/generator_tiny.main-{closure#0}.generator_resume.0.mir
@@ -2,7 +2,11 @@
 /* generator_layout = GeneratorLayout {
     field_tys: {
         _0: GeneratorSavedTy {
-            ty: HasDrop,
+            ty: Adt(
+                HasDrop,
+                [
+                ],
+            ),
             source_info: SourceInfo {
                 span: $DIR/generator_tiny.rs:20:13: 20:15 (#0),
                 scope: scope[0],
diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff
index f61632728ba..9d8f272abea 100644
--- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff
+++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff
@@ -33,12 +33,10 @@
           _3 = &_4;
           _2 = move _3 as &[T] (PointerCoercion(Unsize));
           StorageDead(_3);
-          _8 = Len((*_2));
+          _8 = const 3_usize;
           _9 = const 3_usize;
--         _10 = Eq(move _8, const 3_usize);
--         switchInt(move _10) -> [0: bb1, otherwise: bb2];
-+         nop;
-+         switchInt(move _8) -> [3: bb2, otherwise: bb1];
+          _10 = const true;
+          goto -> bb2;
       }
   
       bb1: {
diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff
index f6c337be10f..738b0b1b3e5 100644
--- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff
+++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff
@@ -33,12 +33,10 @@
           _3 = &_4;
           _2 = move _3 as &[T] (PointerCoercion(Unsize));
           StorageDead(_3);
-          _8 = Len((*_2));
+          _8 = const 3_usize;
           _9 = const 3_usize;
--         _10 = Eq(move _8, const 3_usize);
--         switchInt(move _10) -> [0: bb1, otherwise: bb2];
-+         nop;
-+         switchInt(move _8) -> [3: bb2, otherwise: bb1];
+          _10 = const true;
+          goto -> bb2;
       }
   
       bb1: {
diff --git a/tests/mir-opt/issue_99325.main.built.after.mir b/tests/mir-opt/issue_99325.main.built.after.32bit.mir
index f12179a8905..132b713356e 100644
--- a/tests/mir-opt/issue_99325.main.built.after.mir
+++ b/tests/mir-opt/issue_99325.main.built.after.32bit.mir
@@ -1,8 +1,8 @@
 // MIR for `main` after built
 
 | User Type Annotations
-| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [Const { ty: &'static [u8; 4], kind: Branch([Leaf(0x41), Leaf(0x41), Leaf(0x41), Leaf(0x41)]) }], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:10:16: 10:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}
-| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [Const { ty: &'static [u8; 4], kind: UnevaluatedConst { def: DefId(0:8 ~ issue_99325[22bb]::main::{constant#1}), args: [] } }], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:11:16: 11:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}
+| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:12:16: 12:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}
+| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[22bb]::main::{constant#1}), args: [] }: &ReStatic [u8; 4_usize]], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:13:16: 13:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}
 |
 fn main() -> () {
     let mut _0: ();
diff --git a/tests/mir-opt/issue_99325.main.built.after.64bit.mir b/tests/mir-opt/issue_99325.main.built.after.64bit.mir
new file mode 100644
index 00000000000..132b713356e
--- /dev/null
+++ b/tests/mir-opt/issue_99325.main.built.after.64bit.mir
@@ -0,0 +1,276 @@
+// MIR for `main` after built
+
+| User Type Annotations
+| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:12:16: 12:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}
+| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[22bb]::main::{constant#1}), args: [] }: &ReStatic [u8; 4_usize]], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:13:16: 13:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}
+|
+fn main() -> () {
+    let mut _0: ();
+    let _1: ();
+    let mut _2: (&&[u8], &&[u8; 4]);
+    let mut _3: &&[u8];
+    let _4: &[u8];
+    let mut _5: &&[u8; 4];
+    let _6: &[u8; 4];
+    let _7: [u8; 4];
+    let _8: &&[u8];
+    let _9: &&[u8; 4];
+    let mut _10: bool;
+    let mut _11: &&[u8];
+    let mut _12: &&[u8; 4];
+    let mut _13: !;
+    let _15: !;
+    let mut _16: core::panicking::AssertKind;
+    let mut _17: &&[u8];
+    let _18: &&[u8];
+    let mut _19: &&[u8; 4];
+    let _20: &&[u8; 4];
+    let mut _21: std::option::Option<std::fmt::Arguments<'_>>;
+    let _22: ();
+    let mut _23: (&&[u8], &&[u8; 4]);
+    let mut _24: &&[u8];
+    let _25: &[u8];
+    let mut _26: &&[u8; 4];
+    let _27: &[u8; 4];
+    let _28: &&[u8];
+    let _29: &&[u8; 4];
+    let mut _30: bool;
+    let mut _31: &&[u8];
+    let mut _32: &&[u8; 4];
+    let mut _33: !;
+    let _35: !;
+    let mut _36: core::panicking::AssertKind;
+    let mut _37: &&[u8];
+    let _38: &&[u8];
+    let mut _39: &&[u8; 4];
+    let _40: &&[u8; 4];
+    let mut _41: std::option::Option<std::fmt::Arguments<'_>>;
+    scope 1 {
+        debug left_val => _8;
+        debug right_val => _9;
+        let _14: core::panicking::AssertKind;
+        scope 2 {
+            debug kind => _14;
+        }
+    }
+    scope 3 {
+        debug left_val => _28;
+        debug right_val => _29;
+        let _34: core::panicking::AssertKind;
+        scope 4 {
+            debug kind => _34;
+        }
+    }
+
+    bb0: {
+        StorageLive(_1);
+        StorageLive(_2);
+        StorageLive(_3);
+        StorageLive(_4);
+        _4 = function_with_bytes::<&*b"AAAA">() -> [return: bb1, unwind: bb21];
+    }
+
+    bb1: {
+        _3 = &_4;
+        StorageLive(_5);
+        StorageLive(_6);
+        StorageLive(_7);
+        _7 = [const 65_u8, const 65_u8, const 65_u8, const 65_u8];
+        _6 = &_7;
+        _5 = &_6;
+        _2 = (move _3, move _5);
+        StorageDead(_5);
+        StorageDead(_3);
+        FakeRead(ForMatchedPlace(None), _2);
+        StorageLive(_8);
+        _8 = (_2.0: &&[u8]);
+        StorageLive(_9);
+        _9 = (_2.1: &&[u8; 4]);
+        StorageLive(_10);
+        StorageLive(_11);
+        _11 = &(*_8);
+        StorageLive(_12);
+        _12 = &(*_9);
+        _10 = <&[u8] as PartialEq<&[u8; 4]>>::eq(move _11, move _12) -> [return: bb2, unwind: bb21];
+    }
+
+    bb2: {
+        switchInt(move _10) -> [0: bb4, otherwise: bb3];
+    }
+
+    bb3: {
+        StorageDead(_12);
+        StorageDead(_11);
+        goto -> bb8;
+    }
+
+    bb4: {
+        goto -> bb5;
+    }
+
+    bb5: {
+        StorageDead(_12);
+        StorageDead(_11);
+        StorageLive(_14);
+        _14 = core::panicking::AssertKind::Eq;
+        FakeRead(ForLet(None), _14);
+        StorageLive(_15);
+        StorageLive(_16);
+        _16 = move _14;
+        StorageLive(_17);
+        StorageLive(_18);
+        _18 = &(*_8);
+        _17 = &(*_18);
+        StorageLive(_19);
+        StorageLive(_20);
+        _20 = &(*_9);
+        _19 = &(*_20);
+        StorageLive(_21);
+        _21 = Option::<Arguments<'_>>::None;
+        _15 = core::panicking::assert_failed::<&[u8], &[u8; 4]>(move _16, move _17, move _19, move _21) -> bb21;
+    }
+
+    bb6: {
+        StorageDead(_21);
+        StorageDead(_19);
+        StorageDead(_17);
+        StorageDead(_16);
+        StorageDead(_20);
+        StorageDead(_18);
+        StorageDead(_15);
+        StorageDead(_14);
+        unreachable;
+    }
+
+    bb7: {
+        goto -> bb9;
+    }
+
+    bb8: {
+        _1 = const ();
+        goto -> bb9;
+    }
+
+    bb9: {
+        StorageDead(_10);
+        StorageDead(_9);
+        StorageDead(_8);
+        goto -> bb10;
+    }
+
+    bb10: {
+        StorageDead(_7);
+        StorageDead(_6);
+        StorageDead(_4);
+        StorageDead(_2);
+        StorageDead(_1);
+        StorageLive(_22);
+        StorageLive(_23);
+        StorageLive(_24);
+        StorageLive(_25);
+        _25 = function_with_bytes::<&*b"AAAA">() -> [return: bb11, unwind: bb21];
+    }
+
+    bb11: {
+        _24 = &_25;
+        StorageLive(_26);
+        StorageLive(_27);
+        _27 = const b"AAAA";
+        _26 = &_27;
+        _23 = (move _24, move _26);
+        StorageDead(_26);
+        StorageDead(_24);
+        FakeRead(ForMatchedPlace(None), _23);
+        StorageLive(_28);
+        _28 = (_23.0: &&[u8]);
+        StorageLive(_29);
+        _29 = (_23.1: &&[u8; 4]);
+        StorageLive(_30);
+        StorageLive(_31);
+        _31 = &(*_28);
+        StorageLive(_32);
+        _32 = &(*_29);
+        _30 = <&[u8] as PartialEq<&[u8; 4]>>::eq(move _31, move _32) -> [return: bb12, unwind: bb21];
+    }
+
+    bb12: {
+        switchInt(move _30) -> [0: bb14, otherwise: bb13];
+    }
+
+    bb13: {
+        StorageDead(_32);
+        StorageDead(_31);
+        goto -> bb18;
+    }
+
+    bb14: {
+        goto -> bb15;
+    }
+
+    bb15: {
+        StorageDead(_32);
+        StorageDead(_31);
+        StorageLive(_34);
+        _34 = core::panicking::AssertKind::Eq;
+        FakeRead(ForLet(None), _34);
+        StorageLive(_35);
+        StorageLive(_36);
+        _36 = move _34;
+        StorageLive(_37);
+        StorageLive(_38);
+        _38 = &(*_28);
+        _37 = &(*_38);
+        StorageLive(_39);
+        StorageLive(_40);
+        _40 = &(*_29);
+        _39 = &(*_40);
+        StorageLive(_41);
+        _41 = Option::<Arguments<'_>>::None;
+        _35 = core::panicking::assert_failed::<&[u8], &[u8; 4]>(move _36, move _37, move _39, move _41) -> bb21;
+    }
+
+    bb16: {
+        StorageDead(_41);
+        StorageDead(_39);
+        StorageDead(_37);
+        StorageDead(_36);
+        StorageDead(_40);
+        StorageDead(_38);
+        StorageDead(_35);
+        StorageDead(_34);
+        unreachable;
+    }
+
+    bb17: {
+        goto -> bb19;
+    }
+
+    bb18: {
+        _22 = const ();
+        goto -> bb19;
+    }
+
+    bb19: {
+        StorageDead(_30);
+        StorageDead(_29);
+        StorageDead(_28);
+        goto -> bb20;
+    }
+
+    bb20: {
+        StorageDead(_27);
+        StorageDead(_25);
+        StorageDead(_23);
+        StorageDead(_22);
+        _0 = const ();
+        return;
+    }
+
+    bb21 (cleanup): {
+        resume;
+    }
+}
+
+alloc4 (size: 4, align: 1) {
+    41 41 41 41                                     │ AAAA
+}
diff --git a/tests/mir-opt/issue_99325.rs b/tests/mir-opt/issue_99325.rs
index fe819cddb2c..3603228a502 100644
--- a/tests/mir-opt/issue_99325.rs
+++ b/tests/mir-opt/issue_99325.rs
@@ -1,3 +1,5 @@
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
+
 #![feature(adt_const_params)]
 #![allow(incomplete_features)]
 
diff --git a/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir b/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir
index 56b0f816573..c581d0f8471 100644
--- a/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir
+++ b/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir
@@ -22,7 +22,7 @@
 |
 fn main() -> () {
     let mut _0: ();
-    let mut _1: [usize; Const { ty: usize, kind: Leaf(0x00000003) }];
+    let mut _1: [usize; ValTree(Leaf(0x00000003): usize)];
     let _3: usize;
     let mut _4: usize;
     let mut _5: bool;
diff --git a/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir b/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir
index 83b851eed74..48243e34d08 100644
--- a/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir
+++ b/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir
@@ -22,7 +22,7 @@
 |
 fn main() -> () {
     let mut _0: ();
-    let mut _1: [usize; Const { ty: usize, kind: Leaf(0x0000000000000003) }];
+    let mut _1: [usize; ValTree(Leaf(0x0000000000000003): usize)];
     let _3: usize;
     let mut _4: usize;
     let mut _5: bool;
diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ConstProp.32bit.panic-abort.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ConstProp.32bit.panic-abort.diff
index 2c607b4c055..681e9666e0f 100644
--- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ConstProp.32bit.panic-abort.diff
+++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ConstProp.32bit.panic-abort.diff
@@ -55,5 +55,9 @@
           StorageDead(_1);
           return;
       }
++ }
++ 
++ alloc5 (size: 8, align: 4) {
++     04 00 00 00 00 __ __ __                         │ .....░░░
   }
   
diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ConstProp.32bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ConstProp.32bit.panic-unwind.diff
index b6929f3f93c..db16b8d82d2 100644
--- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ConstProp.32bit.panic-unwind.diff
+++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ConstProp.32bit.panic-unwind.diff
@@ -55,5 +55,9 @@
           StorageDead(_1);
           return;
       }
++ }
++ 
++ alloc5 (size: 8, align: 4) {
++     04 00 00 00 00 __ __ __                         │ .....░░░
   }
   
diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ConstProp.64bit.panic-abort.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ConstProp.64bit.panic-abort.diff
index 2c607b4c055..681e9666e0f 100644
--- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ConstProp.64bit.panic-abort.diff
+++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ConstProp.64bit.panic-abort.diff
@@ -55,5 +55,9 @@
           StorageDead(_1);
           return;
       }
++ }
++ 
++ alloc5 (size: 8, align: 4) {
++     04 00 00 00 00 __ __ __                         │ .....░░░
   }
   
diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ConstProp.64bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ConstProp.64bit.panic-unwind.diff
index b6929f3f93c..db16b8d82d2 100644
--- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ConstProp.64bit.panic-unwind.diff
+++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ConstProp.64bit.panic-unwind.diff
@@ -55,5 +55,9 @@
           StorageDead(_1);
           return;
       }
++ }
++ 
++ alloc5 (size: 8, align: 4) {
++     04 00 00 00 00 __ __ __                         │ .....░░░
   }
   
diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir
index 8dd2cd7900f..2fd669aeeb6 100644
--- a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir
@@ -8,19 +8,22 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range<usize>) ->
     let mut _4: usize;
     scope 1 (inlined core::slice::<impl [u32]>::get_unchecked_mut::<std::ops::Range<usize>>) {
         debug self => _1;
-        debug index => std::ops::Range<usize>{ .0 => _3, .1 => _4, };
+        debug ((index: std::ops::Range<usize>).0: usize) => _3;
+        debug ((index: std::ops::Range<usize>).1: usize) => _4;
         let mut _5: *mut [u32];
         let mut _13: *mut [u32];
         scope 2 {
             scope 3 (inlined <std::ops::Range<usize> as SliceIndex<[u32]>>::get_unchecked_mut) {
-                debug self => std::ops::Range<usize>{ .0 => _3, .1 => _4, };
+                debug ((self: std::ops::Range<usize>).0: usize) => _3;
+                debug ((self: std::ops::Range<usize>).1: usize) => _4;
                 debug slice => _5;
                 let mut _7: *mut u32;
                 let mut _8: *mut u32;
                 let _15: usize;
                 let _16: usize;
                 scope 4 {
-                    debug this => std::ops::Range<usize>{ .0 => _15, .1 => _16, };
+                    debug ((this: std::ops::Range<usize>).0: usize) => _15;
+                    debug ((this: std::ops::Range<usize>).1: usize) => _16;
                     scope 5 {
                         let _6: usize;
                         scope 6 {
@@ -53,7 +56,8 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range<usize>) ->
                             }
                         }
                         scope 7 (inlined <std::ops::Range<usize> as SliceIndex<[T]>>::get_unchecked_mut::runtime::<u32>) {
-                            debug this => std::ops::Range<usize>{ .0 => _15, .1 => _16, };
+                            debug ((this: std::ops::Range<usize>).0: usize) => _15;
+                            debug ((this: std::ops::Range<usize>).1: usize) => _16;
                             debug slice => _5;
                             scope 8 (inlined ptr::mut_ptr::<impl *mut [u32]>::len) {
                                 debug self => _5;
diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir
index 8dd2cd7900f..2fd669aeeb6 100644
--- a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir
@@ -8,19 +8,22 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range<usize>) ->
     let mut _4: usize;
     scope 1 (inlined core::slice::<impl [u32]>::get_unchecked_mut::<std::ops::Range<usize>>) {
         debug self => _1;
-        debug index => std::ops::Range<usize>{ .0 => _3, .1 => _4, };
+        debug ((index: std::ops::Range<usize>).0: usize) => _3;
+        debug ((index: std::ops::Range<usize>).1: usize) => _4;
         let mut _5: *mut [u32];
         let mut _13: *mut [u32];
         scope 2 {
             scope 3 (inlined <std::ops::Range<usize> as SliceIndex<[u32]>>::get_unchecked_mut) {
-                debug self => std::ops::Range<usize>{ .0 => _3, .1 => _4, };
+                debug ((self: std::ops::Range<usize>).0: usize) => _3;
+                debug ((self: std::ops::Range<usize>).1: usize) => _4;
                 debug slice => _5;
                 let mut _7: *mut u32;
                 let mut _8: *mut u32;
                 let _15: usize;
                 let _16: usize;
                 scope 4 {
-                    debug this => std::ops::Range<usize>{ .0 => _15, .1 => _16, };
+                    debug ((this: std::ops::Range<usize>).0: usize) => _15;
+                    debug ((this: std::ops::Range<usize>).1: usize) => _16;
                     scope 5 {
                         let _6: usize;
                         scope 6 {
@@ -53,7 +56,8 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range<usize>) ->
                             }
                         }
                         scope 7 (inlined <std::ops::Range<usize> as SliceIndex<[T]>>::get_unchecked_mut::runtime::<u32>) {
-                            debug this => std::ops::Range<usize>{ .0 => _15, .1 => _16, };
+                            debug ((this: std::ops::Range<usize>).0: usize) => _15;
+                            debug ((this: std::ops::Range<usize>).1: usize) => _16;
                             debug slice => _5;
                             scope 8 (inlined ptr::mut_ptr::<impl *mut [u32]>::len) {
                                 debug self => _5;
diff --git a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff
index bb14b909a95..b020d1baafa 100644
--- a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff
+++ b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff
@@ -33,7 +33,8 @@
 +     let _32: u32;
       scope 1 {
 -         debug foo => _1;
-+         debug foo => Foo<T>{ .0 => _31, .1 => _32, };
++         debug ((foo: Foo<T>).0: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>) => _31;
++         debug ((foo: Foo<T>).1: u32) => _32;
           let _5: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>;
           scope 2 {
               debug x => _5;
diff --git a/tests/mir-opt/sroa/structs.constant.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa/structs.constant.ScalarReplacementOfAggregates.diff
index 7ee0431692c..1330f9b3ac8 100644
--- a/tests/mir-opt/sroa/structs.constant.ScalarReplacementOfAggregates.diff
+++ b/tests/mir-opt/sroa/structs.constant.ScalarReplacementOfAggregates.diff
@@ -8,7 +8,8 @@
 +     let _5: u8;
       scope 1 {
 -         debug y => _1;
-+         debug y => (usize, u8){ .0 => _4, .1 => _5, };
++         debug ((y: (usize, u8)).0: usize) => _4;
++         debug ((y: (usize, u8)).1: u8) => _5;
           let _2: usize;
           scope 2 {
               debug t => _2;
diff --git a/tests/mir-opt/sroa/structs.copies.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa/structs.copies.ScalarReplacementOfAggregates.diff
index 0a1de891aee..3621338635e 100644
--- a/tests/mir-opt/sroa/structs.copies.ScalarReplacementOfAggregates.diff
+++ b/tests/mir-opt/sroa/structs.copies.ScalarReplacementOfAggregates.diff
@@ -11,7 +11,10 @@
 +     let _14: std::option::Option<isize>;
       scope 1 {
 -         debug y => _2;
-+         debug y => Foo{ .0 => _11, .1 => _12, .2 => _13, .3 => _14, };
++         debug ((y: Foo).0: u8) => _11;
++         debug ((y: Foo).1: ()) => _12;
++         debug ((y: Foo).2: &str) => _13;
++         debug ((y: Foo).3: std::option::Option<isize>) => _14;
           let _3: u8;
           scope 2 {
               debug t => _3;
@@ -25,7 +28,10 @@
 +                 let _10: std::option::Option<isize>;
                   scope 4 {
 -                     debug z => _5;
-+                     debug z => Foo{ .0 => _7, .1 => _8, .2 => _9, .3 => _10, };
++                     debug ((z: Foo).0: u8) => _7;
++                     debug ((z: Foo).1: ()) => _8;
++                     debug ((z: Foo).2: &str) => _9;
++                     debug ((z: Foo).3: std::option::Option<isize>) => _10;
                       let _6: ();
                       scope 5 {
                           debug a => _6;
diff --git a/tests/mir-opt/sroa/structs.ref_copies.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa/structs.ref_copies.ScalarReplacementOfAggregates.diff
index d7c57c293c4..304bf2fb1a7 100644
--- a/tests/mir-opt/sroa/structs.ref_copies.ScalarReplacementOfAggregates.diff
+++ b/tests/mir-opt/sroa/structs.ref_copies.ScalarReplacementOfAggregates.diff
@@ -11,7 +11,10 @@
 +     let _8: std::option::Option<isize>;
       scope 1 {
 -         debug y => _2;
-+         debug y => Foo{ .0 => _5, .1 => _6, .2 => _7, .3 => _8, };
++         debug ((y: Foo).0: u8) => _5;
++         debug ((y: Foo).1: ()) => _6;
++         debug ((y: Foo).2: &str) => _7;
++         debug ((y: Foo).3: std::option::Option<isize>) => _8;
           let _3: u8;
           scope 2 {
               debug t => _3;
diff --git a/tests/pretty/tests-are-sorted.pp b/tests/pretty/tests-are-sorted.pp
index 7d7f682130d..fd9386be8f3 100644
--- a/tests/pretty/tests-are-sorted.pp
+++ b/tests/pretty/tests-are-sorted.pp
@@ -79,7 +79,7 @@ pub const a_test: test::TestDescAndFn =
     };
 fn a_test() {}
 #[rustc_main]
-#[no_coverage]
+#[coverage(off)]
 pub fn main() -> () {
     extern crate test;
     test::test_main_static(&[&a_test, &m_test, &z_test])
diff --git a/tests/run-coverage/closure_macro.coverage b/tests/run-coverage/closure_macro.coverage
index 1bfd2013da8..0f2c917e090 100644
--- a/tests/run-coverage/closure_macro.coverage
+++ b/tests/run-coverage/closure_macro.coverage
@@ -1,5 +1,5 @@
    LL|       |// compile-flags: --edition=2018
-   LL|       |#![feature(no_coverage)]
+   LL|       |#![feature(coverage_attribute)]
    LL|       |
    LL|       |macro_rules! bail {
    LL|       |    ($msg:literal $(,)?) => {
diff --git a/tests/run-coverage/closure_macro.rs b/tests/run-coverage/closure_macro.rs
index 5e3b00d1ef5..9b289141c2e 100644
--- a/tests/run-coverage/closure_macro.rs
+++ b/tests/run-coverage/closure_macro.rs
@@ -1,5 +1,5 @@
 // compile-flags: --edition=2018
-#![feature(no_coverage)]
+#![feature(coverage_attribute)]
 
 macro_rules! bail {
     ($msg:literal $(,)?) => {
diff --git a/tests/run-coverage/closure_macro_async.coverage b/tests/run-coverage/closure_macro_async.coverage
index 018e3160e4f..74247f1bc6f 100644
--- a/tests/run-coverage/closure_macro_async.coverage
+++ b/tests/run-coverage/closure_macro_async.coverage
@@ -1,5 +1,5 @@
    LL|       |// compile-flags: --edition=2018
-   LL|       |#![feature(no_coverage)]
+   LL|       |#![feature(coverage_attribute)]
    LL|       |
    LL|       |macro_rules! bail {
    LL|       |    ($msg:literal $(,)?) => {
@@ -40,7 +40,7 @@
    LL|      1|    Ok(())
    LL|      1|}
    LL|       |
-   LL|       |#[no_coverage]
+   LL|       |#[coverage(off)]
    LL|       |fn main() {
    LL|       |    executor::block_on(test()).unwrap();
    LL|       |}
@@ -52,18 +52,18 @@
    LL|       |        task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
    LL|       |    };
    LL|       |
-   LL|       |    #[no_coverage]
+   LL|       |    #[coverage(off)]
    LL|       |    pub fn block_on<F: Future>(mut future: F) -> F::Output {
    LL|       |        let mut future = unsafe { Pin::new_unchecked(&mut future) };
    LL|       |        use std::hint::unreachable_unchecked;
    LL|       |        static VTABLE: RawWakerVTable = RawWakerVTable::new(
-   LL|       |            #[no_coverage]
+   LL|       |            #[coverage(off)]
    LL|       |            |_| unsafe { unreachable_unchecked() }, // clone
-   LL|       |            #[no_coverage]
+   LL|       |            #[coverage(off)]
    LL|       |            |_| unsafe { unreachable_unchecked() }, // wake
-   LL|       |            #[no_coverage]
+   LL|       |            #[coverage(off)]
    LL|       |            |_| unsafe { unreachable_unchecked() }, // wake_by_ref
-   LL|       |            #[no_coverage]
+   LL|       |            #[coverage(off)]
    LL|       |            |_| (),
    LL|       |        );
    LL|       |        let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
diff --git a/tests/run-coverage/closure_macro_async.rs b/tests/run-coverage/closure_macro_async.rs
index 3d6bdb38a2a..b4275599e59 100644
--- a/tests/run-coverage/closure_macro_async.rs
+++ b/tests/run-coverage/closure_macro_async.rs
@@ -1,5 +1,5 @@
 // compile-flags: --edition=2018
-#![feature(no_coverage)]
+#![feature(coverage_attribute)]
 
 macro_rules! bail {
     ($msg:literal $(,)?) => {
@@ -39,7 +39,7 @@ pub async fn test() -> Result<(), String> {
     Ok(())
 }
 
-#[no_coverage]
+#[coverage(off)]
 fn main() {
     executor::block_on(test()).unwrap();
 }
@@ -51,18 +51,18 @@ mod executor {
         task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
     };
 
-    #[no_coverage]
+    #[coverage(off)]
     pub fn block_on<F: Future>(mut future: F) -> F::Output {
         let mut future = unsafe { Pin::new_unchecked(&mut future) };
         use std::hint::unreachable_unchecked;
         static VTABLE: RawWakerVTable = RawWakerVTable::new(
-            #[no_coverage]
+            #[coverage(off)]
             |_| unsafe { unreachable_unchecked() }, // clone
-            #[no_coverage]
+            #[coverage(off)]
             |_| unsafe { unreachable_unchecked() }, // wake
-            #[no_coverage]
+            #[coverage(off)]
             |_| unsafe { unreachable_unchecked() }, // wake_by_ref
-            #[no_coverage]
+            #[coverage(off)]
             |_| (),
         );
         let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
diff --git a/tests/run-coverage/no_cov_crate.coverage b/tests/run-coverage/no_cov_crate.coverage
index 73f6fbd0c2b..f5a0322bf3e 100644
--- a/tests/run-coverage/no_cov_crate.coverage
+++ b/tests/run-coverage/no_cov_crate.coverage
@@ -1,17 +1,17 @@
-   LL|       |// Enables `no_coverage` on the entire crate
-   LL|       |#![feature(no_coverage)]
+   LL|       |// Enables `coverage(off)` on the entire crate
+   LL|       |#![feature(coverage_attribute)]
    LL|       |
-   LL|       |#[no_coverage]
+   LL|       |#[coverage(off)]
    LL|       |fn do_not_add_coverage_1() {
    LL|       |    println!("called but not covered");
    LL|       |}
    LL|       |
    LL|       |fn do_not_add_coverage_2() {
-   LL|       |    #![no_coverage]
+   LL|       |    #![coverage(off)]
    LL|       |    println!("called but not covered");
    LL|       |}
    LL|       |
-   LL|       |#[no_coverage]
+   LL|       |#[coverage(off)]
    LL|       |#[allow(dead_code)]
    LL|       |fn do_not_add_coverage_not_called() {
    LL|       |    println!("not called and not covered");
@@ -33,7 +33,7 @@
    LL|       |// FIXME: These test-cases illustrate confusing results of nested functions.
    LL|       |// See https://github.com/rust-lang/rust/issues/93319
    LL|       |mod nested_fns {
-   LL|       |    #[no_coverage]
+   LL|       |    #[coverage(off)]
    LL|       |    pub fn outer_not_covered(is_true: bool) {
    LL|      1|        fn inner(is_true: bool) {
    LL|      1|            if is_true {
@@ -50,7 +50,7 @@
    LL|      1|        println!("called and covered");
    LL|      1|        inner_not_covered(is_true);
    LL|      1|
-   LL|      1|        #[no_coverage]
+   LL|      1|        #[coverage(off)]
    LL|      1|        fn inner_not_covered(is_true: bool) {
    LL|      1|            if is_true {
    LL|      1|                println!("called but not covered");
diff --git a/tests/run-coverage/no_cov_crate.rs b/tests/run-coverage/no_cov_crate.rs
index 5b748aeefb7..e12e4bc55e3 100644
--- a/tests/run-coverage/no_cov_crate.rs
+++ b/tests/run-coverage/no_cov_crate.rs
@@ -1,17 +1,17 @@
-// Enables `no_coverage` on the entire crate
-#![feature(no_coverage)]
+// Enables `coverage(off)` on the entire crate
+#![feature(coverage_attribute)]
 
-#[no_coverage]
+#[coverage(off)]
 fn do_not_add_coverage_1() {
     println!("called but not covered");
 }
 
 fn do_not_add_coverage_2() {
-    #![no_coverage]
+    #![coverage(off)]
     println!("called but not covered");
 }
 
-#[no_coverage]
+#[coverage(off)]
 #[allow(dead_code)]
 fn do_not_add_coverage_not_called() {
     println!("not called and not covered");
@@ -33,7 +33,7 @@ fn add_coverage_not_called() {
 // FIXME: These test-cases illustrate confusing results of nested functions.
 // See https://github.com/rust-lang/rust/issues/93319
 mod nested_fns {
-    #[no_coverage]
+    #[coverage(off)]
     pub fn outer_not_covered(is_true: bool) {
         fn inner(is_true: bool) {
             if is_true {
@@ -50,7 +50,7 @@ mod nested_fns {
         println!("called and covered");
         inner_not_covered(is_true);
 
-        #[no_coverage]
+        #[coverage(off)]
         fn inner_not_covered(is_true: bool) {
             if is_true {
                 println!("called but not covered");
diff --git a/tests/run-make-fulldeps/issue-19371/foo.rs b/tests/run-make-fulldeps/issue-19371/foo.rs
index 68132638759..1c9d33dcc8e 100644
--- a/tests/run-make-fulldeps/issue-19371/foo.rs
+++ b/tests/run-make-fulldeps/issue-19371/foo.rs
@@ -61,6 +61,7 @@ fn compile(code: String, output: PathBuf, sysroot: PathBuf) {
         override_queries: None,
         make_codegen_backend: None,
         registry: rustc_driver::diagnostics_registry(),
+        expanded_args: Default::default(),
     };
 
     interface::run_compiler(config, |compiler| {
diff --git a/tests/run-make-fulldeps/obtain-borrowck/driver.rs b/tests/run-make-fulldeps/obtain-borrowck/driver.rs
index 04c551cf4bb..b59a65a713f 100644
--- a/tests/run-make-fulldeps/obtain-borrowck/driver.rs
+++ b/tests/run-make-fulldeps/obtain-borrowck/driver.rs
@@ -27,7 +27,7 @@ use rustc_interface::{Config, Queries};
 use rustc_middle::query::queries::mir_borrowck::ProvidedValue;
 use rustc_middle::query::{ExternProviders, Providers};
 use rustc_middle::ty::TyCtxt;
-use rustc_session::{Session, EarlyErrorHandler};
+use rustc_session::Session;
 use std::cell::RefCell;
 use std::collections::HashMap;
 use std::thread_local;
@@ -58,7 +58,6 @@ impl rustc_driver::Callbacks for CompilerCalls {
     // the result.
     fn after_analysis<'tcx>(
         &mut self,
-        _handler: &EarlyErrorHandler,
         compiler: &Compiler,
         queries: &'tcx Queries<'tcx>,
     ) -> Compilation {
diff --git a/tests/run-make/compressed-debuginfo/Makefile b/tests/run-make/compressed-debuginfo/Makefile
new file mode 100644
index 00000000000..f9e4927d008
--- /dev/null
+++ b/tests/run-make/compressed-debuginfo/Makefile
@@ -0,0 +1,15 @@
+# ignore-cross-compile
+include ../tools.mk
+
+# only-linux
+# min-llvm-version: 16.0
+#
+# This tests debuginfo-compression.
+
+all: zlib zstandard
+
+zlib:
+	test "`$(RUSTC) --crate-name=foo --crate-type=lib --emit=obj -C debuginfo=full -Z debuginfo-compression=zlib foo.rs 2>&1 | sed 's/.*unknown.*zlib.*/missing/' | head -n 1`" = missing || readelf -t $(TMPDIR)/foo.o | grep -q ZLIB
+
+zstandard:
+	test "`$(RUSTC) --crate-name=foo --crate-type=lib --emit=obj -C debuginfo=full -Z debuginfo-compression=zstd foo.rs 2>&1 | sed 's/.*unknown.*zstd.*/missing/' | head -n 1`" = missing || readelf -t $(TMPDIR)/foo.o | grep -q ZST
diff --git a/tests/run-make/compressed-debuginfo/foo.rs b/tests/run-make/compressed-debuginfo/foo.rs
new file mode 100644
index 00000000000..185ce22450c
--- /dev/null
+++ b/tests/run-make/compressed-debuginfo/foo.rs
@@ -0,0 +1,3 @@
+pub fn foo() -> i32 {
+    42
+}
diff --git a/tests/run-make/emit-path-unhashed/Makefile b/tests/run-make/emit-path-unhashed/Makefile
index 74047fe5f86..611f8578140 100644
--- a/tests/run-make/emit-path-unhashed/Makefile
+++ b/tests/run-make/emit-path-unhashed/Makefile
@@ -5,10 +5,10 @@ OUT=$(TMPDIR)/emit
 # --emit KIND=PATH should not affect crate hash vs --emit KIND
 all: $(OUT)/a/libfoo.rlib $(OUT)/b/libfoo.rlib $(OUT)/c/libfoo.rlib \
 		$(TMPDIR)/libfoo.rlib
-	$(RUSTC) -Zls $(TMPDIR)/libfoo.rlib > $(TMPDIR)/base.txt
-	$(RUSTC) -Zls $(OUT)/a/libfoo.rlib > $(TMPDIR)/a.txt
-	$(RUSTC) -Zls $(OUT)/b/libfoo.rlib > $(TMPDIR)/b.txt
-	$(RUSTC) -Zls $(OUT)/c/libfoo.rlib > $(TMPDIR)/c.txt
+	$(RUSTC) -Zls=root $(TMPDIR)/libfoo.rlib > $(TMPDIR)/base.txt
+	$(RUSTC) -Zls=root $(OUT)/a/libfoo.rlib > $(TMPDIR)/a.txt
+	$(RUSTC) -Zls=root $(OUT)/b/libfoo.rlib > $(TMPDIR)/b.txt
+	$(RUSTC) -Zls=root $(OUT)/c/libfoo.rlib > $(TMPDIR)/c.txt
 
 	diff $(TMPDIR)/base.txt $(TMPDIR)/a.txt
 	diff $(TMPDIR)/base.txt $(TMPDIR)/b.txt
diff --git a/tests/run-make/issue-88756-default-output/output-default.stdout b/tests/run-make/issue-88756-default-output/output-default.stdout
index de8ff0e5f89..f5981045b03 100644
--- a/tests/run-make/issue-88756-default-output/output-default.stdout
+++ b/tests/run-make/issue-88756-default-output/output-default.stdout
@@ -100,7 +100,7 @@ Options:
                         check if given theme is valid
         --resource-suffix PATH
                         suffix to add to CSS and JavaScript files, e.g.,
-                        "light.css" will become "light-suffix.css"
+                        "search-index.js" will become "search-index-suffix.js"
         --edition EDITION
                         edition to use when compiling rust code (default:
                         2015)
diff --git a/tests/run-make/ls-metadata/Makefile b/tests/run-make/ls-metadata/Makefile
index 123dd64e15c..f03569baef7 100644
--- a/tests/run-make/ls-metadata/Makefile
+++ b/tests/run-make/ls-metadata/Makefile
@@ -3,6 +3,6 @@ include ../tools.mk
 
 all:
 	$(RUSTC) foo.rs
-	$(RUSTC) -Z ls $(TMPDIR)/foo
+	$(RUSTC) -Z ls=root $(TMPDIR)/foo
 	touch $(TMPDIR)/bar
-	$(RUSTC) -Z ls $(TMPDIR)/bar
+	$(RUSTC) -Z ls=root $(TMPDIR)/bar
diff --git a/tests/run-make/lto-linkage-used-attr/Makefile b/tests/run-make/lto-linkage-used-attr/Makefile
new file mode 100644
index 00000000000..e78b83890ed
--- /dev/null
+++ b/tests/run-make/lto-linkage-used-attr/Makefile
@@ -0,0 +1,9 @@
+include ../tools.mk
+
+# Verify that the impl_* symbols are preserved. #108030
+# only-x86_64-unknown-linux-gnu
+# min-llvm-version: 17
+
+all:
+	$(RUSTC) -Cdebuginfo=0 -Copt-level=3 lib.rs
+	$(RUSTC) -Clto=fat -Cdebuginfo=0 -Copt-level=3 main.rs
diff --git a/tests/run-make/lto-linkage-used-attr/lib.rs b/tests/run-make/lto-linkage-used-attr/lib.rs
new file mode 100644
index 00000000000..0a92ea9cd22
--- /dev/null
+++ b/tests/run-make/lto-linkage-used-attr/lib.rs
@@ -0,0 +1,50 @@
+#![crate_type = "rlib"]
+#![crate_type = "cdylib"]
+
+#[macro_export]
+macro_rules! asm_func {
+    ($name:expr, $body:expr $(, $($args:tt)*)?) => {
+        core::arch::global_asm!(
+            concat!(
+                ".p2align 4\n",
+                ".hidden ", $name, "\n",
+                ".global ", $name, "\n",
+                ".type ", $name, ",@function\n",
+                $name, ":\n",
+                $body,
+                ".size ", $name, ",.-", $name,
+            )
+            $(, $($args)*)?
+        );
+    };
+}
+
+macro_rules! libcall_trampoline {
+    ($libcall:ident ; $libcall_impl:ident) => {
+        asm_func!(
+            stringify!($libcall),
+            concat!(
+                "
+                   .cfi_startproc simple
+                   .cfi_def_cfa_offset 0
+                    jmp {}
+                    .cfi_endproc
+                ",
+            ),
+            sym $libcall_impl
+        );
+    };
+}
+
+pub mod trampolines {
+    extern "C" {
+        pub fn table_fill_funcref();
+        pub fn table_fill_externref();
+    }
+
+    unsafe extern "C" fn impl_table_fill_funcref() {}
+    unsafe extern "C" fn impl_table_fill_externref() {}
+
+    libcall_trampoline!(table_fill_funcref ; impl_table_fill_funcref);
+    libcall_trampoline!(table_fill_externref ; impl_table_fill_externref);
+}
diff --git a/tests/run-make/lto-linkage-used-attr/main.rs b/tests/run-make/lto-linkage-used-attr/main.rs
new file mode 100644
index 00000000000..256b02e5b0b
--- /dev/null
+++ b/tests/run-make/lto-linkage-used-attr/main.rs
@@ -0,0 +1,10 @@
+extern crate lib;
+
+use lib::trampolines::*;
+
+fn main() {
+    unsafe {
+        table_fill_externref();
+        table_fill_funcref();
+    }
+}
diff --git a/tests/run-make/output-filename-overwrites-input/Makefile b/tests/run-make/output-filename-overwrites-input/Makefile
index 605b86b253e..fe5d231382d 100644
--- a/tests/run-make/output-filename-overwrites-input/Makefile
+++ b/tests/run-make/output-filename-overwrites-input/Makefile
@@ -8,7 +8,7 @@ all:
 	cp bar.rs $(TMPDIR)/bar.rlib
 	$(RUSTC) $(TMPDIR)/bar.rlib -o $(TMPDIR)/bar.rlib 2>&1 \
 		| $(CGREP) -e "the input file \".*bar.rlib\" would be overwritten by the generated executable"
-	$(RUSTC) foo.rs 2>&1 && $(RUSTC) -Z ls $(TMPDIR)/foo 2>&1
+	$(RUSTC) foo.rs 2>&1 && $(RUSTC) -Z ls=root $(TMPDIR)/foo 2>&1
 	cp foo.rs $(TMPDIR)/foo.rs
 	$(RUSTC) $(TMPDIR)/foo.rs -o $(TMPDIR)/foo.rs 2>&1 \
 		| $(CGREP) -e "the input file \".*foo.rs\" would be overwritten by the generated executable"
diff --git a/tests/run-make/pdb-buildinfo-cl-cmd/Makefile b/tests/run-make/pdb-buildinfo-cl-cmd/Makefile
new file mode 100644
index 00000000000..a7be301a5b0
--- /dev/null
+++ b/tests/run-make/pdb-buildinfo-cl-cmd/Makefile
@@ -0,0 +1,16 @@
+include ../tools.mk
+
+# only-windows-msvc
+
+# tests if the pdb contains the following information in the LF_BUILDINFO:
+# 1. the commandline args to compile it (cmd)
+# 2. full path to the compiler (cl)
+
+# we just do a stringsearch on the pdb, as these need to show up at least once, as the LF_BUILDINFO is created for each cgu
+# actual parsing would be better, but this is a simple and good enough solution for now
+
+all:
+	$(RUSTC_ORIGINAL) main.rs -g --crate-name my_crate_name --crate-type bin -C metadata=dc9ef878b0a48666 --out-dir $(TMPDIR)
+	cat '$(TMPDIR)/my_crate_name.pdb' | grep -F '$(RUSTC_ORIGINAL)'
+# using a file containing the string so I don't have problems with escaping quotes and spaces 
+	cat '$(TMPDIR)/my_crate_name.pdb' | grep -f 'stringlist.txt'
diff --git a/tests/run-make/pdb-buildinfo-cl-cmd/main.rs b/tests/run-make/pdb-buildinfo-cl-cmd/main.rs
new file mode 100644
index 00000000000..f79c691f085
--- /dev/null
+++ b/tests/run-make/pdb-buildinfo-cl-cmd/main.rs
@@ -0,0 +1,2 @@
+fn main() {
+}
diff --git a/tests/run-make/pdb-buildinfo-cl-cmd/stringlist.txt b/tests/run-make/pdb-buildinfo-cl-cmd/stringlist.txt
new file mode 100644
index 00000000000..634e9f19e89
--- /dev/null
+++ b/tests/run-make/pdb-buildinfo-cl-cmd/stringlist.txt
@@ -0,0 +1 @@
+"main.rs" "-g" "--crate-name" "my_crate_name" "--crate-type" "bin" "-C" "metadata=dc9ef878b0a48666" "--out-dir"
\ No newline at end of file
diff --git a/tests/run-make/print-cfg/Makefile b/tests/run-make/print-cfg/Makefile
index 654c303b3e2..6b153e5b54e 100644
--- a/tests/run-make/print-cfg/Makefile
+++ b/tests/run-make/print-cfg/Makefile
@@ -13,19 +13,19 @@ all: default output_to_file
 
 output_to_file:
 	# Backend-independent, printed by rustc_driver_impl/src/lib.rs
-	$(RUSTC) --target x86_64-pc-windows-gnu --print cfg=$(TMPDIR)/cfg.txt -Z unstable-options
+	$(RUSTC) --target x86_64-pc-windows-gnu --print cfg=$(TMPDIR)/cfg.txt
 	$(CGREP) windows < $(TMPDIR)/cfg.txt
 
 	# Printed from CodegenBackend trait impl in rustc_codegen_llvm/src/lib.rs
-	$(RUSTC) --print relocation-models=$(TMPDIR)/relocation-models.txt -Z unstable-options
+	$(RUSTC) --print relocation-models=$(TMPDIR)/relocation-models.txt
 	$(CGREP) dynamic-no-pic < $(TMPDIR)/relocation-models.txt
 
 	# Printed by compiler/rustc_codegen_llvm/src/llvm_util.rs
-	$(RUSTC) --target wasm32-unknown-unknown --print target-features=$(TMPDIR)/target-features.txt -Z unstable-options
+	$(RUSTC) --target wasm32-unknown-unknown --print target-features=$(TMPDIR)/target-features.txt
 	$(CGREP) reference-types < $(TMPDIR)/target-features.txt
 
 	# Printed by C++ code in rustc_llvm/llvm-wrapper/PassWrapper.cpp
-	$(RUSTC) --target wasm32-unknown-unknown --print target-cpus=$(TMPDIR)/target-cpus.txt -Z unstable-options
+	$(RUSTC) --target wasm32-unknown-unknown --print target-cpus=$(TMPDIR)/target-cpus.txt
 	$(CGREP) generic < $(TMPDIR)/target-cpus.txt
 
 ifdef IS_WINDOWS
diff --git a/tests/run-make/rustdoc-themes/Makefile b/tests/run-make/rustdoc-themes/Makefile
index a6d9a43addf..a4980eb0b3e 100644
--- a/tests/run-make/rustdoc-themes/Makefile
+++ b/tests/run-make/rustdoc-themes/Makefile
@@ -5,6 +5,7 @@ include ../tools.mk
 OUTPUT_DIR := "$(TMPDIR)/rustdoc-themes"
 
 all:
-	cp $(S)/src/librustdoc/html/static/css/themes/light.css $(TMPDIR)/test.css
+	awk '/Begin theme: light/ {in_theme=1;next} /End theme:/ {in_theme=0} { if (in_theme) print }' \
+		< '$(S)/src/librustdoc/html/static/css/noscript.css' > '$(TMPDIR)/test.css'
 	$(RUSTDOC) -o $(OUTPUT_DIR) foo.rs --theme $(TMPDIR)/test.css
 	$(HTMLDOCCK) $(OUTPUT_DIR) foo.rs
diff --git a/tests/rustdoc-gui/help-page.goml b/tests/rustdoc-gui/help-page.goml
index 6e880302f28..84c20355500 100644
--- a/tests/rustdoc-gui/help-page.goml
+++ b/tests/rustdoc-gui/help-page.goml
@@ -33,21 +33,21 @@ define-function: (
 
 call-function: ("check-colors", {
     "theme": "ayu",
-    "color": "rgb(197, 197, 197)",
-    "background": "rgb(49, 69, 89)",
-    "box_shadow": "rgb(92, 103, 115)",
+    "color": "#c5c5c5",
+    "background": "#314559",
+    "box_shadow": "#5c6773",
 })
 call-function: ("check-colors", {
     "theme": "dark",
-    "color": "rgb(0, 0, 0)",
-    "background": "rgb(250, 251, 252)",
-    "box_shadow": "rgb(198, 203, 209)",
+    "color": "#000",
+    "background": "#fafbfc",
+    "box_shadow": "#c6cbd1",
 })
 call-function: ("check-colors", {
     "theme": "light",
-    "color": "rgb(0, 0, 0)",
-    "background": "rgb(250, 251, 252)",
-    "box_shadow": "rgb(198, 203, 209)",
+    "color": "#000",
+    "background": "#fafbfc",
+    "box_shadow": "#c6cbd1",
 })
 
 // This test ensures that opening the help popover without switching pages works.
diff --git a/tests/rustdoc-gui/search-no-result.goml b/tests/rustdoc-gui/search-no-result.goml
index 46d1856b4d6..e7c64791256 100644
--- a/tests/rustdoc-gui/search-no-result.goml
+++ b/tests/rustdoc-gui/search-no-result.goml
@@ -21,16 +21,16 @@ define-function: (
 
 call-function: ("check-no-result", {
     "theme": "ayu",
-    "link": "rgb(57, 175, 215)",
-    "link_hover": "rgb(57, 175, 215)",
+    "link": "#39afd7",
+    "link_hover": "#39afd7",
 })
 call-function: ("check-no-result", {
     "theme": "dark",
-    "link": "rgb(210, 153, 29)",
-    "link_hover": "rgb(210, 153, 29)",
+    "link": "#d2991d",
+    "link_hover": "#d2991d",
 })
 call-function: ("check-no-result", {
     "theme": "light",
-    "link": "rgb(56, 115, 173)",
-    "link_hover": "rgb(56, 115, 173)",
+    "link": "#3873ad",
+    "link_hover": "#3873ad",
 })
diff --git a/tests/rustdoc-gui/search-result-color.goml b/tests/rustdoc-gui/search-result-color.goml
index f9f81c5ba04..44677dfbfef 100644
--- a/tests/rustdoc-gui/search-result-color.goml
+++ b/tests/rustdoc-gui/search-result-color.goml
@@ -151,7 +151,7 @@ assert-css: (
 )
 assert-css: (
     "//*[@class='result-name']//*[text()='test_docs::']/ancestor::a",
-    {"color": "#fff", "background-color": "rgb(60, 60, 60)"},
+    {"color": "#fff", "background-color": "#3c3c3c"},
 )
 
 // Dark theme
diff --git a/tests/rustdoc-gui/sidebar-source-code.goml b/tests/rustdoc-gui/sidebar-source-code.goml
index 69c589741cb..92b9045b734 100644
--- a/tests/rustdoc-gui/sidebar-source-code.goml
+++ b/tests/rustdoc-gui/sidebar-source-code.goml
@@ -73,7 +73,7 @@ assert: "//*[@class='dir-entry' and @open]/*[text()='sub_mod']"
 // Only "another_folder" should be "open" in "lib2".
 assert: "//*[@class='dir-entry' and not(@open)]/*[text()='another_mod']"
 // All other trees should be collapsed.
-assert-count: ("//*[@id='src-sidebar']/details[not(text()='lib2') and not(@open)]", 9)
+assert-count: ("//*[@id='src-sidebar']/details[not(text()='lib2') and not(@open)]", 10)
 
 // We now switch to mobile mode.
 set-window-size: (600, 600)
diff --git a/tests/rustdoc-gui/src/theme_css/Cargo.lock b/tests/rustdoc-gui/src/theme_css/Cargo.lock
new file mode 100644
index 00000000000..7ad6737a4d0
--- /dev/null
+++ b/tests/rustdoc-gui/src/theme_css/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "theme_css"
+version = "0.1.0"
diff --git a/tests/rustdoc-gui/src/theme_css/Cargo.toml b/tests/rustdoc-gui/src/theme_css/Cargo.toml
new file mode 100644
index 00000000000..798e64f9309
--- /dev/null
+++ b/tests/rustdoc-gui/src/theme_css/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "theme_css"
+version = "0.1.0"
+edition = "2018"
+
+[lib]
+path = "lib.rs"
diff --git a/src/librustdoc/html/static/css/themes/light.css b/tests/rustdoc-gui/src/theme_css/custom-theme.css
index 9c016db4502..260ef87f6ea 100644
--- a/src/librustdoc/html/static/css/themes/light.css
+++ b/tests/rustdoc-gui/src/theme_css/custom-theme.css
@@ -1,5 +1,5 @@
 :root {
-	--main-background-color: white;
+	--main-background-color: red;
 	--main-color: black;
 	--settings-input-color: #2196f3;
 	--settings-input-border-color: #717171;
diff --git a/tests/rustdoc-gui/src/theme_css/lib.rs b/tests/rustdoc-gui/src/theme_css/lib.rs
new file mode 100644
index 00000000000..e9f3265fa6b
--- /dev/null
+++ b/tests/rustdoc-gui/src/theme_css/lib.rs
@@ -0,0 +1,2 @@
+// compile-flags: --theme custom-theme.css
+//! <div class="custom-text">custom text</div>
diff --git a/tests/rustdoc-gui/theme-change.goml b/tests/rustdoc-gui/theme-change.goml
index e4b031b735e..fdaf9d6289a 100644
--- a/tests/rustdoc-gui/theme-change.goml
+++ b/tests/rustdoc-gui/theme-change.goml
@@ -65,3 +65,36 @@ assert-local-storage: { "rustdoc-theme": "light" }
 reload:
 wait-for: "#settings"
 assert: "#preferred-light-theme.setting-line.hidden"
+
+// Ensures that the custom theme feature is working as expected.
+go-to: "file://" + |DOC_PATH| + "/theme_css/index.html"
+set-local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": "dark"}
+reload:
+
+store-value: (background_light, "white")
+store-value: (background_dark, "#353535")
+store-value: (background_ayu, "#0f1419")
+store-value: (background_custom_theme, "red")
+
+click: "#settings-menu"
+wait-for: "#theme-ayu"
+click: "#theme-ayu"
+// should be the ayu theme so let's check the color.
+wait-for-css: ("body", { "background-color": |background_ayu| })
+assert-local-storage: { "rustdoc-theme": "ayu" }
+assert-text: (".custom-text", "custom text")
+click: "#theme-light"
+// should be the light theme so let's check the color.
+wait-for-css: ("body", { "background-color": |background_light| })
+assert-local-storage: { "rustdoc-theme": "light" }
+assert-text: (".custom-text", "custom text")
+click: "#theme-dark"
+// Should be the dark theme so let's check the color.
+wait-for-css: ("body", { "background-color": |background_dark| })
+assert-local-storage: { "rustdoc-theme": "dark" }
+assert-text: (".custom-text", "custom text")
+click: "#theme-custom-theme"
+// Should be the custom theme so let's check the color.
+wait-for-css: ("body", { "background-color": |background_custom_theme| })
+assert-local-storage: { "rustdoc-theme": "custom-theme" }
+assert-text: (".custom-text", "custom text")
diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs
new file mode 100644
index 00000000000..dd8759b7e37
--- /dev/null
+++ b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs
@@ -0,0 +1,85 @@
+// This test ensures that warnings are working as expected for "custom_code_classes_in_docs"
+// feature.
+
+#![feature(custom_code_classes_in_docs)]
+#![deny(warnings)]
+#![feature(no_core)]
+#![no_core]
+
+/// ```{. }
+/// main;
+/// ```
+//~^^^ ERROR unexpected ` ` character after `.`
+pub fn foo() {}
+
+/// ```{class= a}
+/// main;
+/// ```
+//~^^^ ERROR unexpected ` ` character after `=`
+pub fn foo2() {}
+
+/// ```{#id}
+/// main;
+/// ```
+//~^^^ ERROR unexpected character `#`
+pub fn foo3() {}
+
+/// ```{{
+/// main;
+/// ```
+//~^^^ ERROR unexpected character `{`
+pub fn foo4() {}
+
+/// ```}
+/// main;
+/// ```
+//~^^^ ERROR unexpected character `}`
+pub fn foo5() {}
+
+/// ```)
+/// main;
+/// ```
+//~^^^ ERROR unexpected character `)`
+pub fn foo6() {}
+
+/// ```{class=}
+/// main;
+/// ```
+//~^^^ ERROR unexpected `}` character after `=`
+pub fn foo7() {}
+
+/// ```(
+/// main;
+/// ```
+//~^^^ ERROR unclosed comment: missing `)` at the end
+pub fn foo8() {}
+
+/// ```{class=one=two}
+/// main;
+/// ```
+//~^^^ ERROR unexpected `=`
+pub fn foo9() {}
+
+/// ```{.one.two}
+/// main;
+/// ```
+//~^^^ ERROR unexpected `.` character
+pub fn foo10() {}
+
+/// ```{class=.one}
+/// main;
+/// ```
+//~^^^ ERROR unexpected `.` character after `=`
+pub fn foo11() {}
+
+/// ```{class=one.two}
+/// main;
+/// ```
+//~^^^ ERROR unexpected `.` character
+pub fn foo12() {}
+
+/// ```{(comment)}
+/// main;
+/// ```
+//~^^^ ERROR unexpected character `(`
+pub fn foo13() {}
diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr
new file mode 100644
index 00000000000..3e0dc4b2f7a
--- /dev/null
+++ b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr
@@ -0,0 +1,113 @@
+error: unexpected ` ` character after `.`
+  --> $DIR/custom_code_classes_in_docs-warning.rs:9:1
+   |
+LL | / /// ```{. }
+LL | | /// main;
+LL | | /// ```
+   | |_______^
+   |
+note: the lint level is defined here
+  --> $DIR/custom_code_classes_in_docs-warning.rs:5:9
+   |
+LL | #![deny(warnings)]
+   |         ^^^^^^^^
+   = note: `#[deny(rustdoc::invalid_codeblock_attributes)]` implied by `#[deny(warnings)]`
+
+error: unexpected ` ` character after `=`
+  --> $DIR/custom_code_classes_in_docs-warning.rs:15:1
+   |
+LL | / /// ```{class= a}
+LL | | /// main;
+LL | | /// ```
+   | |_______^
+
+error: unexpected character `#`
+  --> $DIR/custom_code_classes_in_docs-warning.rs:21:1
+   |
+LL | / /// ```{#id}
+LL | | /// main;
+LL | | /// ```
+   | |_______^
+
+error: unexpected character `{`
+  --> $DIR/custom_code_classes_in_docs-warning.rs:27:1
+   |
+LL | / /// ```{{
+LL | | /// main;
+LL | | /// ```
+   | |_______^
+
+error: unexpected character `}`
+  --> $DIR/custom_code_classes_in_docs-warning.rs:33:1
+   |
+LL | / /// ```}
+LL | | /// main;
+LL | | /// ```
+   | |_______^
+
+error: unexpected character `)`
+  --> $DIR/custom_code_classes_in_docs-warning.rs:39:1
+   |
+LL | / /// ```)
+LL | | /// main;
+LL | | /// ```
+   | |_______^
+
+error: unexpected `}` character after `=`
+  --> $DIR/custom_code_classes_in_docs-warning.rs:45:1
+   |
+LL | / /// ```{class=}
+LL | | /// main;
+LL | | /// ```
+   | |_______^
+
+error: unclosed comment: missing `)` at the end
+  --> $DIR/custom_code_classes_in_docs-warning.rs:51:1
+   |
+LL | / /// ```(
+LL | | /// main;
+LL | | /// ```
+   | |_______^
+
+error: unexpected `=` character
+  --> $DIR/custom_code_classes_in_docs-warning.rs:57:1
+   |
+LL | / /// ```{class=one=two}
+LL | | /// main;
+LL | | /// ```
+   | |_______^
+
+error: unexpected `.` character
+  --> $DIR/custom_code_classes_in_docs-warning.rs:63:1
+   |
+LL | / /// ```{.one.two}
+LL | | /// main;
+LL | | /// ```
+   | |_______^
+
+error: unexpected `.` character after `=`
+  --> $DIR/custom_code_classes_in_docs-warning.rs:69:1
+   |
+LL | / /// ```{class=.one}
+LL | | /// main;
+LL | | /// ```
+   | |_______^
+
+error: unexpected `.` character
+  --> $DIR/custom_code_classes_in_docs-warning.rs:75:1
+   |
+LL | / /// ```{class=one.two}
+LL | | /// main;
+LL | | /// ```
+   | |_______^
+
+error: unexpected character `(`
+  --> $DIR/custom_code_classes_in_docs-warning.rs:81:1
+   |
+LL | / /// ```{(comment)}
+LL | | /// main;
+LL | | /// ```
+   | |_______^
+
+error: aborting due to 13 previous errors
+
diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.rs b/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.rs
new file mode 100644
index 00000000000..57d9038cb0c
--- /dev/null
+++ b/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.rs
@@ -0,0 +1,17 @@
+// This test ensures that warnings are working as expected for "custom_code_classes_in_docs"
+// feature.
+
+#![feature(custom_code_classes_in_docs)]
+#![deny(warnings)]
+#![feature(no_core)]
+#![no_core]
+
+/// ```{class="}
+/// main;
+/// ```
+//~^^^ ERROR unclosed quote string
+//~| ERROR unclosed quote string
+/// ```"
+/// main;
+/// ```
+pub fn foo() {}
diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.stderr b/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.stderr
new file mode 100644
index 00000000000..4f2ded78c29
--- /dev/null
+++ b/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.stderr
@@ -0,0 +1,33 @@
+error: unclosed quote string `"`
+  --> $DIR/custom_code_classes_in_docs-warning3.rs:9:1
+   |
+LL | / /// ```{class="}
+LL | | /// main;
+LL | | /// ```
+LL | |
+...  |
+LL | | /// main;
+LL | | /// ```
+   | |_______^
+   |
+note: the lint level is defined here
+  --> $DIR/custom_code_classes_in_docs-warning3.rs:5:9
+   |
+LL | #![deny(warnings)]
+   |         ^^^^^^^^
+   = note: `#[deny(rustdoc::invalid_codeblock_attributes)]` implied by `#[deny(warnings)]`
+
+error: unclosed quote string `"`
+  --> $DIR/custom_code_classes_in_docs-warning3.rs:9:1
+   |
+LL | / /// ```{class="}
+LL | | /// main;
+LL | | /// ```
+LL | |
+...  |
+LL | | /// main;
+LL | | /// ```
+   | |_______^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs
new file mode 100644
index 00000000000..8aa13b2d5d1
--- /dev/null
+++ b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs
@@ -0,0 +1,5 @@
+/// ```{class=language-c}
+/// int main(void) { return 0; }
+/// ```
+//~^^^ ERROR 1:1: 3:8: custom classes in code blocks are unstable [E0658]
+pub struct Bar;
diff --git a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr
new file mode 100644
index 00000000000..c41ebfc8073
--- /dev/null
+++ b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr
@@ -0,0 +1,15 @@
+error[E0658]: custom classes in code blocks are unstable
+  --> $DIR/feature-gate-custom_code_classes_in_docs.rs:1:1
+   |
+LL | / /// ```{class=language-c}
+LL | | /// int main(void) { return 0; }
+LL | | /// ```
+   | |_______^
+   |
+   = note: see issue #79483 <https://github.com/rust-lang/rust/issues/79483> for more information
+   = help: add `#![feature(custom_code_classes_in_docs)]` to the crate attributes to enable
+   = note: found these custom classes: class=language-c
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/rustdoc-ui/issues/issue-91713.stdout b/tests/rustdoc-ui/issues/issue-91713.stdout
index 16783524363..bbea7e5c212 100644
--- a/tests/rustdoc-ui/issues/issue-91713.stdout
+++ b/tests/rustdoc-ui/issues/issue-91713.stdout
@@ -1,4 +1,5 @@
 Available passes for running rustdoc:
+check-custom-code-classes - check for custom code classes without the feature-gate enabled
 check_doc_test_visibility - run various visibility-related lints on doctests
         strip-hidden - strips all `#[doc(hidden)]` items from the output
        strip-private - strips all private items from a crate which cannot be seen externally, implies strip-priv-imports
@@ -10,6 +11,7 @@ calculate-doc-coverage - counts the number of items with and without documentati
            run-lints - runs some of rustdoc's lints
 
 Default passes for rustdoc:
+check-custom-code-classes
  collect-trait-impls
 check_doc_test_visibility
         strip-hidden  (when not --document-hidden-items)
diff --git a/tests/rustdoc/auxiliary/cross_crate_generic_typedef.rs b/tests/rustdoc/auxiliary/cross_crate_generic_typedef.rs
new file mode 100644
index 00000000000..f4e020b3b95
--- /dev/null
+++ b/tests/rustdoc/auxiliary/cross_crate_generic_typedef.rs
@@ -0,0 +1,5 @@
+pub struct InlineOne<A> {
+   pub inline: A
+}
+
+pub type InlineU64 = InlineOne<u64>;
diff --git a/tests/rustdoc/const-generics/const-generic-defaults.rs b/tests/rustdoc/const-generics/const-generic-defaults.rs
index f781c6a62f2..7a0a794112d 100644
--- a/tests/rustdoc/const-generics/const-generic-defaults.rs
+++ b/tests/rustdoc/const-generics/const-generic-defaults.rs
@@ -1,5 +1,5 @@
 #![crate_name = "foo"]
 
 // @has foo/struct.Foo.html '//pre[@class="rust item-decl"]' \
-//      'pub struct Foo<const M: usize = 10, const N: usize = M, T = i32>(_);'
+//      'pub struct Foo<const M: usize = 10, const N: usize = M, T = i32>('
 pub struct Foo<const M: usize = 10, const N: usize = M, T = i32>(T);
diff --git a/tests/rustdoc/const-generics/const-generics-docs.rs b/tests/rustdoc/const-generics/const-generics-docs.rs
index 828486a41d4..70a9518f05b 100644
--- a/tests/rustdoc/const-generics/const-generics-docs.rs
+++ b/tests/rustdoc/const-generics/const-generics-docs.rs
@@ -33,7 +33,7 @@ impl<const N: usize> Trait<N> for [u8; N] {}
 // @has foo/struct.Foo.html '//pre[@class="rust item-decl"]' \
 //      'pub struct Foo<const N: usize> where u8: Trait<N>'
 pub struct Foo<const N: usize> where u8: Trait<N>;
-// @has foo/struct.Bar.html '//pre[@class="rust item-decl"]' 'pub struct Bar<T, const N: usize>(_)'
+// @has foo/struct.Bar.html '//pre[@class="rust item-decl"]' 'pub struct Bar<T, const N: usize>('
 pub struct Bar<T, const N: usize>([T; N]);
 
 // @has foo/struct.Foo.html '//*[@id="impl-Foo%3CM%3E"]/h3[@class="code-header"]' 'impl<const M: usize> Foo<M>where u8: Trait<M>'
@@ -92,7 +92,7 @@ macro_rules! define_me {
 }
 
 // @has foo/struct.Foz.html '//pre[@class="rust item-decl"]' \
-//      'pub struct Foz<const N: usize>(_);'
+//      'pub struct Foz<const N: usize>(/* private fields */);'
 define_me!(Foz<N>);
 
 trait Q {
diff --git a/tests/rustdoc/custom_code_classes.rs b/tests/rustdoc/custom_code_classes.rs
new file mode 100644
index 00000000000..cd20d8b7d6c
--- /dev/null
+++ b/tests/rustdoc/custom_code_classes.rs
@@ -0,0 +1,28 @@
+// Test for `custom_code_classes_in_docs` feature.
+
+#![feature(custom_code_classes_in_docs)]
+#![crate_name = "foo"]
+#![feature(no_core)]
+#![no_core]
+
+// @has 'foo/struct.Bar.html'
+// @has - '//*[@id="main-content"]//pre[@class="language-whatever hoho-c"]' 'main;'
+// @has - '//*[@id="main-content"]//pre[@class="language-whatever2 haha-c"]' 'main;'
+// @has - '//*[@id="main-content"]//pre[@class="language-whatever4 huhu-c"]' 'main;'
+
+/// ```{class=hoho-c},whatever
+/// main;
+/// ```
+///
+/// Testing multiple kinds of orders.
+///
+/// ```whatever2 {class=haha-c}
+/// main;
+/// ```
+///
+/// Testing with multiple "unknown". Only the first should be used.
+///
+/// ```whatever4,{.huhu-c} whatever5
+/// main;
+/// ```
+pub struct Bar;
diff --git a/tests/rustdoc/issue-32077-type-alias-impls.rs b/tests/rustdoc/issue-32077-type-alias-impls.rs
new file mode 100644
index 00000000000..ac486c36ad0
--- /dev/null
+++ b/tests/rustdoc/issue-32077-type-alias-impls.rs
@@ -0,0 +1,66 @@
+// Regression test for <https://github.com/rust-lang/rust/issues/32077>.
+
+#![crate_name = "foo"]
+
+pub struct GenericStruct<T>(T);
+
+impl<T> GenericStruct<T> {
+    pub fn on_gen(arg: T) {}
+}
+
+impl GenericStruct<u32> {
+    pub fn on_u32(arg: u32) {}
+}
+
+pub trait Foo {}
+pub trait Bar {}
+
+impl<T> Foo for GenericStruct<T> {}
+impl Bar for GenericStruct<u32> {}
+
+// @has 'foo/type.TypedefStruct.html'
+// We check that "Aliased type" is also present as a title in the sidebar.
+// @has - '//*[@class="sidebar-elems"]//h3/a[@href="#aliased-type"]' 'Aliased type'
+// We check that we have the implementation of the type alias itself.
+// @has - '//*[@id="impl-TypedefStruct"]/h3' 'impl TypedefStruct'
+// @has - '//*[@id="method.on_alias"]/h4' 'pub fn on_alias()'
+// @has - '//*[@id="impl-GenericStruct%3CT%3E"]/h3' 'impl<T> GenericStruct<T>'
+// @has - '//*[@id="method.on_gen"]/h4' 'pub fn on_gen(arg: T)'
+// @has - '//*[@id="impl-Foo-for-GenericStruct%3CT%3E"]/h3' 'impl<T> Foo for GenericStruct<T>'
+// This trait implementation doesn't match the type alias parameters so shouldn't appear in docs.
+// @!has - '//h3' 'impl Bar for GenericStruct<u32> {}'
+// Same goes for the `Deref` impl.
+// @!has - '//h2' 'Methods from Deref<Target = u32>'
+// @count - '//nav[@class="sidebar"]//a' 'on_alias' 1
+// @count - '//nav[@class="sidebar"]//a' 'on_gen' 1
+// @count - '//nav[@class="sidebar"]//a' 'Foo' 1
+// @!has - '//nav[@class="sidebar"]//a' 'Bar'
+// @!has - '//nav[@class="sidebar"]//a' 'on_u32'
+pub type TypedefStruct = GenericStruct<u8>;
+
+impl TypedefStruct {
+    pub fn on_alias() {}
+}
+
+impl std::ops::Deref for GenericStruct<u32> {
+    type Target = u32;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+pub struct Wrap<T>(GenericStruct<T>);
+
+// @has 'foo/type.Alias.html'
+// @has - '//h2' 'Methods from Deref<Target = u32>'
+// @has - '//*[@id="impl-Deref-for-Wrap%3CT%3E"]/h3' 'impl<T> Deref for Wrap<T>'
+pub type Alias = Wrap<u32>;
+
+impl<T> std::ops::Deref for Wrap<T> {
+    type Target = GenericStruct<T>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
diff --git a/tests/rustdoc/issue-88600.rs b/tests/rustdoc/issue-88600.rs
index db0d102b741..f89af472f6e 100644
--- a/tests/rustdoc/issue-88600.rs
+++ b/tests/rustdoc/issue-88600.rs
@@ -8,10 +8,10 @@ pub struct S;
 
 // @has issue_88600/enum.FooEnum.html
 pub enum FooEnum {
-    // @has - '//*[@id="variant.HiddenTupleItem"]//h3' 'HiddenTupleItem(_)'
+    // @has - '//*[@id="variant.HiddenTupleItem"]//h3' 'HiddenTupleItem(/* private fields */)'
     // @count - '//*[@id="variant.HiddenTupleItem.field.0"]' 0
     HiddenTupleItem(#[doc(hidden)] H),
-    // @has - '//*[@id="variant.MultipleHidden"]//h3' 'MultipleHidden(_, _)'
+    // @has - '//*[@id="variant.MultipleHidden"]//h3' 'MultipleHidden(/* private fields */)'
     // @count - '//*[@id="variant.MultipleHidden.field.0"]' 0
     // @count - '//*[@id="variant.MultipleHidden.field.1"]' 0
     MultipleHidden(#[doc(hidden)] H, #[doc(hidden)] H),
diff --git a/tests/rustdoc/private-fields-tuple-struct.rs b/tests/rustdoc/private-fields-tuple-struct.rs
new file mode 100644
index 00000000000..c6989dd8cdf
--- /dev/null
+++ b/tests/rustdoc/private-fields-tuple-struct.rs
@@ -0,0 +1,15 @@
+// This test checks the diplay of "/* private fields */" sentence in tuple structs.
+#![crate_name = "foo"]
+
+// @has 'foo/struct.A.html' '//*[@class="rust item-decl"]/code' 'pub struct A(pub u8, _);'
+pub struct A(pub u8, u8);
+// @has 'foo/struct.B.html' '//*[@class="rust item-decl"]/code' 'pub struct B(_, pub u8);'
+pub struct B(u8, pub u8);
+// @has 'foo/struct.C.html' '//*[@class="rust item-decl"]/code' 'pub struct C(_, pub u8, _);'
+pub struct C(u8, pub u8, u8);
+// @has 'foo/struct.D.html' '//*[@class="rust item-decl"]/code' 'pub struct D(pub u8, _, pub u8);'
+pub struct D(pub u8, u8, pub u8);
+// @has 'foo/struct.E.html' '//*[@class="rust item-decl"]/code' 'pub struct E(/* private fields */);'
+pub struct E(u8);
+// @has 'foo/struct.F.html' '//*[@class="rust item-decl"]/code' 'pub struct F(/* private fields */);'
+pub struct F(u8, u8);
diff --git a/tests/rustdoc/typedef-inner-variants-lazy_type_alias.rs b/tests/rustdoc/typedef-inner-variants-lazy_type_alias.rs
new file mode 100644
index 00000000000..ff84352d716
--- /dev/null
+++ b/tests/rustdoc/typedef-inner-variants-lazy_type_alias.rs
@@ -0,0 +1,34 @@
+#![crate_name = "inner_types_lazy"]
+
+#![feature(lazy_type_alias)]
+#![allow(incomplete_features)]
+
+// @has 'inner_types_lazy/struct.Pair.html'
+pub struct Pair<A, B> {
+    pub first: A,
+    pub second: B,
+}
+
+// @has 'inner_types_lazy/type.ReversedTypesPair.html'
+// @count - '//*[@id="aliased-type"]' 1
+// @count - '//*[@id="variants"]' 0
+// @count - '//*[@id="fields"]' 1
+// @count - '//span[@class="where fmt-newline"]' 0
+pub type ReversedTypesPair<Q, R> = Pair<R, Q>;
+
+// @has 'inner_types_lazy/type.ReadWrite.html'
+// @count - '//*[@id="aliased-type"]' 1
+// @count - '//*[@id="variants"]' 0
+// @count - '//*[@id="fields"]' 1
+// @count - '//span[@class="where fmt-newline"]' 2
+pub type ReadWrite<R, W> = Pair<R, W>
+where
+    R: std::io::Read,
+    W: std::io::Write;
+
+// @has 'inner_types_lazy/type.VecPair.html'
+// @count - '//*[@id="aliased-type"]' 1
+// @count - '//*[@id="variants"]' 0
+// @count - '//*[@id="fields"]' 1
+// @count - '//span[@class="where fmt-newline"]' 0
+pub type VecPair<U, V> = Pair<Vec<U>, Vec<V>>;
diff --git a/tests/rustdoc/typedef-inner-variants.rs b/tests/rustdoc/typedef-inner-variants.rs
new file mode 100644
index 00000000000..b734714fd64
--- /dev/null
+++ b/tests/rustdoc/typedef-inner-variants.rs
@@ -0,0 +1,119 @@
+// This test checks different combinations of structs, enums, and unions
+// for the "Show Aliased Type" feature on type definition.
+
+#![crate_name = "inner_variants"]
+
+// aux-build:cross_crate_generic_typedef.rs
+extern crate cross_crate_generic_typedef;
+
+pub struct Adt;
+pub struct Ty;
+pub struct TyCtxt;
+
+pub trait Interner {
+    type Adt;
+    type Ty;
+}
+
+impl Interner for TyCtxt {
+    type Adt = Adt;
+    type Ty = Ty;
+}
+
+// @has 'inner_variants/type.AliasTy.html'
+// @count - '//*[@id="variants"]' 0
+// @count - '//*[@id="fields"]' 0
+pub type AliasTy = Ty;
+
+// @has 'inner_variants/enum.IrTyKind.html'
+pub enum IrTyKind<A, I: Interner> {
+    /// Doc comment for AdtKind
+    AdtKind(I::Adt),
+    /// and another one for TyKind
+    TyKind(I::Adt, <I as Interner>::Ty),
+    // no comment
+    StructKind { a: A, },
+    #[doc(hidden)]
+    Unspecified,
+}
+
+// @has 'inner_variants/type.NearlyTyKind.html'
+// @count - '//*[@id="aliased-type"]' 1
+// @count - '//*[@id="variants"]' 1
+// @count - '//*[@id="fields"]' 0
+pub type NearlyTyKind<A> = IrTyKind<A, TyCtxt>;
+
+// @has 'inner_variants/type.TyKind.html'
+// @count - '//*[@id="aliased-type"]' 1
+// @count - '//*[@id="variants"]' 1
+// @count - '//*[@id="fields"]' 0
+// @count - '//*[@class="variant"]' 3
+// @matches - '//pre[@class="rust item-decl"]//code' "enum TyKind"
+// @has - '//pre[@class="rust item-decl"]//code/a[1]' "Adt"
+// @has - '//pre[@class="rust item-decl"]//code/a[2]' "Adt"
+// @has - '//pre[@class="rust item-decl"]//code/a[3]' "Ty"
+// @has - '//pre[@class="rust item-decl"]//code/a[4]' "i64"
+pub type TyKind = IrTyKind<i64, TyCtxt>;
+
+// @has 'inner_variants/union.OneOr.html'
+pub union OneOr<A: Copy> {
+    pub one: i64,
+    pub or: A,
+}
+
+// @has 'inner_variants/type.OneOrF64.html'
+// @count - '//*[@id="aliased-type"]' 1
+// @count - '//*[@id="variants"]' 0
+// @count - '//*[@id="fields"]' 1
+// @count - '//*[@class="structfield small-section-header"]' 2
+// @matches - '//pre[@class="rust item-decl"]//code' "union OneOrF64"
+pub type OneOrF64 = OneOr<f64>;
+
+// @has 'inner_variants/struct.One.html'
+pub struct One<T> {
+    pub val: T,
+    #[doc(hidden)]
+    pub __hidden: T,
+    __private: T,
+}
+
+// @has 'inner_variants/type.OneU64.html'
+// @count - '//*[@id="aliased-type"]' 1
+// @count - '//*[@id="variants"]' 0
+// @count - '//*[@id="fields"]' 1
+// @count - '//*[@class="structfield small-section-header"]' 1
+// @matches - '//pre[@class="rust item-decl"]//code' "struct OneU64"
+// @matches - '//pre[@class="rust item-decl"]//code' "pub val"
+pub type OneU64 = One<u64>;
+
+// @has 'inner_variants/struct.OnceA.html'
+pub struct OnceA<'a, A> {
+    pub a: &'a A,
+}
+
+// @has 'inner_variants/type.Once.html'
+// @count - '//*[@id="aliased-type"]' 1
+// @count - '//*[@id="variants"]' 0
+// @count - '//*[@id="fields"]' 1
+// @matches - '//pre[@class="rust item-decl"]//code' "struct Once<'a>"
+// @matches - '//pre[@class="rust item-decl"]//code' "&'a"
+pub type Once<'a> = OnceA<'a, i64>;
+
+// @has 'inner_variants/struct.HighlyGenericStruct.html'
+pub struct HighlyGenericStruct<A, B, C, D> {
+    pub z: (A, B, C, D)
+}
+
+// @has 'inner_variants/type.HighlyGenericAABB.html'
+// @count - '//*[@id="aliased-type"]' 1
+// @count - '//*[@id="variants"]' 0
+// @count - '//*[@id="fields"]' 1
+// @matches - '//pre[@class="rust item-decl"]//code' "struct HighlyGenericAABB<A, B>"
+// @matches - '//pre[@class="rust item-decl"]//code' "pub z"
+pub type HighlyGenericAABB<A, B> = HighlyGenericStruct<A, A, B, B>;
+
+// @has 'inner_variants/type.InlineU64.html'
+// @count - '//*[@id="aliased-type"]' 1
+// @count - '//*[@id="variants"]' 0
+// @count - '//*[@id="fields"]' 1
+pub use cross_crate_generic_typedef::InlineU64;
diff --git a/tests/rustdoc/where.SWhere_Simd_item-decl.html b/tests/rustdoc/where.SWhere_Simd_item-decl.html
index 3e72ba2b74f..46708b9e4e9 100644
--- a/tests/rustdoc/where.SWhere_Simd_item-decl.html
+++ b/tests/rustdoc/where.SWhere_Simd_item-decl.html
@@ -1,3 +1,3 @@
-<pre class="rust item-decl"><code>pub struct Simd&lt;T&gt;(_)
+<pre class="rust item-decl"><code>pub struct Simd&lt;T&gt;(/* private fields */)
 <span class="where">where
     T: <a class="trait" href="trait.MyTrait.html" title="trait foo::MyTrait">MyTrait</a></span>;</code></pre>
diff --git a/tests/rustdoc/where.alpha_trait_decl.html b/tests/rustdoc/where.alpha_trait_decl.html
index a7700055c9a..0c0b2d1ceca 100644
--- a/tests/rustdoc/where.alpha_trait_decl.html
+++ b/tests/rustdoc/where.alpha_trait_decl.html
@@ -1,3 +1,3 @@
-<code>pub struct Alpha&lt;A&gt;(_)
+<code>pub struct Alpha&lt;A&gt;(/* private fields */)
 <span class="where">where
     A: <a class="trait" href="trait.MyTrait.html" title="trait foo::MyTrait">MyTrait</a></span>;</code>
\ No newline at end of file
diff --git a/tests/rustdoc/where.rs b/tests/rustdoc/where.rs
index 2aa9c8b5461..aea02c14039 100644
--- a/tests/rustdoc/where.rs
+++ b/tests/rustdoc/where.rs
@@ -4,7 +4,7 @@ use std::io::Lines;
 
 pub trait MyTrait { fn dummy(&self) { } }
 
-// @has foo/struct.Alpha.html '//pre' "pub struct Alpha<A>(_) where A: MyTrait"
+// @has foo/struct.Alpha.html '//pre' "pub struct Alpha<A>(/* private fields */) where A: MyTrait"
 // @snapshot alpha_trait_decl - '//*[@class="rust item-decl"]/code'
 pub struct Alpha<A>(A) where A: MyTrait;
 // @has foo/trait.Bravo.html '//pre' "pub trait Bravo<B>where B: MyTrait"
diff --git a/tests/ui-fulldeps/plugin/lint-tool-cmdline-allow.stderr b/tests/ui-fulldeps/plugin/lint-tool-cmdline-allow.stderr
index b060e3a3e38..0e661795999 100644
--- a/tests/ui-fulldeps/plugin/lint-tool-cmdline-allow.stderr
+++ b/tests/ui-fulldeps/plugin/lint-tool-cmdline-allow.stderr
@@ -1,9 +1,12 @@
-warning: lint name `test_lint` is deprecated and does not have an effect anymore. Use: clippy::test_lint
+warning: lint name `test_lint` is deprecated and may not have an effect in the future.
    |
+   = help: change it to clippy::test_lint
    = note: requested on the command line with `-A test_lint`
+   = note: `#[warn(renamed_and_removed_lints)]` on by default
 
-warning: lint name `test_lint` is deprecated and does not have an effect anymore. Use: clippy::test_lint
+warning: lint name `test_lint` is deprecated and may not have an effect in the future.
    |
+   = help: change it to clippy::test_lint
    = note: requested on the command line with `-A test_lint`
 
 warning: item is named 'lintme'
@@ -22,8 +25,9 @@ LL | #![plugin(lint_tool_test)]
    |
    = note: `#[warn(deprecated)]` on by default
 
-warning: lint name `test_lint` is deprecated and does not have an effect anymore. Use: clippy::test_lint
+warning: lint name `test_lint` is deprecated and may not have an effect in the future.
    |
+   = help: change it to clippy::test_lint
    = note: requested on the command line with `-A test_lint`
 
 warning: 5 warnings emitted
diff --git a/tests/ui-fulldeps/pprust-expr-roundtrip.rs b/tests/ui-fulldeps/pprust-expr-roundtrip.rs
index ae375dfab90..541be7ebbc0 100644
--- a/tests/ui-fulldeps/pprust-expr-roundtrip.rs
+++ b/tests/ui-fulldeps/pprust-expr-roundtrip.rs
@@ -80,14 +80,20 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) {
                 let seg = PathSegment::from_ident(Ident::from_str("x"));
                 iter_exprs(depth - 1, &mut |e| {
                     g(ExprKind::MethodCall(Box::new(MethodCall {
-                        seg: seg.clone(), receiver: e, args: thin_vec![make_x()], span: DUMMY_SP
-                    }))
-                )});
+                        seg: seg.clone(),
+                        receiver: e,
+                        args: thin_vec![make_x()],
+                        span: DUMMY_SP,
+                    })))
+                });
                 iter_exprs(depth - 1, &mut |e| {
                     g(ExprKind::MethodCall(Box::new(MethodCall {
-                        seg: seg.clone(), receiver: make_x(), args: thin_vec![e], span: DUMMY_SP
-                    }))
-                )});
+                        seg: seg.clone(),
+                        receiver: make_x(),
+                        args: thin_vec![e],
+                        span: DUMMY_SP,
+                    })))
+                });
             }
             2..=7 => {
                 let op = Spanned {
@@ -174,7 +180,7 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) {
             18 => {
                 let pat =
                     P(Pat { id: DUMMY_NODE_ID, kind: PatKind::Wild, span: DUMMY_SP, tokens: None });
-                iter_exprs(depth - 1, &mut |e| g(ExprKind::Let(pat.clone(), e, DUMMY_SP)))
+                iter_exprs(depth - 1, &mut |e| g(ExprKind::Let(pat.clone(), e, DUMMY_SP, None)))
             }
             _ => panic!("bad counter value in iter_exprs"),
         }
diff --git a/tests/ui-fulldeps/stable-mir/crate-info.rs b/tests/ui-fulldeps/stable-mir/crate-info.rs
index d55eae86f07..a11720c4b55 100644
--- a/tests/ui-fulldeps/stable-mir/crate-info.rs
+++ b/tests/ui-fulldeps/stable-mir/crate-info.rs
@@ -154,6 +154,10 @@ fn test_stable_mir(tcx: TyCtxt<'_>) -> ControlFlow<()> {
         }
     }
 
+    let foo_const = get_item(tcx, &items, (DefKind::Const, "FOO")).unwrap();
+    // Ensure we don't panic trying to get the body of a constant.
+    foo_const.body();
+
     ControlFlow::Continue(())
 }
 
@@ -191,6 +195,8 @@ fn generate_input(path: &str) -> std::io::Result<()> {
     write!(
         file,
         r#"
+    pub const FOO: u32 = 1 + 2;
+
     fn generic<T, const U: usize>(t: T) -> [(); U] {{
         _ = t;
         [(); U]
diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs
new file mode 100644
index 00000000000..b3e75bb8233
--- /dev/null
+++ b/tests/ui/abi/compatibility.rs
@@ -0,0 +1,194 @@
+// check-pass
+#![feature(rustc_attrs, unsized_fn_params, transparent_unions)]
+#![allow(unused, improper_ctypes_definitions, internal_features)]
+use std::marker::PhantomData;
+use std::mem::ManuallyDrop;
+use std::num::NonZeroI32;
+use std::ptr::NonNull;
+
+// FIXME: a bunch of targets are broken in various ways.
+// Hence there are `cfg` throughout this test to disable parts of it on those targets.
+// sparc64: https://github.com/rust-lang/rust/issues/115336
+// mips64: https://github.com/rust-lang/rust/issues/115404
+// riscv64: https://github.com/rust-lang/rust/issues/115481
+// loongarch64: https://github.com/rust-lang/rust/issues/115509
+
+macro_rules! assert_abi_compatible {
+    ($name:ident, $t1:ty, $t2:ty) => {
+        mod $name {
+            use super::*;
+            // Declaring a `type` doesn't even check well-formedness, so we also declare a function.
+            fn check_wf(_x: $t1, _y: $t2) {}
+            // Test argument and return value, `Rust` and `C` ABIs.
+            #[rustc_abi(assert_eq)]
+            type TestRust = (fn($t1) -> $t1, fn($t2) -> $t2);
+            #[rustc_abi(assert_eq)]
+            type TestC = (extern "C" fn($t1) -> $t1, extern "C" fn($t2) -> $t2);
+        }
+    };
+}
+
+#[derive(Copy, Clone)]
+struct Zst;
+
+#[repr(C)]
+struct ReprC1<T: ?Sized>(T);
+#[repr(C)]
+struct ReprC2Int<T>(i32, T);
+#[repr(C)]
+struct ReprC2Float<T>(f32, T);
+#[repr(C)]
+struct ReprC4<T>(T, Vec<i32>, Zst, T);
+#[repr(C)]
+struct ReprC4Mixed<T>(T, f32, i32, T);
+#[repr(C)]
+enum ReprCEnum<T> {
+    Variant1,
+    Variant2(T),
+}
+#[repr(C)]
+union ReprCUnion<T> {
+    nothing: (),
+    something: ManuallyDrop<T>,
+}
+
+macro_rules! test_abi_compatible {
+    ($name:ident, $t1:ty, $t2:ty) => {
+        mod $name {
+            use super::*;
+            assert_abi_compatible!(plain, $t1, $t2);
+            // We also do some tests with differences in fields of `repr(C)` types.
+            assert_abi_compatible!(repr_c_1, ReprC1<$t1>, ReprC1<$t2>);
+            assert_abi_compatible!(repr_c_2_int, ReprC2Int<$t1>, ReprC2Int<$t2>);
+            assert_abi_compatible!(repr_c_2_float, ReprC2Float<$t1>, ReprC2Float<$t2>);
+            assert_abi_compatible!(repr_c_4, ReprC4<$t1>, ReprC4<$t2>);
+            assert_abi_compatible!(repr_c_4mixed, ReprC4Mixed<$t1>, ReprC4Mixed<$t2>);
+            assert_abi_compatible!(repr_c_enum, ReprCEnum<$t1>, ReprCEnum<$t2>);
+            assert_abi_compatible!(repr_c_union, ReprCUnion<$t1>, ReprCUnion<$t2>);
+        }
+    };
+}
+
+// Compatibility of pointers is probably de-facto guaranteed,
+// but that does not seem to be documented.
+test_abi_compatible!(ptr_mut, *const i32, *mut i32);
+test_abi_compatible!(ptr_pointee, *const i32, *const Vec<i32>);
+test_abi_compatible!(ref_mut, &i32, &mut i32);
+test_abi_compatible!(ref_ptr, &i32, *const i32);
+test_abi_compatible!(box_ptr, Box<i32>, *const i32);
+test_abi_compatible!(nonnull_ptr, NonNull<i32>, *const i32);
+test_abi_compatible!(fn_fn, fn(), fn(i32) -> i32);
+
+// Some further guarantees we will likely (have to) make.
+test_abi_compatible!(zst_unit, Zst, ());
+#[cfg(not(any(target_arch = "sparc64")))]
+test_abi_compatible!(zst_array, Zst, [u8; 0]);
+test_abi_compatible!(nonzero_int, NonZeroI32, i32);
+
+// `DispatchFromDyn` relies on ABI compatibility.
+// This is interesting since these types are not `repr(transparent)`.
+test_abi_compatible!(rc, std::rc::Rc<i32>, *mut i32);
+test_abi_compatible!(arc, std::sync::Arc<i32>, *mut i32);
+
+// `repr(transparent)` compatibility.
+#[repr(transparent)]
+struct Wrapper1<T: ?Sized>(T);
+#[repr(transparent)]
+struct Wrapper2<T: ?Sized>((), Zst, T);
+#[repr(transparent)]
+struct Wrapper3<T>(T, [u8; 0], PhantomData<u64>);
+#[repr(transparent)]
+union WrapperUnion<T> {
+    nothing: (),
+    something: ManuallyDrop<T>,
+}
+
+macro_rules! test_transparent {
+    ($name:ident, $t:ty) => {
+        mod $name {
+            use super::*;
+            test_abi_compatible!(wrap1, $t, Wrapper1<$t>);
+            test_abi_compatible!(wrap2, $t, Wrapper2<$t>);
+            test_abi_compatible!(wrap3, $t, Wrapper3<$t>);
+            #[cfg(not(any(target_arch = "riscv64", target_arch = "loongarch64")))]
+            test_abi_compatible!(wrap4, $t, WrapperUnion<$t>);
+        }
+    };
+}
+
+test_transparent!(simple, i32);
+test_transparent!(reference, &'static i32);
+test_transparent!(zst, Zst);
+test_transparent!(unit, ());
+test_transparent!(enum_, Option<i32>);
+test_transparent!(enum_niched, Option<&'static i32>);
+#[cfg(not(any(target_arch = "mips64", target_arch = "sparc64")))]
+mod tuples {
+    use super::*;
+    // mixing in some floats since they often get special treatment
+    test_transparent!(pair, (i32, f32));
+    // chosen to fit into 64bit
+    test_transparent!(triple, (i8, i16, f32));
+    // Pure-float types that are not ScalarPair seem to be tricky.
+    test_transparent!(triple_f32, (f32, f32, f32));
+    test_transparent!(triple_f64, (f64, f64, f64));
+    // and also something that's larger than 2 pointers
+    test_transparent!(tuple, (i32, f32, i64, f64));
+}
+// Some targets have special rules for arrays.
+#[cfg(not(any(target_arch = "mips64", target_arch = "sparc64")))]
+mod arrays {
+    use super::*;
+    test_transparent!(empty_array, [u32; 0]);
+    test_transparent!(empty_1zst_array, [u8; 0]);
+    test_transparent!(small_array, [i32; 2]); // chosen to fit into 64bit
+    test_transparent!(large_array, [i32; 16]);
+}
+
+// Some tests with unsized types (not all wrappers are compatible with that).
+macro_rules! test_transparent_unsized {
+    ($name:ident, $t:ty) => {
+        mod $name {
+            use super::*;
+            assert_abi_compatible!(wrap1, $t, Wrapper1<$t>);
+            assert_abi_compatible!(wrap1_reprc, ReprC1<$t>, ReprC1<Wrapper1<$t>>);
+            assert_abi_compatible!(wrap2, $t, Wrapper2<$t>);
+            assert_abi_compatible!(wrap2_reprc, ReprC1<$t>, ReprC1<Wrapper2<$t>>);
+        }
+    };
+}
+
+#[cfg(not(any(target_arch = "mips64", target_arch = "sparc64")))]
+mod unsized_ {
+    use super::*;
+    test_transparent_unsized!(str_, str);
+    test_transparent_unsized!(slice, [u8]);
+    test_transparent_unsized!(dyn_trait, dyn std::any::Any);
+}
+
+// RFC 3391 <https://rust-lang.github.io/rfcs/3391-result_ffi_guarantees.html>.
+macro_rules! test_nonnull {
+    ($name:ident, $t:ty) => {
+        mod $name {
+            use super::*;
+            test_abi_compatible!(option, Option<$t>, $t);
+            test_abi_compatible!(result_err_unit, Result<$t, ()>, $t);
+            test_abi_compatible!(result_ok_unit, Result<(), $t>, $t);
+            test_abi_compatible!(result_err_zst, Result<$t, Zst>, $t);
+            test_abi_compatible!(result_ok_zst, Result<Zst, $t>, $t);
+            test_abi_compatible!(result_err_arr, Result<$t, [i8; 0]>, $t);
+            test_abi_compatible!(result_ok_arr, Result<[i8; 0], $t>, $t);
+        }
+    }
+}
+
+test_nonnull!(ref_, &i32);
+test_nonnull!(mut_, &mut i32);
+test_nonnull!(ref_unsized, &[i32]);
+test_nonnull!(mut_unsized, &mut [i32]);
+test_nonnull!(fn_, fn());
+test_nonnull!(nonnull, NonNull<i32>);
+test_nonnull!(nonnull_unsized, NonNull<dyn std::fmt::Debug>);
+test_nonnull!(non_zero, NonZeroI32);
+
+fn main() {}
diff --git a/tests/ui/abi/debug.rs b/tests/ui/abi/debug.rs
index 13464be275e..77715ee4023 100644
--- a/tests/ui/abi/debug.rs
+++ b/tests/ui/abi/debug.rs
@@ -9,15 +9,45 @@
 #![feature(rustc_attrs)]
 #![crate_type = "lib"]
 
+struct S(u16);
+
 #[rustc_abi(debug)]
 fn test(_x: u8) -> bool { true } //~ ERROR: fn_abi
 
+#[rustc_abi(debug)]
+type TestFnPtr = fn(bool) -> u8; //~ ERROR: fn_abi
 
 #[rustc_abi(debug)]
 fn test_generic<T>(_x: *const T) { } //~ ERROR: fn_abi
 
-struct S(u16);
+#[rustc_abi(debug)]
+const C: () = (); //~ ERROR: can only be applied to
+
+impl S {
+    #[rustc_abi(debug)]
+    const C: () = (); //~ ERROR: can only be applied to
+}
+
 impl S {
     #[rustc_abi(debug)]
     fn assoc_test(&self) { } //~ ERROR: fn_abi
 }
+
+#[rustc_abi(assert_eq)]
+type TestAbiEq = (fn(bool), fn(bool));
+
+#[rustc_abi(assert_eq)]
+type TestAbiNe = (fn(u8), fn(u32)); //~ ERROR: ABIs are not compatible
+
+#[rustc_abi(assert_eq)]
+type TestAbiNeLarger = (fn([u8; 32]), fn([u32; 32])); //~ ERROR: ABIs are not compatible
+
+#[rustc_abi(assert_eq)]
+type TestAbiNeFloat = (fn(f32), fn(u32)); //~ ERROR: ABIs are not compatible
+
+// Sign matters on some targets (such as s390x), so let's make sure we never accept this.
+#[rustc_abi(assert_eq)]
+type TestAbiNeSign = (fn(i32), fn(u32)); //~ ERROR: ABIs are not compatible
+
+#[rustc_abi(assert_eq)]
+type TestAbiEqNonsense = (fn((str, str)), fn((str, str))); //~ ERROR: cannot be known at compilation time
diff --git a/tests/ui/abi/debug.stderr b/tests/ui/abi/debug.stderr
index 4f4ee3de4b8..ceaf5136a6f 100644
--- a/tests/ui/abi/debug.stderr
+++ b/tests/ui/abi/debug.stderr
@@ -1,4 +1,4 @@
-error: fn_abi_of_instance(test) = FnAbi {
+error: fn_abi_of(test) = FnAbi {
            args: [
                ArgAbi {
                    layout: TyAndLayout {
@@ -87,12 +87,106 @@ error: fn_abi_of_instance(test) = FnAbi {
            conv: Rust,
            can_unwind: $SOME_BOOL,
        }
-  --> $DIR/debug.rs:13:1
+  --> $DIR/debug.rs:15:1
    |
 LL | fn test(_x: u8) -> bool { true }
    | ^^^^^^^^^^^^^^^^^^^^^^^
 
-error: fn_abi_of_instance(test_generic) = FnAbi {
+error: fn_abi_of(TestFnPtr) = FnAbi {
+           args: [
+               ArgAbi {
+                   layout: TyAndLayout {
+                       ty: bool,
+                       layout: Layout {
+                           size: Size(1 bytes),
+                           align: AbiAndPrefAlign {
+                               abi: $SOME_ALIGN,
+                               pref: $SOME_ALIGN,
+                           },
+                           abi: Scalar(
+                               Initialized {
+                                   value: Int(
+                                       I8,
+                                       false,
+                                   ),
+                                   valid_range: 0..=1,
+                               },
+                           ),
+                           fields: Primitive,
+                           largest_niche: Some(
+                               Niche {
+                                   offset: Size(0 bytes),
+                                   value: Int(
+                                       I8,
+                                       false,
+                                   ),
+                                   valid_range: 0..=1,
+                               },
+                           ),
+                           variants: Single {
+                               index: 0,
+                           },
+                           max_repr_align: None,
+                           unadjusted_abi_align: $SOME_ALIGN,
+                       },
+                   },
+                   mode: Direct(
+                       ArgAttributes {
+                           regular: NoUndef,
+                           arg_ext: Zext,
+                           pointee_size: Size(0 bytes),
+                           pointee_align: None,
+                       },
+                   ),
+               },
+           ],
+           ret: ArgAbi {
+               layout: TyAndLayout {
+                   ty: u8,
+                   layout: Layout {
+                       size: Size(1 bytes),
+                       align: AbiAndPrefAlign {
+                           abi: $SOME_ALIGN,
+                           pref: $SOME_ALIGN,
+                       },
+                       abi: Scalar(
+                           Initialized {
+                               value: Int(
+                                   I8,
+                                   false,
+                               ),
+                               valid_range: 0..=255,
+                           },
+                       ),
+                       fields: Primitive,
+                       largest_niche: None,
+                       variants: Single {
+                           index: 0,
+                       },
+                       max_repr_align: None,
+                       unadjusted_abi_align: $SOME_ALIGN,
+                   },
+               },
+               mode: Direct(
+                   ArgAttributes {
+                       regular: NoUndef,
+                       arg_ext: None,
+                       pointee_size: Size(0 bytes),
+                       pointee_align: None,
+                   },
+               ),
+           },
+           c_variadic: false,
+           fixed_count: 1,
+           conv: Rust,
+           can_unwind: $SOME_BOOL,
+       }
+  --> $DIR/debug.rs:18:1
+   |
+LL | type TestFnPtr = fn(bool) -> u8;
+   | ^^^^^^^^^^^^^^
+
+error: fn_abi_of(test_generic) = FnAbi {
            args: [
                ArgAbi {
                    layout: TyAndLayout {
@@ -163,12 +257,616 @@ error: fn_abi_of_instance(test_generic) = FnAbi {
            conv: Rust,
            can_unwind: $SOME_BOOL,
        }
-  --> $DIR/debug.rs:17:1
+  --> $DIR/debug.rs:21:1
    |
 LL | fn test_generic<T>(_x: *const T) { }
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: fn_abi_of_instance(assoc_test) = FnAbi {
+error: `#[rustc_abi]` can only be applied to function items, type aliases, and associated functions
+  --> $DIR/debug.rs:24:1
+   |
+LL | const C: () = ();
+   | ^^^^^^^^^^^
+
+error: ABIs are not compatible
+       left ABI = FnAbi {
+           args: [
+               ArgAbi {
+                   layout: TyAndLayout {
+                       ty: u8,
+                       layout: Layout {
+                           size: Size(1 bytes),
+                           align: AbiAndPrefAlign {
+                               abi: $SOME_ALIGN,
+                               pref: $SOME_ALIGN,
+                           },
+                           abi: Scalar(
+                               Initialized {
+                                   value: Int(
+                                       I8,
+                                       false,
+                                   ),
+                                   valid_range: 0..=255,
+                               },
+                           ),
+                           fields: Primitive,
+                           largest_niche: None,
+                           variants: Single {
+                               index: 0,
+                           },
+                           max_repr_align: None,
+                           unadjusted_abi_align: $SOME_ALIGN,
+                       },
+                   },
+                   mode: Direct(
+                       ArgAttributes {
+                           regular: NoUndef,
+                           arg_ext: None,
+                           pointee_size: Size(0 bytes),
+                           pointee_align: None,
+                       },
+                   ),
+               },
+           ],
+           ret: ArgAbi {
+               layout: TyAndLayout {
+                   ty: (),
+                   layout: Layout {
+                       size: Size(0 bytes),
+                       align: AbiAndPrefAlign {
+                           abi: $SOME_ALIGN,
+                           pref: $SOME_ALIGN,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       fields: Arbitrary {
+                           offsets: [],
+                           memory_index: [],
+                       },
+                       largest_niche: None,
+                       variants: Single {
+                           index: 0,
+                       },
+                       max_repr_align: None,
+                       unadjusted_abi_align: $SOME_ALIGN,
+                   },
+               },
+               mode: Ignore,
+           },
+           c_variadic: false,
+           fixed_count: 1,
+           conv: Rust,
+           can_unwind: $SOME_BOOL,
+       }
+       right ABI = FnAbi {
+           args: [
+               ArgAbi {
+                   layout: TyAndLayout {
+                       ty: u32,
+                       layout: Layout {
+                           size: $SOME_SIZE,
+                           align: AbiAndPrefAlign {
+                               abi: $SOME_ALIGN,
+                               pref: $SOME_ALIGN,
+                           },
+                           abi: Scalar(
+                               Initialized {
+                                   value: Int(
+                                       I32,
+                                       false,
+                                   ),
+                                   valid_range: $FULL,
+                               },
+                           ),
+                           fields: Primitive,
+                           largest_niche: None,
+                           variants: Single {
+                               index: 0,
+                           },
+                           max_repr_align: None,
+                           unadjusted_abi_align: $SOME_ALIGN,
+                       },
+                   },
+                   mode: Direct(
+                       ArgAttributes {
+                           regular: NoUndef,
+                           arg_ext: None,
+                           pointee_size: Size(0 bytes),
+                           pointee_align: None,
+                       },
+                   ),
+               },
+           ],
+           ret: ArgAbi {
+               layout: TyAndLayout {
+                   ty: (),
+                   layout: Layout {
+                       size: Size(0 bytes),
+                       align: AbiAndPrefAlign {
+                           abi: $SOME_ALIGN,
+                           pref: $SOME_ALIGN,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       fields: Arbitrary {
+                           offsets: [],
+                           memory_index: [],
+                       },
+                       largest_niche: None,
+                       variants: Single {
+                           index: 0,
+                       },
+                       max_repr_align: None,
+                       unadjusted_abi_align: $SOME_ALIGN,
+                   },
+               },
+               mode: Ignore,
+           },
+           c_variadic: false,
+           fixed_count: 1,
+           conv: Rust,
+           can_unwind: $SOME_BOOL,
+       }
+  --> $DIR/debug.rs:40:1
+   |
+LL | type TestAbiNe = (fn(u8), fn(u32));
+   | ^^^^^^^^^^^^^^
+
+error: ABIs are not compatible
+       left ABI = FnAbi {
+           args: [
+               ArgAbi {
+                   layout: TyAndLayout {
+                       ty: [u8; 32],
+                       layout: Layout {
+                           size: Size(32 bytes),
+                           align: AbiAndPrefAlign {
+                               abi: $SOME_ALIGN,
+                               pref: $SOME_ALIGN,
+                           },
+                           abi: Aggregate {
+                               sized: true,
+                           },
+                           fields: Array {
+                               stride: Size(1 bytes),
+                               count: 32,
+                           },
+                           largest_niche: None,
+                           variants: Single {
+                               index: 0,
+                           },
+                           max_repr_align: None,
+                           unadjusted_abi_align: $SOME_ALIGN,
+                       },
+                   },
+                   mode: Indirect {
+                       attrs: ArgAttributes {
+                           regular: NoAlias | NoCapture | NonNull | NoUndef,
+                           arg_ext: None,
+                           pointee_size: Size(32 bytes),
+                           pointee_align: Some(
+                               Align(1 bytes),
+                           ),
+                       },
+                       extra_attrs: None,
+                       on_stack: false,
+                   },
+               },
+           ],
+           ret: ArgAbi {
+               layout: TyAndLayout {
+                   ty: (),
+                   layout: Layout {
+                       size: Size(0 bytes),
+                       align: AbiAndPrefAlign {
+                           abi: $SOME_ALIGN,
+                           pref: $SOME_ALIGN,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       fields: Arbitrary {
+                           offsets: [],
+                           memory_index: [],
+                       },
+                       largest_niche: None,
+                       variants: Single {
+                           index: 0,
+                       },
+                       max_repr_align: None,
+                       unadjusted_abi_align: $SOME_ALIGN,
+                   },
+               },
+               mode: Ignore,
+           },
+           c_variadic: false,
+           fixed_count: 1,
+           conv: Rust,
+           can_unwind: $SOME_BOOL,
+       }
+       right ABI = FnAbi {
+           args: [
+               ArgAbi {
+                   layout: TyAndLayout {
+                       ty: [u32; 32],
+                       layout: Layout {
+                           size: Size(128 bytes),
+                           align: AbiAndPrefAlign {
+                               abi: $SOME_ALIGN,
+                               pref: $SOME_ALIGN,
+                           },
+                           abi: Aggregate {
+                               sized: true,
+                           },
+                           fields: Array {
+                               stride: Size(4 bytes),
+                               count: 32,
+                           },
+                           largest_niche: None,
+                           variants: Single {
+                               index: 0,
+                           },
+                           max_repr_align: None,
+                           unadjusted_abi_align: $SOME_ALIGN,
+                       },
+                   },
+                   mode: Indirect {
+                       attrs: ArgAttributes {
+                           regular: NoAlias | NoCapture | NonNull | NoUndef,
+                           arg_ext: None,
+                           pointee_size: Size(128 bytes),
+                           pointee_align: Some(
+                               Align(4 bytes),
+                           ),
+                       },
+                       extra_attrs: None,
+                       on_stack: false,
+                   },
+               },
+           ],
+           ret: ArgAbi {
+               layout: TyAndLayout {
+                   ty: (),
+                   layout: Layout {
+                       size: Size(0 bytes),
+                       align: AbiAndPrefAlign {
+                           abi: $SOME_ALIGN,
+                           pref: $SOME_ALIGN,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       fields: Arbitrary {
+                           offsets: [],
+                           memory_index: [],
+                       },
+                       largest_niche: None,
+                       variants: Single {
+                           index: 0,
+                       },
+                       max_repr_align: None,
+                       unadjusted_abi_align: $SOME_ALIGN,
+                   },
+               },
+               mode: Ignore,
+           },
+           c_variadic: false,
+           fixed_count: 1,
+           conv: Rust,
+           can_unwind: $SOME_BOOL,
+       }
+  --> $DIR/debug.rs:43:1
+   |
+LL | type TestAbiNeLarger = (fn([u8; 32]), fn([u32; 32]));
+   | ^^^^^^^^^^^^^^^^^^^^
+
+error: ABIs are not compatible
+       left ABI = FnAbi {
+           args: [
+               ArgAbi {
+                   layout: TyAndLayout {
+                       ty: f32,
+                       layout: Layout {
+                           size: $SOME_SIZE,
+                           align: AbiAndPrefAlign {
+                               abi: $SOME_ALIGN,
+                               pref: $SOME_ALIGN,
+                           },
+                           abi: Scalar(
+                               Initialized {
+                                   value: F32,
+                                   valid_range: $FULL,
+                               },
+                           ),
+                           fields: Primitive,
+                           largest_niche: None,
+                           variants: Single {
+                               index: 0,
+                           },
+                           max_repr_align: None,
+                           unadjusted_abi_align: $SOME_ALIGN,
+                       },
+                   },
+                   mode: Direct(
+                       ArgAttributes {
+                           regular: NoUndef,
+                           arg_ext: None,
+                           pointee_size: Size(0 bytes),
+                           pointee_align: None,
+                       },
+                   ),
+               },
+           ],
+           ret: ArgAbi {
+               layout: TyAndLayout {
+                   ty: (),
+                   layout: Layout {
+                       size: Size(0 bytes),
+                       align: AbiAndPrefAlign {
+                           abi: $SOME_ALIGN,
+                           pref: $SOME_ALIGN,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       fields: Arbitrary {
+                           offsets: [],
+                           memory_index: [],
+                       },
+                       largest_niche: None,
+                       variants: Single {
+                           index: 0,
+                       },
+                       max_repr_align: None,
+                       unadjusted_abi_align: $SOME_ALIGN,
+                   },
+               },
+               mode: Ignore,
+           },
+           c_variadic: false,
+           fixed_count: 1,
+           conv: Rust,
+           can_unwind: $SOME_BOOL,
+       }
+       right ABI = FnAbi {
+           args: [
+               ArgAbi {
+                   layout: TyAndLayout {
+                       ty: u32,
+                       layout: Layout {
+                           size: $SOME_SIZE,
+                           align: AbiAndPrefAlign {
+                               abi: $SOME_ALIGN,
+                               pref: $SOME_ALIGN,
+                           },
+                           abi: Scalar(
+                               Initialized {
+                                   value: Int(
+                                       I32,
+                                       false,
+                                   ),
+                                   valid_range: $FULL,
+                               },
+                           ),
+                           fields: Primitive,
+                           largest_niche: None,
+                           variants: Single {
+                               index: 0,
+                           },
+                           max_repr_align: None,
+                           unadjusted_abi_align: $SOME_ALIGN,
+                       },
+                   },
+                   mode: Direct(
+                       ArgAttributes {
+                           regular: NoUndef,
+                           arg_ext: None,
+                           pointee_size: Size(0 bytes),
+                           pointee_align: None,
+                       },
+                   ),
+               },
+           ],
+           ret: ArgAbi {
+               layout: TyAndLayout {
+                   ty: (),
+                   layout: Layout {
+                       size: Size(0 bytes),
+                       align: AbiAndPrefAlign {
+                           abi: $SOME_ALIGN,
+                           pref: $SOME_ALIGN,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       fields: Arbitrary {
+                           offsets: [],
+                           memory_index: [],
+                       },
+                       largest_niche: None,
+                       variants: Single {
+                           index: 0,
+                       },
+                       max_repr_align: None,
+                       unadjusted_abi_align: $SOME_ALIGN,
+                   },
+               },
+               mode: Ignore,
+           },
+           c_variadic: false,
+           fixed_count: 1,
+           conv: Rust,
+           can_unwind: $SOME_BOOL,
+       }
+  --> $DIR/debug.rs:46:1
+   |
+LL | type TestAbiNeFloat = (fn(f32), fn(u32));
+   | ^^^^^^^^^^^^^^^^^^^
+
+error: ABIs are not compatible
+       left ABI = FnAbi {
+           args: [
+               ArgAbi {
+                   layout: TyAndLayout {
+                       ty: i32,
+                       layout: Layout {
+                           size: $SOME_SIZE,
+                           align: AbiAndPrefAlign {
+                               abi: $SOME_ALIGN,
+                               pref: $SOME_ALIGN,
+                           },
+                           abi: Scalar(
+                               Initialized {
+                                   value: Int(
+                                       I32,
+                                       true,
+                                   ),
+                                   valid_range: $FULL,
+                               },
+                           ),
+                           fields: Primitive,
+                           largest_niche: None,
+                           variants: Single {
+                               index: 0,
+                           },
+                           max_repr_align: None,
+                           unadjusted_abi_align: $SOME_ALIGN,
+                       },
+                   },
+                   mode: Direct(
+                       ArgAttributes {
+                           regular: NoUndef,
+                           arg_ext: None,
+                           pointee_size: Size(0 bytes),
+                           pointee_align: None,
+                       },
+                   ),
+               },
+           ],
+           ret: ArgAbi {
+               layout: TyAndLayout {
+                   ty: (),
+                   layout: Layout {
+                       size: Size(0 bytes),
+                       align: AbiAndPrefAlign {
+                           abi: $SOME_ALIGN,
+                           pref: $SOME_ALIGN,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       fields: Arbitrary {
+                           offsets: [],
+                           memory_index: [],
+                       },
+                       largest_niche: None,
+                       variants: Single {
+                           index: 0,
+                       },
+                       max_repr_align: None,
+                       unadjusted_abi_align: $SOME_ALIGN,
+                   },
+               },
+               mode: Ignore,
+           },
+           c_variadic: false,
+           fixed_count: 1,
+           conv: Rust,
+           can_unwind: $SOME_BOOL,
+       }
+       right ABI = FnAbi {
+           args: [
+               ArgAbi {
+                   layout: TyAndLayout {
+                       ty: u32,
+                       layout: Layout {
+                           size: $SOME_SIZE,
+                           align: AbiAndPrefAlign {
+                               abi: $SOME_ALIGN,
+                               pref: $SOME_ALIGN,
+                           },
+                           abi: Scalar(
+                               Initialized {
+                                   value: Int(
+                                       I32,
+                                       false,
+                                   ),
+                                   valid_range: $FULL,
+                               },
+                           ),
+                           fields: Primitive,
+                           largest_niche: None,
+                           variants: Single {
+                               index: 0,
+                           },
+                           max_repr_align: None,
+                           unadjusted_abi_align: $SOME_ALIGN,
+                       },
+                   },
+                   mode: Direct(
+                       ArgAttributes {
+                           regular: NoUndef,
+                           arg_ext: None,
+                           pointee_size: Size(0 bytes),
+                           pointee_align: None,
+                       },
+                   ),
+               },
+           ],
+           ret: ArgAbi {
+               layout: TyAndLayout {
+                   ty: (),
+                   layout: Layout {
+                       size: Size(0 bytes),
+                       align: AbiAndPrefAlign {
+                           abi: $SOME_ALIGN,
+                           pref: $SOME_ALIGN,
+                       },
+                       abi: Aggregate {
+                           sized: true,
+                       },
+                       fields: Arbitrary {
+                           offsets: [],
+                           memory_index: [],
+                       },
+                       largest_niche: None,
+                       variants: Single {
+                           index: 0,
+                       },
+                       max_repr_align: None,
+                       unadjusted_abi_align: $SOME_ALIGN,
+                   },
+               },
+               mode: Ignore,
+           },
+           c_variadic: false,
+           fixed_count: 1,
+           conv: Rust,
+           can_unwind: $SOME_BOOL,
+       }
+  --> $DIR/debug.rs:50:1
+   |
+LL | type TestAbiNeSign = (fn(i32), fn(u32));
+   | ^^^^^^^^^^^^^^^^^^
+
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/debug.rs:53:46
+   |
+LL | type TestAbiEqNonsense = (fn((str, str)), fn((str, str)));
+   |                                              ^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `str`
+   = note: only the last element of a tuple may have a dynamically sized type
+
+error: `#[rustc_abi]` can only be applied to function items, type aliases, and associated functions
+  --> $DIR/debug.rs:28:5
+   |
+LL |     const C: () = ();
+   |     ^^^^^^^^^^^
+
+error: fn_abi_of(assoc_test) = FnAbi {
            args: [
                ArgAbi {
                    layout: TyAndLayout {
@@ -251,10 +949,11 @@ error: fn_abi_of_instance(assoc_test) = FnAbi {
            conv: Rust,
            can_unwind: $SOME_BOOL,
        }
-  --> $DIR/debug.rs:22:5
+  --> $DIR/debug.rs:33:5
    |
 LL |     fn assoc_test(&self) { }
    |     ^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 3 previous errors
+error: aborting due to 11 previous errors
 
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/associated-consts/associated-const-array-len.stderr b/tests/ui/associated-consts/associated-const-array-len.stderr
index 0e0dec35b53..e3db45810fd 100644
--- a/tests/ui/associated-consts/associated-const-array-len.stderr
+++ b/tests/ui/associated-consts/associated-const-array-len.stderr
@@ -3,6 +3,12 @@ error[E0277]: the trait bound `i32: Foo` is not satisfied
    |
 LL | const X: [i32; <i32 as Foo>::ID] = [0, 1, 2];
    |                 ^^^ the trait `Foo` is not implemented for `i32`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/associated-const-array-len.rs:1:1
+   |
+LL | trait Foo {
+   | ^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/tests/ui/associated-consts/issue-105330.stderr b/tests/ui/associated-consts/issue-105330.stderr
index e9fe3a5e514..927422fa8dc 100644
--- a/tests/ui/associated-consts/issue-105330.stderr
+++ b/tests/ui/associated-consts/issue-105330.stderr
@@ -51,6 +51,11 @@ error[E0277]: the trait bound `Demo: TraitWAssocConst` is not satisfied
 LL |     foo::<Demo>()();
    |           ^^^^ the trait `TraitWAssocConst` is not implemented for `Demo`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-105330.rs:1:1
+   |
+LL | pub trait TraitWAssocConst {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
 note: required by a bound in `foo`
   --> $DIR/issue-105330.rs:11:11
    |
@@ -87,6 +92,11 @@ error[E0277]: the trait bound `Demo: TraitWAssocConst` is not satisfied
 LL |     foo::<Demo>();
    |           ^^^^ the trait `TraitWAssocConst` is not implemented for `Demo`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-105330.rs:1:1
+   |
+LL | pub trait TraitWAssocConst {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
 note: required by a bound in `foo`
   --> $DIR/issue-105330.rs:11:11
    |
diff --git a/tests/ui/associated-type-bounds/return-type-notation/basic.without.stderr b/tests/ui/associated-type-bounds/return-type-notation/basic.without.stderr
index c2da4f57696..edce1045e24 100644
--- a/tests/ui/associated-type-bounds/return-type-notation/basic.without.stderr
+++ b/tests/ui/associated-type-bounds/return-type-notation/basic.without.stderr
@@ -13,12 +13,12 @@ error: future cannot be sent between threads safely
 LL |     is_send(foo::<T>());
    |             ^^^^^^^^^^ future returned by `foo` is not `Send`
    |
-   = help: within `impl Future<Output = Result<(), ()>>`, the trait `Send` is not implemented for `impl Future<Output = Result<(), ()>>`
+   = help: within `impl Future<Output = Result<(), ()>>`, the trait `Send` is not implemented for `impl Future<Output = Result<(), ()>> { <T as Foo>::method() }`
 note: future is not `Send` as it awaits another future which is not `Send`
   --> $DIR/basic.rs:13:5
    |
 LL |     T::method().await?;
-   |     ^^^^^^^^^^^ await occurs here on type `impl Future<Output = Result<(), ()>>`, which is not `Send`
+   |     ^^^^^^^^^^^ await occurs here on type `impl Future<Output = Result<(), ()>> { <T as Foo>::method() }`, which is not `Send`
 note: required by a bound in `is_send`
   --> $DIR/basic.rs:17:20
    |
diff --git a/tests/ui/associated-types/associated-types-ICE-when-projecting-out-of-err.stderr b/tests/ui/associated-types/associated-types-ICE-when-projecting-out-of-err.stderr
index 8c3463a2832..fbc4ccd4cf4 100644
--- a/tests/ui/associated-types/associated-types-ICE-when-projecting-out-of-err.stderr
+++ b/tests/ui/associated-types/associated-types-ICE-when-projecting-out-of-err.stderr
@@ -3,6 +3,12 @@ error[E0277]: the trait bound `(): Add<A>` is not satisfied
    |
 LL |     r = r + a;
    |           ^ the trait `Add<A>` is not implemented for `()`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/associated-types-ICE-when-projecting-out-of-err.rs:15:1
+   |
+LL | trait Add<RHS=Self> {
+   | ^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/tests/ui/associated-types/associated-types-no-suitable-supertrait.stderr b/tests/ui/associated-types/associated-types-no-suitable-supertrait.stderr
index bd3ee2abd2c..b3f2e16ba0d 100644
--- a/tests/ui/associated-types/associated-types-no-suitable-supertrait.stderr
+++ b/tests/ui/associated-types/associated-types-no-suitable-supertrait.stderr
@@ -3,6 +3,12 @@ error[E0277]: the trait bound `(T, U): Get` is not satisfied
    |
 LL |     fn uhoh<U:Get>(&self, foo: U, bar: <(T, U) as Get>::Value) {}
    |                                        ^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `(T, U)`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/associated-types-no-suitable-supertrait.rs:12:1
+   |
+LL | trait Get {
+   | ^^^^^^^^^
 
 error[E0277]: the trait bound `Self: Get` is not satisfied
   --> $DIR/associated-types-no-suitable-supertrait.rs:17:40
diff --git a/tests/ui/associated-types/defaults-suitability.stderr b/tests/ui/associated-types/defaults-suitability.stderr
index 4b2094691f8..0a8ad0f89e2 100644
--- a/tests/ui/associated-types/defaults-suitability.stderr
+++ b/tests/ui/associated-types/defaults-suitability.stderr
@@ -58,6 +58,11 @@ error[E0277]: the trait bound `(): Foo<Self>` is not satisfied
 LL |     type Assoc: Foo<Self> = ();
    |                             ^^ the trait `Foo<Self>` is not implemented for `()`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/defaults-suitability.rs:27:1
+   |
+LL | trait Foo<T> {
+   | ^^^^^^^^^^^^
 note: required by a bound in `Bar::Assoc`
   --> $DIR/defaults-suitability.rs:34:17
    |
diff --git a/tests/ui/associated-types/issue-23595-2.stderr b/tests/ui/associated-types/issue-23595-2.stderr
index dded673f6ee..73effa9f955 100644
--- a/tests/ui/associated-types/issue-23595-2.stderr
+++ b/tests/ui/associated-types/issue-23595-2.stderr
@@ -2,7 +2,7 @@ error[E0220]: associated type `anything_here_kills_it` not found for `Self`
   --> $DIR/issue-23595-2.rs:6:22
    |
 LL |     type B = C<Self::anything_here_kills_it>;
-   |                      ^^^^^^^^^^^^^^^^^^^^^^ associated type `anything_here_kills_it` not found
+   |                      ^^^^^^^^^^^^^^^^^^^^^^ help: `Self` has the following associated type: `B`
 
 error: aborting due to previous error
 
diff --git a/tests/ui/associated-types/issue-59324.stderr b/tests/ui/associated-types/issue-59324.stderr
index a84b599b52b..266e22d4726 100644
--- a/tests/ui/associated-types/issue-59324.stderr
+++ b/tests/ui/associated-types/issue-59324.stderr
@@ -48,6 +48,12 @@ error[E0277]: the trait bound `(): Foo` is not satisfied
    |
 LL | fn with_factory<H>(factory: dyn ThriftService<()>) {}
    |                             ^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-59324.rs:3:1
+   |
+LL | pub trait Foo: NotFoo {
+   | ^^^^^^^^^^^^^^^^^^^^^
 
 error[E0277]: the trait bound `Bug: Foo` is not satisfied
   --> $DIR/issue-59324.rs:19:10
diff --git a/tests/ui/associated-types/issue-64855.stderr b/tests/ui/associated-types/issue-64855.stderr
index 6ad795c1117..f1016f0e3a1 100644
--- a/tests/ui/associated-types/issue-64855.stderr
+++ b/tests/ui/associated-types/issue-64855.stderr
@@ -3,6 +3,12 @@ error[E0277]: the trait bound `Bar<T>: Foo` is not satisfied
    |
 LL | pub struct Bar<T>(<Self as Foo>::Type) where Self: ;
    |                   ^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `Bar<T>`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-64855.rs:1:1
+   |
+LL | pub trait Foo {
+   | ^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/tests/ui/associated-types/issue-85103-layout-debug.rs b/tests/ui/associated-types/issue-85103-layout-debug.rs
new file mode 100644
index 00000000000..77c9876ffa5
--- /dev/null
+++ b/tests/ui/associated-types/issue-85103-layout-debug.rs
@@ -0,0 +1,9 @@
+#![feature(rustc_attrs)]
+
+use std::borrow::Cow;
+
+#[rustc_layout(debug)]
+type Edges<'a, E> = Cow<'a, [E]>;
+//~^ the trait bound `[E]: ToOwned` is not satisfied
+
+fn main() {}
diff --git a/tests/ui/associated-types/issue-85103-layout-debug.stderr b/tests/ui/associated-types/issue-85103-layout-debug.stderr
new file mode 100644
index 00000000000..0bdea10ba47
--- /dev/null
+++ b/tests/ui/associated-types/issue-85103-layout-debug.stderr
@@ -0,0 +1,16 @@
+error[E0277]: the trait bound `[E]: ToOwned` is not satisfied
+  --> $DIR/issue-85103-layout-debug.rs:6:21
+   |
+LL | type Edges<'a, E> = Cow<'a, [E]>;
+   |                     ^^^^^^^^^^^^ the trait `ToOwned` is not implemented for `[E]`
+   |
+note: required by a bound in `Cow`
+  --> $SRC_DIR/alloc/src/borrow.rs:LL:COL
+help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
+   |
+LL | type Edges<'a, E> where [E]: ToOwned = Cow<'a, [E]>;
+   |                   ++++++++++++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/associated-types/issue-85103.rs b/tests/ui/associated-types/issue-85103.rs
deleted file mode 100644
index 9c6a419e9f7..00000000000
--- a/tests/ui/associated-types/issue-85103.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-#![feature(rustc_attrs)]
-
-use std::borrow::Cow;
-
-#[rustc_layout(debug)]
-type Edges<'a, E> = Cow<'a, [E]>;
-//~^ 6:1: 6:18: unable to determine layout for `<[E] as ToOwned>::Owned` because `<[E] as ToOwned>::Owned` cannot be normalized
-
-fn main() {}
diff --git a/tests/ui/associated-types/issue-85103.stderr b/tests/ui/associated-types/issue-85103.stderr
deleted file mode 100644
index 17f7148074c..00000000000
--- a/tests/ui/associated-types/issue-85103.stderr
+++ /dev/null
@@ -1,8 +0,0 @@
-error: unable to determine layout for `<[E] as ToOwned>::Owned` because `<[E] as ToOwned>::Owned` cannot be normalized
-  --> $DIR/issue-85103.rs:6:1
-   |
-LL | type Edges<'a, E> = Cow<'a, [E]>;
-   | ^^^^^^^^^^^^^^^^^
-
-error: aborting due to previous error
-
diff --git a/tests/ui/associated-types/point-at-type-on-obligation-failure-2.stderr b/tests/ui/associated-types/point-at-type-on-obligation-failure-2.stderr
index 3b4689e08cc..056d9201b4a 100644
--- a/tests/ui/associated-types/point-at-type-on-obligation-failure-2.stderr
+++ b/tests/ui/associated-types/point-at-type-on-obligation-failure-2.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `bool: Bar` is not satisfied
 LL |     type Assoc = bool;
    |                  ^^^^ the trait `Bar` is not implemented for `bool`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/point-at-type-on-obligation-failure-2.rs:1:1
+   |
+LL | trait Bar {}
+   | ^^^^^^^^^
 note: required by a bound in `Foo::Assoc`
   --> $DIR/point-at-type-on-obligation-failure-2.rs:4:17
    |
@@ -16,6 +21,11 @@ error[E0277]: the trait bound `bool: Bar` is not satisfied
 LL |     type Assoc = bool;
    |                  ^^^^ the trait `Bar` is not implemented for `bool`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/point-at-type-on-obligation-failure-2.rs:1:1
+   |
+LL | trait Bar {}
+   | ^^^^^^^^^
 note: required by a bound in `Baz::Assoc`
   --> $DIR/point-at-type-on-obligation-failure-2.rs:13:18
    |
@@ -31,6 +41,11 @@ error[E0277]: the trait bound `bool: Bar` is not satisfied
 LL |     type Assoc = bool;
    |                  ^^^^ the trait `Bar` is not implemented for `bool`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/point-at-type-on-obligation-failure-2.rs:1:1
+   |
+LL | trait Bar {}
+   | ^^^^^^^^^
 note: required by a bound in `Bat::Assoc`
   --> $DIR/point-at-type-on-obligation-failure-2.rs:24:27
    |
diff --git a/tests/ui/async-await/async-is-unwindsafe.stderr b/tests/ui/async-await/async-is-unwindsafe.stderr
index 5d29325c827..c855e902ba9 100644
--- a/tests/ui/async-await/async-is-unwindsafe.stderr
+++ b/tests/ui/async-await/async-is-unwindsafe.stderr
@@ -15,7 +15,7 @@ LL | |     });
    |       within this `[async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6]`
    |
    = help: within `[async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6]`, the trait `UnwindSafe` is not implemented for `&mut Context<'_>`
-   = note: `UnwindSafe` is implemented for `&std::task::Context<'_>`, but not for `&mut std::task::Context<'_>`
+   = note: `UnwindSafe` is implemented for `&Context<'_>`, but not for `&mut Context<'_>`
 note: future does not implement `UnwindSafe` as this value is used across an await
   --> $DIR/async-is-unwindsafe.rs:25:18
    |
diff --git a/tests/ui/async-await/in-trait/async-example-desugared-extra.rs b/tests/ui/async-await/in-trait/async-example-desugared-extra.rs
index 81e1e59a362..3505690f1ec 100644
--- a/tests/ui/async-await/in-trait/async-example-desugared-extra.rs
+++ b/tests/ui/async-await/in-trait/async-example-desugared-extra.rs
@@ -2,14 +2,14 @@
 // edition: 2021
 
 #![feature(async_fn_in_trait)]
-#![feature(return_position_impl_trait_in_trait)]
+#![feature(return_position_impl_trait_in_trait, lint_reasons)]
 #![allow(incomplete_features)]
 
 use std::future::Future;
 use std::pin::Pin;
 use std::task::Poll;
 
-trait MyTrait {
+pub trait MyTrait {
     async fn foo(&self) -> i32;
 }
 
@@ -27,8 +27,7 @@ impl Future for MyFuture {
 }
 
 impl MyTrait for i32 {
-    // FIXME: this should eventually require `#[refine]` to compile, because it also provides
-    // `Clone`.
+    #[expect(refining_impl_trait)]
     fn foo(&self) -> impl Future<Output = i32> + Clone {
         MyFuture(*self)
     }
diff --git a/tests/ui/async-await/in-trait/async-example-desugared.rs b/tests/ui/async-await/in-trait/async-example-desugared.rs
index fb92ec78674..0a5023176fe 100644
--- a/tests/ui/async-await/in-trait/async-example-desugared.rs
+++ b/tests/ui/async-await/in-trait/async-example-desugared.rs
@@ -12,7 +12,7 @@ trait MyTrait {
 }
 
 impl MyTrait for i32 {
-    fn foo(&self) -> impl Future<Output = i32> + '_ {
+    fn foo(&self) -> impl Future<Output = i32> {
         async { *self }
     }
 }
diff --git a/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.current.stderr b/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.current.stderr
new file mode 100644
index 00000000000..3b63ec45804
--- /dev/null
+++ b/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.current.stderr
@@ -0,0 +1,27 @@
+warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/normalizing-self-auto-trait-issue-109924.rs:8:12
+   |
+LL | #![feature(return_type_notation)]
+   |            ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error[E0277]: `impl Future<Output = ()> { <_ as Foo>::bar() }` cannot be sent between threads safely
+  --> $DIR/normalizing-self-auto-trait-issue-109924.rs:23:11
+   |
+LL |     build(Bar);
+   |     ----- ^^^ `impl Future<Output = ()> { <_ as Foo>::bar() }` cannot be sent between threads safely
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `for<'a> Send` is not implemented for `impl Future<Output = ()> { <_ as Foo>::bar() }`
+note: required by a bound in `build`
+  --> $DIR/normalizing-self-auto-trait-issue-109924.rs:20:39
+   |
+LL | fn build<T>(_: T) where T: Foo<bar(): Send> {}
+   |                                       ^^^^ required by this bound in `build`
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.next.stderr b/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.next.stderr
new file mode 100644
index 00000000000..6fab7178767
--- /dev/null
+++ b/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.next.stderr
@@ -0,0 +1,11 @@
+warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/normalizing-self-auto-trait-issue-109924.rs:8:12
+   |
+LL | #![feature(return_type_notation)]
+   |            ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.rs b/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.rs
new file mode 100644
index 00000000000..b2cd9707db9
--- /dev/null
+++ b/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.rs
@@ -0,0 +1,24 @@
+// revisions: current next
+//[current] known-bug: #109924
+//[next] check-pass
+//[next] compile-flags: -Ztrait-solver=next
+// edition:2021
+
+#![feature(async_fn_in_trait)]
+#![feature(return_type_notation)]
+//[next]~^ WARN the feature `return_type_notation` is incomplete
+
+trait Foo {
+    async fn bar(&self);
+}
+
+struct Bar;
+impl Foo for Bar {
+    async fn bar(&self) {}
+}
+
+fn build<T>(_: T) where T: Foo<bar(): Send> {}
+
+fn main() {
+    build(Bar);
+}
diff --git a/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.rs b/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.rs
new file mode 100644
index 00000000000..4a6c2f9ed06
--- /dev/null
+++ b/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.rs
@@ -0,0 +1,16 @@
+#![allow(dead_code)]
+
+fn bar<'a>(_: std::fmt::Arguments<'a>) {}
+fn main() {
+    let x = format_args!("a {} {} {}.", 1, format_args!("b{}!", 2), 3);
+    //~^ ERROR temporary value dropped while borrowed
+
+    bar(x);
+
+    let foo = format_args!("{}", "hi");
+    //~^ ERROR temporary value dropped while borrowed
+    bar(foo);
+
+    let foo = format_args!("hi"); // no placeholder in arguments, so no error
+    bar(foo);
+}
diff --git a/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.stderr b/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.stderr
new file mode 100644
index 00000000000..8221505b100
--- /dev/null
+++ b/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.stderr
@@ -0,0 +1,33 @@
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/issue-114374-invalid-help-fmt-args.rs:5:13
+   |
+LL |     let x = format_args!("a {} {} {}.", 1, format_args!("b{}!", 2), 3);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement
+   |             |
+   |             creates a temporary value which is freed while still in use
+...
+LL |     bar(x);
+   |         - borrow later used here
+   |
+   = note: the result of `format_args!` can only be assigned directly if no placeholders in it's arguments are used
+   = note: to learn more, visit <https://doc.rust-lang.org/std/macro.format_args.html>
+   = note: this error originates in the macro `format_args` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/issue-114374-invalid-help-fmt-args.rs:10:15
+   |
+LL |     let foo = format_args!("{}", "hi");
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement
+   |               |
+   |               creates a temporary value which is freed while still in use
+LL |
+LL |     bar(foo);
+   |         --- borrow later used here
+   |
+   = note: the result of `format_args!` can only be assigned directly if no placeholders in it's arguments are used
+   = note: to learn more, visit <https://doc.rust-lang.org/std/macro.format_args.html>
+   = note: this error originates in the macro `format_args` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0716`.
diff --git a/tests/ui/borrowck/issue-115259-suggest-iter-mut.fixed b/tests/ui/borrowck/issue-115259-suggest-iter-mut.fixed
new file mode 100644
index 00000000000..4653fe7375d
--- /dev/null
+++ b/tests/ui/borrowck/issue-115259-suggest-iter-mut.fixed
@@ -0,0 +1,20 @@
+// run-rustfix
+#![allow(unused_mut)]
+#![allow(dead_code)]
+
+pub trait Layer {
+    fn process(&mut self) -> u32;
+}
+
+pub struct State {
+    layers: Vec<Box<dyn Layer>>,
+}
+
+impl State {
+    pub fn process(&mut self) -> u32 {
+        self.layers.iter_mut().fold(0, |result, mut layer| result + layer.process())
+        //~^ ERROR cannot borrow `**layer` as mutable, as it is behind a `&` reference
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/borrowck/issue-115259-suggest-iter-mut.rs b/tests/ui/borrowck/issue-115259-suggest-iter-mut.rs
new file mode 100644
index 00000000000..e0f6ab1321f
--- /dev/null
+++ b/tests/ui/borrowck/issue-115259-suggest-iter-mut.rs
@@ -0,0 +1,20 @@
+// run-rustfix
+#![allow(unused_mut)]
+#![allow(dead_code)]
+
+pub trait Layer {
+    fn process(&mut self) -> u32;
+}
+
+pub struct State {
+    layers: Vec<Box<dyn Layer>>,
+}
+
+impl State {
+    pub fn process(&mut self) -> u32 {
+        self.layers.iter().fold(0, |result, mut layer| result + layer.process())
+        //~^ ERROR cannot borrow `**layer` as mutable, as it is behind a `&` reference
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/borrowck/issue-115259-suggest-iter-mut.stderr b/tests/ui/borrowck/issue-115259-suggest-iter-mut.stderr
new file mode 100644
index 00000000000..7e0fc2cf298
--- /dev/null
+++ b/tests/ui/borrowck/issue-115259-suggest-iter-mut.stderr
@@ -0,0 +1,16 @@
+error[E0596]: cannot borrow `**layer` as mutable, as it is behind a `&` reference
+  --> $DIR/issue-115259-suggest-iter-mut.rs:15:65
+   |
+LL |         self.layers.iter().fold(0, |result, mut layer| result + layer.process())
+   |                                             ---------           ^^^^^ `layer` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+   |                                             |
+   |                                             consider changing this binding's type to be: `&mut Box<dyn Layer>`
+   |
+help: you may want to use `iter_mut` here
+   |
+LL |         self.layers.iter_mut().fold(0, |result, mut layer| result + layer.process())
+   |                     ~~~~~~~~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/tests/ui/borrowck/issue-62387-suggest-iter-mut-2.fixed b/tests/ui/borrowck/issue-62387-suggest-iter-mut-2.fixed
new file mode 100644
index 00000000000..f02374d8e11
--- /dev/null
+++ b/tests/ui/borrowck/issue-62387-suggest-iter-mut-2.fixed
@@ -0,0 +1,36 @@
+// run-rustfix
+#![allow(unused_mut)]
+#![allow(dead_code)]
+use std::path::PathBuf;
+
+#[derive(Clone)]
+struct Container {
+    things: Vec<PathBuf>,
+}
+
+impl Container {
+    fn things(&mut self) -> &[PathBuf] {
+        &self.things
+    }
+}
+
+// contains containers
+struct ContainerContainer {
+    contained: Vec<Container>,
+}
+
+impl ContainerContainer {
+    fn contained(&self) -> &[Container] {
+        &self.contained
+    }
+
+    fn all_the_things(&mut self) -> &[PathBuf] {
+        let mut vec = self.contained.clone();
+        let _a =
+            vec.iter_mut().flat_map(|container| container.things()).cloned().collect::<Vec<PathBuf>>();
+        //~^ ERROR cannot borrow `*container` as mutable, as it is behind a `&` reference
+        unimplemented!();
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/borrowck/issue-62387-suggest-iter-mut-2.rs b/tests/ui/borrowck/issue-62387-suggest-iter-mut-2.rs
new file mode 100644
index 00000000000..2d0b837a946
--- /dev/null
+++ b/tests/ui/borrowck/issue-62387-suggest-iter-mut-2.rs
@@ -0,0 +1,36 @@
+// run-rustfix
+#![allow(unused_mut)]
+#![allow(dead_code)]
+use std::path::PathBuf;
+
+#[derive(Clone)]
+struct Container {
+    things: Vec<PathBuf>,
+}
+
+impl Container {
+    fn things(&mut self) -> &[PathBuf] {
+        &self.things
+    }
+}
+
+// contains containers
+struct ContainerContainer {
+    contained: Vec<Container>,
+}
+
+impl ContainerContainer {
+    fn contained(&self) -> &[Container] {
+        &self.contained
+    }
+
+    fn all_the_things(&mut self) -> &[PathBuf] {
+        let mut vec = self.contained.clone();
+        let _a =
+            vec.iter().flat_map(|container| container.things()).cloned().collect::<Vec<PathBuf>>();
+        //~^ ERROR cannot borrow `*container` as mutable, as it is behind a `&` reference
+        unimplemented!();
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/borrowck/issue-62387-suggest-iter-mut-2.stderr b/tests/ui/borrowck/issue-62387-suggest-iter-mut-2.stderr
new file mode 100644
index 00000000000..19f194100a1
--- /dev/null
+++ b/tests/ui/borrowck/issue-62387-suggest-iter-mut-2.stderr
@@ -0,0 +1,16 @@
+error[E0596]: cannot borrow `*container` as mutable, as it is behind a `&` reference
+  --> $DIR/issue-62387-suggest-iter-mut-2.rs:30:45
+   |
+LL |             vec.iter().flat_map(|container| container.things()).cloned().collect::<Vec<PathBuf>>();
+   |                                  ---------  ^^^^^^^^^ `container` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+   |                                  |
+   |                                  consider changing this binding's type to be: `&mut Container`
+   |
+help: you may want to use `iter_mut` here
+   |
+LL |             vec.iter_mut().flat_map(|container| container.things()).cloned().collect::<Vec<PathBuf>>();
+   |                 ~~~~~~~~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/tests/ui/borrowck/issue-62387-suggest-iter-mut.fixed b/tests/ui/borrowck/issue-62387-suggest-iter-mut.fixed
new file mode 100644
index 00000000000..8bf2625de6d
--- /dev/null
+++ b/tests/ui/borrowck/issue-62387-suggest-iter-mut.fixed
@@ -0,0 +1,30 @@
+// run-rustfix
+#![allow(unused_mut)]
+#![allow(dead_code)]
+
+#[derive(Debug)]
+struct A {
+    a: i32,
+}
+
+impl A {
+    fn double(&mut self) {
+        self.a += self.a
+    }
+}
+
+fn baz() {
+    let mut v = [A { a: 4 }];
+    v.iter_mut().for_each(|a| a.double());
+    //~^ ERROR cannot borrow `*a` as mutable, as it is behind a `&` reference
+    println!("{:?}", v);
+}
+
+fn bar() {
+    let mut v = [A { a: 4 }];
+    v.iter_mut().rev().rev().for_each(|a| a.double());
+    //~^ ERROR cannot borrow `*a` as mutable, as it is behind a `&` reference
+    println!("{:?}", v);
+}
+
+fn main() {}
diff --git a/tests/ui/borrowck/issue-62387-suggest-iter-mut.rs b/tests/ui/borrowck/issue-62387-suggest-iter-mut.rs
new file mode 100644
index 00000000000..39bc30bf294
--- /dev/null
+++ b/tests/ui/borrowck/issue-62387-suggest-iter-mut.rs
@@ -0,0 +1,30 @@
+// run-rustfix
+#![allow(unused_mut)]
+#![allow(dead_code)]
+
+#[derive(Debug)]
+struct A {
+    a: i32,
+}
+
+impl A {
+    fn double(&mut self) {
+        self.a += self.a
+    }
+}
+
+fn baz() {
+    let mut v = [A { a: 4 }];
+    v.iter().for_each(|a| a.double());
+    //~^ ERROR cannot borrow `*a` as mutable, as it is behind a `&` reference
+    println!("{:?}", v);
+}
+
+fn bar() {
+    let mut v = [A { a: 4 }];
+    v.iter().rev().rev().for_each(|a| a.double());
+    //~^ ERROR cannot borrow `*a` as mutable, as it is behind a `&` reference
+    println!("{:?}", v);
+}
+
+fn main() {}
diff --git a/tests/ui/borrowck/issue-62387-suggest-iter-mut.stderr b/tests/ui/borrowck/issue-62387-suggest-iter-mut.stderr
new file mode 100644
index 00000000000..fd58e433020
--- /dev/null
+++ b/tests/ui/borrowck/issue-62387-suggest-iter-mut.stderr
@@ -0,0 +1,29 @@
+error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference
+  --> $DIR/issue-62387-suggest-iter-mut.rs:18:27
+   |
+LL |     v.iter().for_each(|a| a.double());
+   |                        -  ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+   |                        |
+   |                        consider changing this binding's type to be: `&mut A`
+   |
+help: you may want to use `iter_mut` here
+   |
+LL |     v.iter_mut().for_each(|a| a.double());
+   |       ~~~~~~~~
+
+error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference
+  --> $DIR/issue-62387-suggest-iter-mut.rs:25:39
+   |
+LL |     v.iter().rev().rev().for_each(|a| a.double());
+   |                                    -  ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+   |                                    |
+   |                                    consider changing this binding's type to be: `&mut A`
+   |
+help: you may want to use `iter_mut` here
+   |
+LL |     v.iter_mut().rev().rev().for_each(|a| a.double());
+   |       ~~~~~~~~
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/tests/ui/c-variadic/variadic-ffi-2.rs b/tests/ui/c-variadic/variadic-ffi-2.rs
index c34b7e55f6a..67a0a9a1dec 100644
--- a/tests/ui/c-variadic/variadic-ffi-2.rs
+++ b/tests/ui/c-variadic/variadic-ffi-2.rs
@@ -3,10 +3,13 @@
 
 fn baz(f: extern "stdcall" fn(usize, ...)) {
     //~^ ERROR: C-variadic function must have a compatible calling convention,
-    // like C, cdecl, win64, sysv64 or efiapi
+    // like C, cdecl, aapcs, win64, sysv64 or efiapi
     f(22, 44);
 }
 
+fn aapcs(f: extern "aapcs" fn(usize, ...)) {
+    f(22, 44);
+}
 fn sysv(f: extern "sysv64" fn(usize, ...)) {
     f(22, 44);
 }
diff --git a/tests/ui/c-variadic/variadic-ffi-2.stderr b/tests/ui/c-variadic/variadic-ffi-2.stderr
index e21001ecaf8..8884fc6fb2a 100644
--- a/tests/ui/c-variadic/variadic-ffi-2.stderr
+++ b/tests/ui/c-variadic/variadic-ffi-2.stderr
@@ -1,4 +1,4 @@
-error[E0045]: C-variadic function must have a compatible calling convention, like `C`, `cdecl`, `win64`, `sysv64` or `efiapi`
+error[E0045]: C-variadic function must have a compatible calling convention, like `C`, `cdecl`, `aapcs`, `win64`, `sysv64` or `efiapi`
   --> $DIR/variadic-ffi-2.rs:4:11
    |
 LL | fn baz(f: extern "stdcall" fn(usize, ...)) {
diff --git a/tests/ui/cast/cast-as-bool.rs b/tests/ui/cast/cast-as-bool.rs
index fbebc80d91c..750a88f97eb 100644
--- a/tests/ui/cast/cast-as-bool.rs
+++ b/tests/ui/cast/cast-as-bool.rs
@@ -1,12 +1,48 @@
 fn main() {
-    let u = 5 as bool; //~ ERROR cannot cast as `bool`
+    let u = 5 as bool; //~ ERROR cannot cast `i32` as `bool`
                        //~| HELP compare with zero instead
                        //~| SUGGESTION 5 != 0
 
-    let t = (1 + 2) as bool; //~ ERROR cannot cast as `bool`
+    let t = (1 + 2) as bool; //~ ERROR cannot cast `i32` as `bool`
                              //~| HELP compare with zero instead
                              //~| SUGGESTION (1 + 2) != 0
 
+    let _ = 5_u32 as bool; //~ ERROR cannot cast `u32` as `bool`
+                           //~| HELP compare with zero instead
+
+    let _ = 64.0_f64 as bool; //~ ERROR cannot cast `f64` as `bool`
+                              //~| HELP compare with zero instead
+
+    // Enums that can normally be cast to integers can't be cast to `bool`, just like integers.
+    // Note that enums that cannot be cast to integers can't be cast to anything at *all*
+    // so that's not tested here.
+    enum IntEnum {
+        Zero,
+        One,
+        Two
+    }
+    let _ = IntEnum::One as bool; //~ ERROR cannot cast `IntEnum` as `bool`
+
+    fn uwu(_: u8) -> String {
+        todo!()
+    }
+
+    unsafe fn owo() {}
+
+    // fn item to bool
+    let _ = uwu as bool; //~ ERROR cannot cast `fn(u8) -> String {uwu}` as `bool`
+    // unsafe fn item
+    let _ = owo as bool; //~ ERROR cannot cast `unsafe fn() {owo}` as `bool`
+
+    // fn ptr to bool
+    let _ = uwu as fn(u8) -> String as bool; //~ ERROR cannot cast `fn(u8) -> String` as `bool`
+
+    let _ = 'x' as bool; //~ ERROR cannot cast `char` as `bool`
+
+    let ptr = 1 as *const ();
+
+    let _ = ptr as bool; //~ ERROR cannot cast `*const ()` as `bool`
+
     let v = "hello" as bool;
     //~^ ERROR casting `&'static str` as `bool` is invalid
     //~| HELP consider using the `is_empty` method on `&'static str` to determine if it contains anything
diff --git a/tests/ui/cast/cast-as-bool.stderr b/tests/ui/cast/cast-as-bool.stderr
index 19ac8f10fec..852fb30cc20 100644
--- a/tests/ui/cast/cast-as-bool.stderr
+++ b/tests/ui/cast/cast-as-bool.stderr
@@ -1,18 +1,66 @@
-error[E0054]: cannot cast as `bool`
+error[E0054]: cannot cast `i32` as `bool`
   --> $DIR/cast-as-bool.rs:2:13
    |
 LL |     let u = 5 as bool;
    |             ^^^^^^^^^ help: compare with zero instead: `5 != 0`
 
-error[E0054]: cannot cast as `bool`
+error[E0054]: cannot cast `i32` as `bool`
   --> $DIR/cast-as-bool.rs:6:13
    |
 LL |     let t = (1 + 2) as bool;
    |             ^^^^^^^^^^^^^^^ help: compare with zero instead: `(1 + 2) != 0`
 
-error[E0606]: casting `&'static str` as `bool` is invalid
+error[E0054]: cannot cast `u32` as `bool`
   --> $DIR/cast-as-bool.rs:10:13
    |
+LL |     let _ = 5_u32 as bool;
+   |             ^^^^^^^^^^^^^ help: compare with zero instead: `5_u32 != 0`
+
+error[E0054]: cannot cast `f64` as `bool`
+  --> $DIR/cast-as-bool.rs:13:13
+   |
+LL |     let _ = 64.0_f64 as bool;
+   |             ^^^^^^^^^^^^^^^^ help: compare with zero instead: `64.0_f64 != 0`
+
+error[E0054]: cannot cast `IntEnum` as `bool`
+  --> $DIR/cast-as-bool.rs:24:13
+   |
+LL |     let _ = IntEnum::One as bool;
+   |             ^^^^^^^^^^^^^^^^^^^^ unsupported cast
+
+error[E0054]: cannot cast `fn(u8) -> String {uwu}` as `bool`
+  --> $DIR/cast-as-bool.rs:33:13
+   |
+LL |     let _ = uwu as bool;
+   |             ^^^^^^^^^^^ unsupported cast
+
+error[E0054]: cannot cast `unsafe fn() {owo}` as `bool`
+  --> $DIR/cast-as-bool.rs:35:13
+   |
+LL |     let _ = owo as bool;
+   |             ^^^^^^^^^^^ unsupported cast
+
+error[E0054]: cannot cast `fn(u8) -> String` as `bool`
+  --> $DIR/cast-as-bool.rs:38:13
+   |
+LL |     let _ = uwu as fn(u8) -> String as bool;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported cast
+
+error[E0054]: cannot cast `char` as `bool`
+  --> $DIR/cast-as-bool.rs:40:13
+   |
+LL |     let _ = 'x' as bool;
+   |             ^^^^^^^^^^^ unsupported cast
+
+error[E0054]: cannot cast `*const ()` as `bool`
+  --> $DIR/cast-as-bool.rs:44:13
+   |
+LL |     let _ = ptr as bool;
+   |             ^^^^^^^^^^^ unsupported cast
+
+error[E0606]: casting `&'static str` as `bool` is invalid
+  --> $DIR/cast-as-bool.rs:46:13
+   |
 LL |     let v = "hello" as bool;
    |             ^^^^^^^^^^^^^^^
    |
@@ -21,7 +69,7 @@ help: consider using the `is_empty` method on `&'static str` to determine if it
 LL |     let v = !"hello".is_empty();
    |             +       ~~~~~~~~~~~
 
-error: aborting due to 3 previous errors
+error: aborting due to 11 previous errors
 
 Some errors have detailed explanations: E0054, E0606.
 For more information about an error, try `rustc --explain E0054`.
diff --git a/tests/ui/cast/cast-rfc0401-2.rs b/tests/ui/cast/cast-rfc0401-2.rs
index 7709aa34104..70604a587ea 100644
--- a/tests/ui/cast/cast-rfc0401-2.rs
+++ b/tests/ui/cast/cast-rfc0401-2.rs
@@ -4,5 +4,5 @@
 
 fn main() {
     let _ = 3 as bool;
-    //~^ ERROR cannot cast as `bool`
+    //~^ ERROR cannot cast `i32` as `bool`
 }
diff --git a/tests/ui/cast/cast-rfc0401-2.stderr b/tests/ui/cast/cast-rfc0401-2.stderr
index 52f6af78a9b..5dc21ca847c 100644
--- a/tests/ui/cast/cast-rfc0401-2.stderr
+++ b/tests/ui/cast/cast-rfc0401-2.stderr
@@ -1,4 +1,4 @@
-error[E0054]: cannot cast as `bool`
+error[E0054]: cannot cast `i32` as `bool`
   --> $DIR/cast-rfc0401-2.rs:6:13
    |
 LL |     let _ = 3 as bool;
diff --git a/tests/ui/closures/2229_closure_analysis/repr_packed.rs b/tests/ui/closures/2229_closure_analysis/repr_packed.rs
index f23670f63ac..8c23454fae9 100644
--- a/tests/ui/closures/2229_closure_analysis/repr_packed.rs
+++ b/tests/ui/closures/2229_closure_analysis/repr_packed.rs
@@ -3,7 +3,8 @@
 #![feature(rustc_attrs)]
 
 // `u8` aligned at a byte and are unaffected by repr(packed).
-// Therefore we can precisely (and safely) capture references to both the fields.
+// Therefore we *could* precisely (and safely) capture references to both the fields,
+// but we don't, since we don't want capturing to change when field types change alignment.
 fn test_alignment_not_affected() {
     #[repr(packed)]
     struct Foo { x: u8, y: u8 }
@@ -17,11 +18,10 @@ fn test_alignment_not_affected() {
     //~^ ERROR: First Pass analysis includes:
     //~| ERROR: Min Capture analysis includes:
         let z1: &u8 = &foo.x;
-        //~^ NOTE: Capturing foo[(0, 0)] -> ImmBorrow
-        //~| NOTE: Min Capture foo[(0, 0)] -> ImmBorrow
+        //~^ NOTE: Capturing foo[] -> ImmBorrow
         let z2: &mut u8 = &mut foo.y;
-        //~^ NOTE: Capturing foo[(1, 0)] -> MutBorrow
-        //~| NOTE: Min Capture foo[(1, 0)] -> MutBorrow
+        //~^ NOTE: Capturing foo[] -> MutBorrow
+        //~| NOTE: Min Capture foo[] -> MutBorrow
 
         *z2 = 42;
 
diff --git a/tests/ui/closures/2229_closure_analysis/repr_packed.stderr b/tests/ui/closures/2229_closure_analysis/repr_packed.stderr
index 580061ebc6e..32b3d844c6e 100644
--- a/tests/ui/closures/2229_closure_analysis/repr_packed.stderr
+++ b/tests/ui/closures/2229_closure_analysis/repr_packed.stderr
@@ -1,5 +1,5 @@
 error[E0658]: attributes on expressions are experimental
-  --> $DIR/repr_packed.rs:13:17
+  --> $DIR/repr_packed.rs:14:17
    |
 LL |     let mut c = #[rustc_capture_analysis]
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -26,7 +26,7 @@ LL |     let c = #[rustc_capture_analysis]
    = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
 
 error: First Pass analysis includes:
-  --> $DIR/repr_packed.rs:16:5
+  --> $DIR/repr_packed.rs:17:5
    |
 LL | /     || {
 LL | |
@@ -37,19 +37,19 @@ LL | |         println!("({}, {})", z1, z2);
 LL | |     };
    | |_____^
    |
-note: Capturing foo[(0, 0)] -> ImmBorrow
-  --> $DIR/repr_packed.rs:19:24
+note: Capturing foo[] -> ImmBorrow
+  --> $DIR/repr_packed.rs:20:24
    |
 LL |         let z1: &u8 = &foo.x;
    |                        ^^^^^
-note: Capturing foo[(1, 0)] -> MutBorrow
+note: Capturing foo[] -> MutBorrow
   --> $DIR/repr_packed.rs:22:32
    |
 LL |         let z2: &mut u8 = &mut foo.y;
    |                                ^^^^^
 
 error: Min Capture analysis includes:
-  --> $DIR/repr_packed.rs:16:5
+  --> $DIR/repr_packed.rs:17:5
    |
 LL | /     || {
 LL | |
@@ -60,12 +60,7 @@ LL | |         println!("({}, {})", z1, z2);
 LL | |     };
    | |_____^
    |
-note: Min Capture foo[(0, 0)] -> ImmBorrow
-  --> $DIR/repr_packed.rs:19:24
-   |
-LL |         let z1: &u8 = &foo.x;
-   |                        ^^^^^
-note: Min Capture foo[(1, 0)] -> MutBorrow
+note: Min Capture foo[] -> MutBorrow
   --> $DIR/repr_packed.rs:22:32
    |
 LL |         let z2: &mut u8 = &mut foo.y;
diff --git a/tests/ui/codegen/subtyping-enforces-type-equality.rs b/tests/ui/codegen/subtyping-enforces-type-equality.rs
new file mode 100644
index 00000000000..a5ffcb3f854
--- /dev/null
+++ b/tests/ui/codegen/subtyping-enforces-type-equality.rs
@@ -0,0 +1,48 @@
+// ignore-pass
+// build-pass
+// edition:2021
+use std::future::Future;
+use std::pin::Pin;
+
+type BoxFuture<T> = Pin<Box<dyn Future<Output = T>>>;
+
+fn main() {
+    let _ = wrapper_call(handler);
+}
+
+async fn wrapper_call(handler: impl Handler) {
+    handler.call().await;
+}
+async fn handler() {
+    f(&()).await;
+}
+async fn f<'a>(db: impl Acquire<'a>) {
+    db.acquire().await;
+}
+
+trait Handler {
+    type Future: Future;
+    fn call(self) -> Self::Future;
+}
+
+impl<Fut, F> Handler for F
+where
+    F: Fn() -> Fut,
+    Fut: Future,
+{
+    type Future = Fut;
+    fn call(self) -> Self::Future {
+        loop {}
+    }
+}
+
+trait Acquire<'a> {
+    type Connection;
+    fn acquire(self) -> BoxFuture<Self::Connection>;
+}
+impl<'a> Acquire<'a> for &'a () {
+    type Connection = Self;
+    fn acquire(self) -> BoxFuture<Self> {
+        loop {}
+    }
+}
diff --git a/tests/ui/codegen/subtyping-enforces-type-equality.stderr b/tests/ui/codegen/subtyping-enforces-type-equality.stderr
new file mode 100644
index 00000000000..870ca0f839f
--- /dev/null
+++ b/tests/ui/codegen/subtyping-enforces-type-equality.stderr
@@ -0,0 +1 @@
+WARN rustc_codegen_ssa::mir::locals Unexpected initial operand type. See the issues/114858
diff --git a/tests/ui/coherence/coherence-unsafe-trait-object-impl.stderr b/tests/ui/coherence/coherence-unsafe-trait-object-impl.stderr
index 2e2dac288a1..a3a37fd2775 100644
--- a/tests/ui/coherence/coherence-unsafe-trait-object-impl.stderr
+++ b/tests/ui/coherence/coherence-unsafe-trait-object-impl.stderr
@@ -6,6 +6,11 @@ LL |     takes_t(t);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/coherence-unsafe-trait-object-impl.rs:6:1
+   |
+LL | trait Trait: Sized {
+   | ^^^^^^^^^^^^^^^^^^
 note: required by a bound in `takes_t`
   --> $DIR/coherence-unsafe-trait-object-impl.rs:10:15
    |
diff --git a/tests/ui/const-generics/dont-evaluate-array-len-on-err-1.stderr b/tests/ui/const-generics/dont-evaluate-array-len-on-err-1.stderr
index cb51d9b1ea5..b3a27566026 100644
--- a/tests/ui/const-generics/dont-evaluate-array-len-on-err-1.stderr
+++ b/tests/ui/const-generics/dont-evaluate-array-len-on-err-1.stderr
@@ -3,6 +3,12 @@ error[E0277]: the trait bound `[Adt; std::mem::size_of::<Self::Assoc>()]: Foo` i
    |
 LL |         <[Adt; std::mem::size_of::<Self::Assoc>()] as Foo>::bar()
    |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `[Adt; std::mem::size_of::<Self::Assoc>()]`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/dont-evaluate-array-len-on-err-1.rs:9:1
+   |
+LL | trait Foo {
+   | ^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/tests/ui/const-generics/early/const-param-from-outer-fn.rs b/tests/ui/const-generics/early/const-param-from-outer-fn.rs
index c3b418ee3f8..ee57f3c4fc3 100644
--- a/tests/ui/const-generics/early/const-param-from-outer-fn.rs
+++ b/tests/ui/const-generics/early/const-param-from-outer-fn.rs
@@ -1,6 +1,6 @@
 fn foo<const X: u32>() {
     fn bar() -> u32 {
-        X //~ ERROR can't use generic parameters from outer function
+        X //~ ERROR can't use generic parameters from outer item
     }
 }
 
diff --git a/tests/ui/const-generics/early/const-param-from-outer-fn.stderr b/tests/ui/const-generics/early/const-param-from-outer-fn.stderr
index e3bf38b702e..826f2657905 100644
--- a/tests/ui/const-generics/early/const-param-from-outer-fn.stderr
+++ b/tests/ui/const-generics/early/const-param-from-outer-fn.stderr
@@ -1,12 +1,12 @@
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/const-param-from-outer-fn.rs:3:9
    |
 LL | fn foo<const X: u32>() {
-   |              - const parameter from outer function
+   |              - const parameter from outer item
 LL |     fn bar() -> u32 {
-   |           - help: try using a local generic parameter instead: `<X>`
+   |           - help: try introducing a local generic parameter here: `<X>`
 LL |         X
-   |         ^ use of generic parameter from outer function
+   |         ^ use of generic parameter from outer item
 
 error: aborting due to previous error
 
diff --git a/tests/ui/const-generics/generic_const_exprs/issue-85848.stderr b/tests/ui/const-generics/generic_const_exprs/issue-85848.stderr
index e50ac671eca..9391b1c1a17 100644
--- a/tests/ui/const-generics/generic_const_exprs/issue-85848.stderr
+++ b/tests/ui/const-generics/generic_const_exprs/issue-85848.stderr
@@ -6,7 +6,11 @@ LL |     writes_to_specific_path(&cap);
    |     |
    |     required by a bound introduced by this call
    |
-   = help: the trait `Delegates<U>` is implemented for `T`
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-85848.rs:4:1
+   |
+LL | trait _Contains<T> {
+   | ^^^^^^^^^^^^^^^^^^
 note: required for `&C` to implement `Contains<(), true>`
   --> $DIR/issue-85848.rs:21:12
    |
diff --git a/tests/ui/const-generics/issues/issue-86530.stderr b/tests/ui/const-generics/issues/issue-86530.stderr
index 620ed4f0fb2..d02808f7c56 100644
--- a/tests/ui/const-generics/issues/issue-86530.stderr
+++ b/tests/ui/const-generics/issues/issue-86530.stderr
@@ -6,6 +6,11 @@ LL |     z(" ");
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-86530.rs:4:1
+   |
+LL | pub trait X {
+   | ^^^^^^^^^^^
 note: required by a bound in `z`
   --> $DIR/issue-86530.rs:10:8
    |
diff --git a/tests/ui/consts/drop-maybe_uninit.rs b/tests/ui/consts/drop-maybe_uninit.rs
new file mode 100644
index 00000000000..2fdeae5f185
--- /dev/null
+++ b/tests/ui/consts/drop-maybe_uninit.rs
@@ -0,0 +1,17 @@
+// build-pass
+
+pub const fn f<T, const N: usize>(_: [std::mem::MaybeUninit<T>; N]) {}
+
+pub struct Blubb<T>(*const T);
+
+pub const fn g<T, const N: usize>(_: [Blubb<T>; N]) {}
+
+pub struct Blorb<const N: usize>([String; N]);
+
+pub const fn h(_: Blorb<0>) {}
+
+pub struct Wrap(Blorb<0>);
+
+pub const fn i(_: Wrap) {}
+
+fn main() {}
diff --git a/tests/ui/consts/issue-105536-const-val-roundtrip-ptr-eq.rs b/tests/ui/consts/issue-105536-const-val-roundtrip-ptr-eq.rs
new file mode 100644
index 00000000000..1615399be32
--- /dev/null
+++ b/tests/ui/consts/issue-105536-const-val-roundtrip-ptr-eq.rs
@@ -0,0 +1,31 @@
+// run-pass
+
+// This does not reflect a stable guarantee (we guarantee very little for equality of pointers
+// around `const`), but it would be good to understand what is happening if these assertions ever
+// fail.
+use std::ptr::NonNull;
+use std::slice::from_raw_parts;
+
+const PTR_U8: *const u8 = NonNull::dangling().as_ptr();
+const CONST_U8_REF: &[u8] = unsafe { from_raw_parts(PTR_U8, 0) };
+const CONST_U8_PTR: *const u8 = unsafe { from_raw_parts(PTR_U8, 0).as_ptr() };
+static STATIC_U8_REF: &[u8] = unsafe { from_raw_parts(PTR_U8, 0) };
+
+const PTR_U16: *const u16 = NonNull::dangling().as_ptr();
+const CONST_U16_REF: &[u16] = unsafe { from_raw_parts(PTR_U16, 0) };
+
+const fn const_u8_fn() -> &'static [u8] {
+    unsafe { from_raw_parts(PTR_U8, 0) }
+}
+
+fn main() {
+    let ptr_u8 = unsafe { from_raw_parts(PTR_U8, 0) }.as_ptr();
+    let ptr_u16 = unsafe { from_raw_parts(PTR_U16, 0) }.as_ptr();
+
+    assert_eq!(ptr_u8, PTR_U8);
+    assert_eq!(ptr_u8, CONST_U8_PTR);
+    assert_eq!(ptr_u8, const_u8_fn().as_ptr());
+    assert_eq!(ptr_u8, STATIC_U8_REF.as_ptr());
+    assert_eq!(ptr_u16, CONST_U16_REF.as_ptr());
+    assert_eq!(ptr_u8, CONST_U8_REF.as_ptr());
+}
diff --git a/tests/ui/cross/cross-fn-cache-hole.stderr b/tests/ui/cross/cross-fn-cache-hole.stderr
index 7e15562b081..79d1713934b 100644
--- a/tests/ui/cross/cross-fn-cache-hole.stderr
+++ b/tests/ui/cross/cross-fn-cache-hole.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `i32: Bar<u32>` is not satisfied
 LL |     where i32: Foo<u32, A>
    |           ^^^^^^^^^^^^^^^^ the trait `Bar<u32>` is not implemented for `i32`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/cross-fn-cache-hole.rs:11:1
+   |
+LL | trait Bar<X> { }
+   | ^^^^^^^^^^^^
    = help: see issue #48214
    = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
 
diff --git a/tests/ui/deriving/deriving-all-codegen.stdout b/tests/ui/deriving/deriving-all-codegen.stdout
index 6bfc859bfe8..3d9f8129d93 100644
--- a/tests/ui/deriving/deriving-all-codegen.stdout
+++ b/tests/ui/deriving/deriving-all-codegen.stdout
@@ -60,7 +60,7 @@ impl ::core::marker::StructuralEq for Empty { }
 impl ::core::cmp::Eq for Empty {
     #[inline]
     #[doc(hidden)]
-    #[no_coverage]
+    #[coverage(off)]
     fn assert_receiver_is_total_eq(&self) -> () {}
 }
 #[automatically_derived]
@@ -135,7 +135,7 @@ impl ::core::marker::StructuralEq for Point { }
 impl ::core::cmp::Eq for Point {
     #[inline]
     #[doc(hidden)]
-    #[no_coverage]
+    #[coverage(off)]
     fn assert_receiver_is_total_eq(&self) -> () {
         let _: ::core::cmp::AssertParamIsEq<u32>;
     }
@@ -221,7 +221,7 @@ impl ::core::marker::StructuralEq for PackedPoint { }
 impl ::core::cmp::Eq for PackedPoint {
     #[inline]
     #[doc(hidden)]
-    #[no_coverage]
+    #[coverage(off)]
     fn assert_receiver_is_total_eq(&self) -> () {
         let _: ::core::cmp::AssertParamIsEq<u32>;
     }
@@ -334,7 +334,7 @@ impl ::core::marker::StructuralEq for Big { }
 impl ::core::cmp::Eq for Big {
     #[inline]
     #[doc(hidden)]
-    #[no_coverage]
+    #[coverage(off)]
     fn assert_receiver_is_total_eq(&self) -> () {
         let _: ::core::cmp::AssertParamIsEq<u32>;
     }
@@ -500,7 +500,7 @@ impl ::core::marker::StructuralEq for Unsized { }
 impl ::core::cmp::Eq for Unsized {
     #[inline]
     #[doc(hidden)]
-    #[no_coverage]
+    #[coverage(off)]
     fn assert_receiver_is_total_eq(&self) -> () {
         let _: ::core::cmp::AssertParamIsEq<[u32]>;
     }
@@ -615,7 +615,7 @@ impl<T: ::core::cmp::Eq + Trait, U: ::core::cmp::Eq> ::core::cmp::Eq for
     Generic<T, U> where T::A: ::core::cmp::Eq {
     #[inline]
     #[doc(hidden)]
-    #[no_coverage]
+    #[coverage(off)]
     fn assert_receiver_is_total_eq(&self) -> () {
         let _: ::core::cmp::AssertParamIsEq<T>;
         let _: ::core::cmp::AssertParamIsEq<T::A>;
@@ -738,7 +738,7 @@ impl<T: ::core::cmp::Eq + ::core::marker::Copy + Trait, U: ::core::cmp::Eq +
     T::A: ::core::cmp::Eq + ::core::marker::Copy {
     #[inline]
     #[doc(hidden)]
-    #[no_coverage]
+    #[coverage(off)]
     fn assert_receiver_is_total_eq(&self) -> () {
         let _: ::core::cmp::AssertParamIsEq<T>;
         let _: ::core::cmp::AssertParamIsEq<T::A>;
@@ -821,7 +821,7 @@ impl ::core::marker::StructuralEq for Enum0 { }
 impl ::core::cmp::Eq for Enum0 {
     #[inline]
     #[doc(hidden)]
-    #[no_coverage]
+    #[coverage(off)]
     fn assert_receiver_is_total_eq(&self) -> () {}
 }
 #[automatically_derived]
@@ -892,7 +892,7 @@ impl ::core::marker::StructuralEq for Enum1 { }
 impl ::core::cmp::Eq for Enum1 {
     #[inline]
     #[doc(hidden)]
-    #[no_coverage]
+    #[coverage(off)]
     fn assert_receiver_is_total_eq(&self) -> () {
         let _: ::core::cmp::AssertParamIsEq<u32>;
     }
@@ -959,7 +959,7 @@ impl ::core::marker::StructuralEq for Fieldless1 { }
 impl ::core::cmp::Eq for Fieldless1 {
     #[inline]
     #[doc(hidden)]
-    #[no_coverage]
+    #[coverage(off)]
     fn assert_receiver_is_total_eq(&self) -> () {}
 }
 #[automatically_derived]
@@ -1034,7 +1034,7 @@ impl ::core::marker::StructuralEq for Fieldless { }
 impl ::core::cmp::Eq for Fieldless {
     #[inline]
     #[doc(hidden)]
-    #[no_coverage]
+    #[coverage(off)]
     fn assert_receiver_is_total_eq(&self) -> () {}
 }
 #[automatically_derived]
@@ -1142,7 +1142,7 @@ impl ::core::marker::StructuralEq for Mixed { }
 impl ::core::cmp::Eq for Mixed {
     #[inline]
     #[doc(hidden)]
-    #[no_coverage]
+    #[coverage(off)]
     fn assert_receiver_is_total_eq(&self) -> () {
         let _: ::core::cmp::AssertParamIsEq<u32>;
         let _: ::core::cmp::AssertParamIsEq<Option<u32>>;
@@ -1270,7 +1270,7 @@ impl ::core::marker::StructuralEq for Fielded { }
 impl ::core::cmp::Eq for Fielded {
     #[inline]
     #[doc(hidden)]
-    #[no_coverage]
+    #[coverage(off)]
     fn assert_receiver_is_total_eq(&self) -> () {
         let _: ::core::cmp::AssertParamIsEq<u32>;
         let _: ::core::cmp::AssertParamIsEq<bool>;
@@ -1393,7 +1393,7 @@ impl<T: ::core::cmp::Eq, U: ::core::cmp::Eq> ::core::cmp::Eq for
     EnumGeneric<T, U> {
     #[inline]
     #[doc(hidden)]
-    #[no_coverage]
+    #[coverage(off)]
     fn assert_receiver_is_total_eq(&self) -> () {
         let _: ::core::cmp::AssertParamIsEq<T>;
         let _: ::core::cmp::AssertParamIsEq<U>;
diff --git a/tests/ui/drop-bounds/drop-bounds-impl-drop.rs b/tests/ui/drop-bounds/drop-bounds-impl-drop.rs
index 063efc7b31a..15aebdf1bc9 100644
--- a/tests/ui/drop-bounds/drop-bounds-impl-drop.rs
+++ b/tests/ui/drop-bounds/drop-bounds-impl-drop.rs
@@ -2,13 +2,13 @@
 #![deny(drop_bounds)]
 // As a special exemption, `impl Drop` in the return position raises no error.
 // This allows a convenient way to return an unnamed drop guard.
-fn voldemort_type() -> impl Drop {
-  struct Voldemort;
-  impl Drop for Voldemort {
+fn unnameable_type() -> impl Drop {
+  struct Unnameable;
+  impl Drop for Unnameable {
     fn drop(&mut self) {}
   }
-  Voldemort
+  Unnameable
 }
 fn main() {
-  let _ = voldemort_type();
+  let _ = unnameable_type();
 }
diff --git a/tests/ui/dst/dst-bad-coerce1.stderr b/tests/ui/dst/dst-bad-coerce1.stderr
index 2c75518c298..455d15e935f 100644
--- a/tests/ui/dst/dst-bad-coerce1.stderr
+++ b/tests/ui/dst/dst-bad-coerce1.stderr
@@ -15,6 +15,11 @@ error[E0277]: the trait bound `Foo: Bar` is not satisfied
 LL |     let f3: &Fat<dyn Bar> = f2;
    |                             ^^ the trait `Bar` is not implemented for `Foo`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/dst-bad-coerce1.rs:10:1
+   |
+LL | trait Bar { fn bar(&self) {} }
+   | ^^^^^^^^^
    = note: required for the cast from `&Fat<Foo>` to `&Fat<dyn Bar>`
 
 error[E0308]: mismatched types
@@ -34,6 +39,11 @@ error[E0277]: the trait bound `Foo: Bar` is not satisfied
 LL |     let f3: &(dyn Bar,) = f2;
    |                           ^^ the trait `Bar` is not implemented for `Foo`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/dst-bad-coerce1.rs:10:1
+   |
+LL | trait Bar { fn bar(&self) {} }
+   | ^^^^^^^^^
    = note: required for the cast from `&(Foo,)` to `&(dyn Bar,)`
 
 error: aborting due to 4 previous errors
diff --git a/tests/ui/dyn-star/error.stderr b/tests/ui/dyn-star/error.stderr
index ae54b9ca707..e039bb6f1c8 100644
--- a/tests/ui/dyn-star/error.stderr
+++ b/tests/ui/dyn-star/error.stderr
@@ -3,6 +3,12 @@ error[E0277]: the trait bound `{integer}: Foo` is not satisfied
    |
 LL |     let dyn_i: dyn* Foo = i;
    |                           ^ the trait `Foo` is not implemented for `{integer}`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/error.rs:6:1
+   |
+LL | trait Foo {}
+   | ^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/tests/ui/error-codes/E0054.stderr b/tests/ui/error-codes/E0054.stderr
index 6b1092760fb..ea81f4476a7 100644
--- a/tests/ui/error-codes/E0054.stderr
+++ b/tests/ui/error-codes/E0054.stderr
@@ -1,4 +1,4 @@
-error[E0054]: cannot cast as `bool`
+error[E0054]: cannot cast `i32` as `bool`
   --> $DIR/E0054.rs:3:24
    |
 LL |     let x_is_nonzero = x as bool;
diff --git a/tests/ui/error-codes/E0220.stderr b/tests/ui/error-codes/E0220.stderr
index 11763ce788d..e03eadacae4 100644
--- a/tests/ui/error-codes/E0220.stderr
+++ b/tests/ui/error-codes/E0220.stderr
@@ -2,7 +2,7 @@ error[E0220]: associated type `F` not found for `Trait`
   --> $DIR/E0220.rs:5:22
    |
 LL | type Foo = dyn Trait<F=i32>;
-   |                      ^ associated type `F` not found
+   |                      ^ help: `Trait` has the following associated type: `Bar`
 
 error[E0191]: the value of the associated type `Bar` (from trait `Trait`) must be specified
   --> $DIR/E0220.rs:5:16
diff --git a/tests/ui/error-codes/E0277.stderr b/tests/ui/error-codes/E0277.stderr
index 440e43dff81..0b0d2b09720 100644
--- a/tests/ui/error-codes/E0277.stderr
+++ b/tests/ui/error-codes/E0277.stderr
@@ -21,6 +21,11 @@ LL |     some_func(5i32);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/E0277.rs:3:1
+   |
+LL | trait Foo {
+   | ^^^^^^^^^
 note: required by a bound in `some_func`
   --> $DIR/E0277.rs:7:17
    |
diff --git a/tests/ui/error-codes/E0401.stderr b/tests/ui/error-codes/E0401.stderr
index fa4b91cacef..928c8d11d20 100644
--- a/tests/ui/error-codes/E0401.stderr
+++ b/tests/ui/error-codes/E0401.stderr
@@ -1,26 +1,26 @@
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/E0401.rs:4:39
    |
 LL | fn foo<T>(x: T) {
-   |        - type parameter from outer function
+   |        - type parameter from outer item
 LL |     fn bfnr<U, V: Baz<U>, W: Fn()>(y: T) {
-   |             -                         ^ use of generic parameter from outer function
+   |             -                         ^ use of generic parameter from outer item
    |             |
-   |             help: try using a local generic parameter instead: `T,`
+   |             help: try introducing a local generic parameter here: `T,`
 
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/E0401.rs:9:16
    |
 LL | fn foo<T>(x: T) {
-   |        - type parameter from outer function
+   |        - type parameter from outer item
 ...
 LL |     fn baz<U,
-   |            - help: try using a local generic parameter instead: `T,`
+   |            - help: try introducing a local generic parameter here: `T,`
 ...
 LL |            (y: T) {
-   |                ^ use of generic parameter from outer function
+   |                ^ use of generic parameter from outer item
 
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/E0401.rs:24:25
    |
 LL | impl<T> Iterator for A<T> {
@@ -29,8 +29,8 @@ LL | impl<T> Iterator for A<T> {
 LL |         fn helper(sel: &Self) -> u8 {
    |                         ^^^^
    |                         |
-   |                         use of generic parameter from outer function
-   |                         use a type here instead
+   |                         use of generic parameter from outer item
+   |                         refer to the type directly here instead
 
 error[E0282]: type annotations needed
   --> $DIR/E0401.rs:11:5
diff --git a/tests/ui/error-codes/E0602.rs b/tests/ui/error-codes/E0602.rs
index 8fadce526d9..77d28838a10 100644
--- a/tests/ui/error-codes/E0602.rs
+++ b/tests/ui/error-codes/E0602.rs
@@ -1,6 +1,8 @@
 // compile-flags:-D bogus
+// check-pass
 
 // error-pattern:E0602
 // error-pattern:requested on the command line with `-D bogus`
+// error-pattern:`#[warn(unknown_lints)]` on by default
 
 fn main() {}
diff --git a/tests/ui/error-codes/E0602.stderr b/tests/ui/error-codes/E0602.stderr
index 2b372263345..60ecec7cdd7 100644
--- a/tests/ui/error-codes/E0602.stderr
+++ b/tests/ui/error-codes/E0602.stderr
@@ -1,11 +1,16 @@
-error[E0602]: unknown lint: `bogus`
+warning[E0602]: unknown lint: `bogus`
    |
    = note: requested on the command line with `-D bogus`
+   = note: `#[warn(unknown_lints)]` on by default
 
-error[E0602]: unknown lint: `bogus`
+warning[E0602]: unknown lint: `bogus`
    |
    = note: requested on the command line with `-D bogus`
 
-error: aborting due to 2 previous errors
+warning[E0602]: unknown lint: `bogus`
+   |
+   = note: requested on the command line with `-D bogus`
+
+warning: 3 warnings emitted
 
 For more information about this error, try `rustc --explain E0602`.
diff --git a/tests/ui/error-festival.stderr b/tests/ui/error-festival.stderr
index e8ee1d96942..74a2bc8d768 100644
--- a/tests/ui/error-festival.stderr
+++ b/tests/ui/error-festival.stderr
@@ -59,7 +59,7 @@ error[E0605]: non-primitive cast: `u8` as `Vec<u8>`
 LL |     x as Vec<u8>;
    |     ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
 
-error[E0054]: cannot cast as `bool`
+error[E0054]: cannot cast `{integer}` as `bool`
   --> $DIR/error-festival.rs:33:24
    |
 LL |     let x_is_nonzero = x as bool;
diff --git a/tests/ui/expr/if/bad-if-let-suggestion.rs b/tests/ui/expr/if/bad-if-let-suggestion.rs
index a8b2a283039..99d584ac7a5 100644
--- a/tests/ui/expr/if/bad-if-let-suggestion.rs
+++ b/tests/ui/expr/if/bad-if-let-suggestion.rs
@@ -4,9 +4,8 @@
 fn a() {
     if let x = 1 && i = 2 {}
     //~^ ERROR cannot find value `i` in this scope
-    //~| ERROR `let` expressions in this position are unstable
     //~| ERROR mismatched types
-    //~| ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
 }
 
 fn b() {
diff --git a/tests/ui/expr/if/bad-if-let-suggestion.stderr b/tests/ui/expr/if/bad-if-let-suggestion.stderr
index 3a53a20b453..20ac9ca76ba 100644
--- a/tests/ui/expr/if/bad-if-let-suggestion.stderr
+++ b/tests/ui/expr/if/bad-if-let-suggestion.stderr
@@ -1,4 +1,4 @@
-error: `let` expressions are not supported here
+error: expected expression, found `let` statement
   --> $DIR/bad-if-let-suggestion.rs:5:8
    |
 LL |     if let x = 1 && i = 2 {}
@@ -13,7 +13,7 @@ LL |     if let x = 1 && i = 2 {}
    |                     ^ not found in this scope
 
 error[E0425]: cannot find value `i` in this scope
-  --> $DIR/bad-if-let-suggestion.rs:13:9
+  --> $DIR/bad-if-let-suggestion.rs:12:9
    |
 LL | fn a() {
    | ------ similarly named function `a` defined here
@@ -22,7 +22,7 @@ LL |     if (i + j) = i {}
    |         ^ help: a function with a similar name exists: `a`
 
 error[E0425]: cannot find value `j` in this scope
-  --> $DIR/bad-if-let-suggestion.rs:13:13
+  --> $DIR/bad-if-let-suggestion.rs:12:13
    |
 LL | fn a() {
    | ------ similarly named function `a` defined here
@@ -31,7 +31,7 @@ LL |     if (i + j) = i {}
    |             ^ help: a function with a similar name exists: `a`
 
 error[E0425]: cannot find value `i` in this scope
-  --> $DIR/bad-if-let-suggestion.rs:13:18
+  --> $DIR/bad-if-let-suggestion.rs:12:18
    |
 LL | fn a() {
    | ------ similarly named function `a` defined here
@@ -40,7 +40,7 @@ LL |     if (i + j) = i {}
    |                  ^ help: a function with a similar name exists: `a`
 
 error[E0425]: cannot find value `x` in this scope
-  --> $DIR/bad-if-let-suggestion.rs:20:8
+  --> $DIR/bad-if-let-suggestion.rs:19:8
    |
 LL | fn a() {
    | ------ similarly named function `a` defined here
@@ -48,15 +48,6 @@ LL | fn a() {
 LL |     if x[0] = 1 {}
    |        ^ help: a function with a similar name exists: `a`
 
-error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/bad-if-let-suggestion.rs:5:8
-   |
-LL |     if let x = 1 && i = 2 {}
-   |        ^^^^^^^^^
-   |
-   = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
-   = help: add `#![feature(let_chains)]` to the crate attributes to enable
-
 error[E0308]: mismatched types
   --> $DIR/bad-if-let-suggestion.rs:5:8
    |
@@ -68,7 +59,7 @@ help: you might have meant to compare for equality
 LL |     if let x = 1 && i == 2 {}
    |                        +
 
-error: aborting due to 8 previous errors
+error: aborting due to 7 previous errors
 
-Some errors have detailed explanations: E0308, E0425, E0658.
+Some errors have detailed explanations: E0308, E0425.
 For more information about an error, try `rustc --explain E0308`.
diff --git a/tests/ui/feature-gates/feature-gate-coverage-attribute.rs b/tests/ui/feature-gates/feature-gate-coverage-attribute.rs
new file mode 100644
index 00000000000..0a463755f13
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-coverage-attribute.rs
@@ -0,0 +1,14 @@
+#![crate_type = "lib"]
+#![feature(no_coverage)] //~ ERROR feature has been removed [E0557]
+
+#[derive(PartialEq, Eq)] // ensure deriving `Eq` does not enable `feature(coverage)`
+struct Foo {
+    a: u8,
+    b: u32,
+}
+
+#[coverage(off)] //~ ERROR the `#[coverage]` attribute is an experimental feature
+fn requires_feature_coverage() -> bool {
+    let bar = Foo { a: 0, b: 0 };
+    bar == Foo { a: 0, b: 0 }
+}
diff --git a/tests/ui/feature-gates/feature-gate-coverage-attribute.stderr b/tests/ui/feature-gates/feature-gate-coverage-attribute.stderr
new file mode 100644
index 00000000000..0131a19a39d
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-coverage-attribute.stderr
@@ -0,0 +1,21 @@
+error[E0557]: feature has been removed
+  --> $DIR/feature-gate-coverage-attribute.rs:2:12
+   |
+LL | #![feature(no_coverage)]
+   |            ^^^^^^^^^^^ feature has been removed
+   |
+   = note: renamed to `coverage_attribute`
+
+error[E0658]: the `#[coverage]` attribute is an experimental feature
+  --> $DIR/feature-gate-coverage-attribute.rs:10:1
+   |
+LL | #[coverage(off)]
+   | ^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #84605 <https://github.com/rust-lang/rust/issues/84605> for more information
+   = help: add `#![feature(coverage_attribute)]` to the crate attributes to enable
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0557, E0658.
+For more information about an error, try `rustc --explain E0557`.
diff --git a/tests/ui/feature-gates/feature-gate-no_coverage.rs b/tests/ui/feature-gates/feature-gate-no_coverage.rs
deleted file mode 100644
index fd4c6f76059..00000000000
--- a/tests/ui/feature-gates/feature-gate-no_coverage.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-#![crate_type = "lib"]
-
-#[derive(PartialEq, Eq)] // ensure deriving `Eq` does not enable `feature(no_coverage)`
-struct Foo {
-    a: u8,
-    b: u32,
-}
-
-#[no_coverage] //~ ERROR the `#[no_coverage]` attribute is an experimental feature
-fn requires_feature_no_coverage() -> bool {
-    let bar = Foo { a: 0, b: 0 };
-    bar == Foo { a: 0, b: 0 }
-}
diff --git a/tests/ui/feature-gates/feature-gate-no_coverage.stderr b/tests/ui/feature-gates/feature-gate-no_coverage.stderr
deleted file mode 100644
index f7167e0b771..00000000000
--- a/tests/ui/feature-gates/feature-gate-no_coverage.stderr
+++ /dev/null
@@ -1,12 +0,0 @@
-error[E0658]: the `#[no_coverage]` attribute is an experimental feature
-  --> $DIR/feature-gate-no_coverage.rs:9:1
-   |
-LL | #[no_coverage]
-   | ^^^^^^^^^^^^^^
-   |
-   = note: see issue #84605 <https://github.com/rust-lang/rust/issues/84605> for more information
-   = help: add `#![feature(no_coverage)]` to the crate attributes to enable
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/feature-gates/print-with-path.cfg.stderr b/tests/ui/feature-gates/print-with-path.cfg.stderr
deleted file mode 100644
index a6c51baa320..00000000000
--- a/tests/ui/feature-gates/print-with-path.cfg.stderr
+++ /dev/null
@@ -1,2 +0,0 @@
-error: the `-Z unstable-options` flag must also be passed to enable the path print option
-
diff --git a/tests/ui/feature-gates/print-with-path.rs b/tests/ui/feature-gates/print-with-path.rs
deleted file mode 100644
index f929c14c218..00000000000
--- a/tests/ui/feature-gates/print-with-path.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-// check-fail
-// revisions: cfg target-features target-cpus
-// [cfg]compile-flags: --print cfg=cfg.txt
-// [target-cpus]compile-flags: --print target-cpu=target_cpu.txt
-// [target-features]compile-flags: --print target-features=target_features.txt
-
-fn main() {}
diff --git a/tests/ui/feature-gates/print-with-path.target-cpus.stderr b/tests/ui/feature-gates/print-with-path.target-cpus.stderr
deleted file mode 100644
index a6c51baa320..00000000000
--- a/tests/ui/feature-gates/print-with-path.target-cpus.stderr
+++ /dev/null
@@ -1,2 +0,0 @@
-error: the `-Z unstable-options` flag must also be passed to enable the path print option
-
diff --git a/tests/ui/feature-gates/print-with-path.target-features.stderr b/tests/ui/feature-gates/print-with-path.target-features.stderr
deleted file mode 100644
index a6c51baa320..00000000000
--- a/tests/ui/feature-gates/print-with-path.target-features.stderr
+++ /dev/null
@@ -1,2 +0,0 @@
-error: the `-Z unstable-options` flag must also be passed to enable the path print option
-
diff --git a/tests/ui/fn/keyword-order.stderr b/tests/ui/fn/keyword-order.stderr
index d3b140c8528..97d8f91b1ee 100644
--- a/tests/ui/fn/keyword-order.stderr
+++ b/tests/ui/fn/keyword-order.stderr
@@ -11,6 +11,8 @@ error: expected item, found keyword `pub`
    |
 LL | default pub const async unsafe extern fn err() {}
    |         ^^^ expected item
+   |
+   = note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/generic-associated-types/issue-101020.stderr b/tests/ui/generic-associated-types/issue-101020.stderr
index 5c8db617c17..91967fb8509 100644
--- a/tests/ui/generic-associated-types/issue-101020.stderr
+++ b/tests/ui/generic-associated-types/issue-101020.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `for<'a> &'a mut (): Foo<&'a mut ()>` is not satis
 LL |     (&mut EmptyIter).consume(());
    |                      ^^^^^^^ the trait `for<'a> Foo<&'a mut ()>` is not implemented for `&'a mut ()`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-101020.rs:28:1
+   |
+LL | trait Foo<T> {}
+   | ^^^^^^^^^^^^
 note: required for `&'a mut ()` to implement `for<'a> FuncInput<'a, &'a mut ()>`
   --> $DIR/issue-101020.rs:27:20
    |
diff --git a/tests/ui/generics/issue-94432-garbage-ice.rs b/tests/ui/generics/issue-94432-garbage-ice.rs
index d0709e2d2a4..4ddb3a7e9f8 100644
--- a/tests/ui/generics/issue-94432-garbage-ice.rs
+++ b/tests/ui/generics/issue-94432-garbage-ice.rs
@@ -4,7 +4,7 @@
 
 fn�a<e>(){fn�p(){e}} //~ ERROR unknown start of token: \u{fffd}
 //~^ ERROR unknown start of token: \u{fffd}
-//~^^ ERROR can't use generic parameters from outer function [E0401]
+//~^^ ERROR can't use generic parameters from outer item [E0401]
 //~^^^ WARN type parameter `e` should have an upper camel case name
 
 fn main(){}
diff --git a/tests/ui/generics/issue-98432.rs b/tests/ui/generics/issue-98432.rs
index 780c50d6ffa..c31dea76c09 100644
--- a/tests/ui/generics/issue-98432.rs
+++ b/tests/ui/generics/issue-98432.rs
@@ -2,7 +2,7 @@ struct Struct<T>(T);
 
 impl<T> Struct<T> {
     const CONST: fn() = || {
-        struct _Obligation where T:; //~ ERROR can't use generic parameters from outer function
+        struct _Obligation where T:; //~ ERROR can't use generic parameters from outer item
     };
 }
 
diff --git a/tests/ui/generics/issue-98432.stderr b/tests/ui/generics/issue-98432.stderr
index c7b5c33618d..0736d94106e 100644
--- a/tests/ui/generics/issue-98432.stderr
+++ b/tests/ui/generics/issue-98432.stderr
@@ -1,13 +1,13 @@
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/issue-98432.rs:5:34
    |
 LL | impl<T> Struct<T> {
-   |      - type parameter from outer function
+   |      - type parameter from outer item
 LL |     const CONST: fn() = || {
 LL |         struct _Obligation where T:;
-   |                           -      ^ use of generic parameter from outer function
+   |                           -      ^ use of generic parameter from outer item
    |                           |
-   |                           help: try using a local generic parameter instead: `<T>`
+   |                           help: try introducing a local generic parameter here: `<T>`
 
 error: aborting due to previous error
 
diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-89118.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-89118.stderr
index edef6ccd34e..7fe803550bd 100644
--- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-89118.stderr
+++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-89118.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `for<'a> &'a (): BufferMut` is not satisfied
 LL |     C: StackContext,
    |        ^^^^^^^^^^^^ the trait `for<'a> BufferMut` is not implemented for `&'a ()`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-89118.rs:1:1
+   |
+LL | trait BufferMut {}
+   | ^^^^^^^^^^^^^^^
 note: required for `Ctx<()>` to implement `for<'a> BufferUdpStateContext<&'a ()>`
   --> $DIR/issue-89118.rs:5:23
    |
@@ -26,6 +31,11 @@ error[E0277]: the trait bound `for<'a> &'a (): BufferMut` is not satisfied
 LL | impl<C> EthernetWorker<C> {}
    |         ^^^^^^^^^^^^^^^^^ the trait `for<'a> BufferMut` is not implemented for `&'a ()`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-89118.rs:1:1
+   |
+LL | trait BufferMut {}
+   | ^^^^^^^^^^^^^^^
 note: required for `Ctx<()>` to implement `for<'a> BufferUdpStateContext<&'a ()>`
   --> $DIR/issue-89118.rs:5:23
    |
@@ -48,6 +58,11 @@ error[E0277]: the trait bound `for<'a> &'a (): BufferMut` is not satisfied
 LL |     type Handler = Ctx<C::Dispatcher>;
    |                    ^^^^^^^^^^^^^^^^^^ the trait `for<'a> BufferMut` is not implemented for `&'a ()`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-89118.rs:1:1
+   |
+LL | trait BufferMut {}
+   | ^^^^^^^^^^^^^^^
 note: required for `Ctx<()>` to implement `for<'a> BufferUdpStateContext<&'a ()>`
   --> $DIR/issue-89118.rs:5:23
    |
diff --git a/tests/ui/impl-trait/async_scope_creep.rs b/tests/ui/impl-trait/async_scope_creep.rs
index 7a9d64d339f..9a8831a299e 100644
--- a/tests/ui/impl-trait/async_scope_creep.rs
+++ b/tests/ui/impl-trait/async_scope_creep.rs
@@ -1,6 +1,7 @@
 #![feature(type_alias_impl_trait)]
 // edition:2021
-// check-pass
+//[rpit] check-pass
+// revisions: tait rpit
 
 struct Pending {}
 
@@ -12,15 +13,23 @@ impl AsyncRead for i32 {}
 
 type PendingReader<'a> = impl AsyncRead + 'a;
 
-type OpeningReadFuture<'a> =
-    impl std::future::Future<Output = Result<PendingReader<'a>, CantOpen>>;
+#[cfg(tait)]
+type OpeningReadFuture<'a> = impl std::future::Future<Output = Result<PendingReader<'a>, CantOpen>>;
 
 impl Pending {
     async fn read(&mut self) -> Result<impl AsyncRead + '_, CantOpen> {
         Ok(42)
     }
 
+    #[cfg(tait)]
     fn read_fut(&mut self) -> OpeningReadFuture<'_> {
+        self.read() //[tait]~ ERROR: cannot satisfy `impl AsyncRead + 'a == PendingReader<'a>`
+    }
+
+    #[cfg(rpit)]
+    fn read_fut(
+        &mut self,
+    ) -> impl std::future::Future<Output = Result<PendingReader<'_>, CantOpen>> {
         self.read()
     }
 }
diff --git a/tests/ui/impl-trait/async_scope_creep.tait.stderr b/tests/ui/impl-trait/async_scope_creep.tait.stderr
new file mode 100644
index 00000000000..165096a0574
--- /dev/null
+++ b/tests/ui/impl-trait/async_scope_creep.tait.stderr
@@ -0,0 +1,9 @@
+error[E0284]: type annotations needed: cannot satisfy `impl AsyncRead + 'a == PendingReader<'a>`
+  --> $DIR/async_scope_creep.rs:26:9
+   |
+LL |         self.read()
+   |         ^^^^^^^^^^^ cannot satisfy `impl AsyncRead + 'a == PendingReader<'a>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0284`.
diff --git a/tests/ui/impl-trait/in-trait/auxiliary/rpitit.rs b/tests/ui/impl-trait/in-trait/auxiliary/rpitit.rs
index cfc2193f633..6e99402113a 100644
--- a/tests/ui/impl-trait/in-trait/auxiliary/rpitit.rs
+++ b/tests/ui/impl-trait/in-trait/auxiliary/rpitit.rs
@@ -1,4 +1,4 @@
-#![feature(return_position_impl_trait_in_trait)]
+#![feature(return_position_impl_trait_in_trait, lint_reasons)]
 
 use std::ops::Deref;
 
@@ -8,6 +8,7 @@ pub trait Foo {
 
 pub struct Foreign;
 impl Foo for Foreign {
+    #[expect(refining_impl_trait)]
     fn bar(self) -> &'static () {
         &()
     }
diff --git a/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.rs b/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.rs
index ff7ad4bf389..fbbbb8585d1 100644
--- a/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.rs
+++ b/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.rs
@@ -2,7 +2,7 @@
 
 #![feature(return_position_impl_trait_in_trait)]
 
-trait Iterable {
+pub trait Iterable {
     type Item<'a>
     where
         Self: 'a;
@@ -17,6 +17,7 @@ impl<'a, I: 'a + Iterable> Iterable for &'a I {
     //~^ ERROR impl has stricter requirements than trait
 
     fn iter(&self) -> impl 'a + Iterator<Item = I::Item<'a>> {
+        //~^ WARN impl trait in impl method signature does not match trait method signature
         (*self).iter()
     }
 }
diff --git a/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.stderr b/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.stderr
index 106b8a7c804..a5fb338ea4e 100644
--- a/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.stderr
+++ b/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.stderr
@@ -12,6 +12,22 @@ help: copy the `where` clause predicates from the trait
 LL |     where Self: 'b;
    |     ~~~~~~~~~~~~~~
 
-error: aborting due to previous error
+warning: impl trait in impl method signature does not match trait method signature
+  --> $DIR/bad-item-bound-within-rpitit.rs:19:28
+   |
+LL |     fn iter(&self) -> impl '_ + Iterator<Item = Self::Item<'_>>;
+   |                       ----------------------------------------- return type from trait method defined here
+...
+LL |     fn iter(&self) -> impl 'a + Iterator<Item = I::Item<'a>> {
+   |                            ^^ this bound is stronger than that defined on the trait
+   |
+   = note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
+   = note: `#[warn(refining_impl_trait)]` on by default
+help: replace the return type so that it matches the trait
+   |
+LL |     fn iter(&self) -> impl Iterator<Item = <Self as Iterable>::Item<'_>> + '_ {
+   |                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to previous error; 1 warning emitted
 
 For more information about this error, try `rustc --explain E0276`.
diff --git a/tests/ui/impl-trait/in-trait/deep-match-works.rs b/tests/ui/impl-trait/in-trait/deep-match-works.rs
index 78cff97c616..fc290f11f9d 100644
--- a/tests/ui/impl-trait/in-trait/deep-match-works.rs
+++ b/tests/ui/impl-trait/in-trait/deep-match-works.rs
@@ -1,15 +1,16 @@
 // check-pass
 
-#![feature(return_position_impl_trait_in_trait)]
+#![feature(return_position_impl_trait_in_trait, lint_reasons)]
 #![allow(incomplete_features)]
 
-struct Wrapper<T>(T);
+pub struct Wrapper<T>(T);
 
-trait Foo {
+pub trait Foo {
     fn bar() -> Wrapper<impl Sized>;
 }
 
 impl Foo for () {
+    #[expect(refining_impl_trait)]
     fn bar() -> Wrapper<i32> {
         Wrapper(0)
     }
diff --git a/tests/ui/impl-trait/in-trait/foreign.rs b/tests/ui/impl-trait/in-trait/foreign.rs
index b0c93a02935..6285d7786d5 100644
--- a/tests/ui/impl-trait/in-trait/foreign.rs
+++ b/tests/ui/impl-trait/in-trait/foreign.rs
@@ -1,14 +1,25 @@
 // check-pass
 // aux-build: rpitit.rs
 
+#![feature(lint_reasons)]
+
 extern crate rpitit;
 
 use rpitit::{Foo, Foreign};
 use std::sync::Arc;
 
 // Implement an RPITIT from another crate.
-struct Local;
+pub struct Local;
 impl Foo for Local {
+    #[expect(refining_impl_trait)]
+    fn bar(self) -> Arc<String> {
+        Arc::new(String::new())
+    }
+}
+
+struct LocalIgnoreRefining;
+impl Foo for LocalIgnoreRefining {
+    #[deny(refining_impl_trait)]
     fn bar(self) -> Arc<String> {
         Arc::new(String::new())
     }
@@ -23,4 +34,5 @@ fn main() {
     let &() = Foreign.bar();
 
     let x: Arc<String> = Local.bar();
+    let x: Arc<String> = LocalIgnoreRefining.bar();
 }
diff --git a/tests/ui/impl-trait/in-trait/issue-102571.rs b/tests/ui/impl-trait/in-trait/issue-102571.rs
index 61c91e64417..ccb53031c44 100644
--- a/tests/ui/impl-trait/in-trait/issue-102571.rs
+++ b/tests/ui/impl-trait/in-trait/issue-102571.rs
@@ -8,14 +8,6 @@ trait Foo {
     fn bar(self) -> impl Deref<Target = impl Display + ?Sized>;
 }
 
-struct A;
-
-impl Foo for A {
-    fn bar(self) -> &'static str {
-        "Hello, world"
-    }
-}
-
 fn foo<T: Foo>(t: T) {
     let () = t.bar();
     //~^ ERROR mismatched types
diff --git a/tests/ui/impl-trait/in-trait/issue-102571.stderr b/tests/ui/impl-trait/in-trait/issue-102571.stderr
index 87219941d91..594b9ae9cd6 100644
--- a/tests/ui/impl-trait/in-trait/issue-102571.stderr
+++ b/tests/ui/impl-trait/in-trait/issue-102571.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/issue-102571.rs:20:9
+  --> $DIR/issue-102571.rs:12:9
    |
 LL |     let () = t.bar();
    |         ^^   ------- this expression has type `impl Deref<Target = impl std::fmt::Display + ?Sized>`
diff --git a/tests/ui/impl-trait/in-trait/nested-rpitit.rs b/tests/ui/impl-trait/in-trait/nested-rpitit.rs
index 65285e3a3cc..58ba1acaf14 100644
--- a/tests/ui/impl-trait/in-trait/nested-rpitit.rs
+++ b/tests/ui/impl-trait/in-trait/nested-rpitit.rs
@@ -1,26 +1,28 @@
 // check-pass
 
-#![feature(return_position_impl_trait_in_trait)]
+#![feature(return_position_impl_trait_in_trait, lint_reasons)]
 #![allow(incomplete_features)]
 
 use std::fmt::Display;
 use std::ops::Deref;
 
-trait Foo {
+pub trait Foo {
     fn bar(self) -> impl Deref<Target = impl Display + ?Sized>;
 }
 
-struct A;
+pub struct A;
 
 impl Foo for A {
+    #[expect(refining_impl_trait)]
     fn bar(self) -> &'static str {
         "Hello, world"
     }
 }
 
-struct B;
+pub struct B;
 
 impl Foo for B {
+    #[expect(refining_impl_trait)]
     fn bar(self) -> Box<i32> {
         Box::new(42)
     }
diff --git a/tests/ui/impl-trait/in-trait/object-safety.rs b/tests/ui/impl-trait/in-trait/object-safety.rs
index 9a231e59b09..d1c9fba4e99 100644
--- a/tests/ui/impl-trait/in-trait/object-safety.rs
+++ b/tests/ui/impl-trait/in-trait/object-safety.rs
@@ -8,7 +8,7 @@ trait Foo {
 }
 
 impl Foo for u32 {
-    fn baz(&self) -> u32 {
+    fn baz(&self) -> impl Debug {
         32
     }
 }
diff --git a/tests/ui/impl-trait/in-trait/refine.rs b/tests/ui/impl-trait/in-trait/refine.rs
new file mode 100644
index 00000000000..a91f9b3e722
--- /dev/null
+++ b/tests/ui/impl-trait/in-trait/refine.rs
@@ -0,0 +1,48 @@
+#![feature(return_position_impl_trait_in_trait, async_fn_in_trait)]
+#![deny(refining_impl_trait)]
+
+pub trait Foo {
+    fn bar() -> impl Sized;
+}
+
+pub struct A;
+impl Foo for A {
+    fn bar() -> impl Copy {}
+    //~^ ERROR impl method signature does not match trait method signature
+}
+
+pub struct B;
+impl Foo for B {
+    fn bar() {}
+    //~^ ERROR impl method signature does not match trait method signature
+}
+
+pub struct C;
+impl Foo for C {
+    fn bar() -> () {}
+    //~^ ERROR impl method signature does not match trait method signature
+}
+
+struct Private;
+impl Foo for Private {
+    fn bar() -> () {}
+}
+
+pub trait Arg<A> {
+    fn bar() -> impl Sized;
+}
+impl Arg<Private> for A {
+    fn bar() -> () {}
+}
+
+pub trait Late {
+    fn bar<'a>(&'a self) -> impl Sized + 'a;
+}
+
+pub struct D;
+impl Late for D {
+    fn bar(&self) -> impl Copy + '_ {}
+    //~^ ERROR impl method signature does not match trait method signature
+}
+
+fn main() {}
diff --git a/tests/ui/impl-trait/in-trait/refine.stderr b/tests/ui/impl-trait/in-trait/refine.stderr
new file mode 100644
index 00000000000..29aa08e25bb
--- /dev/null
+++ b/tests/ui/impl-trait/in-trait/refine.stderr
@@ -0,0 +1,67 @@
+error: impl trait in impl method signature does not match trait method signature
+  --> $DIR/refine.rs:10:22
+   |
+LL |     fn bar() -> impl Sized;
+   |                 ---------- return type from trait method defined here
+...
+LL |     fn bar() -> impl Copy {}
+   |                      ^^^^ this bound is stronger than that defined on the trait
+   |
+   = note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
+note: the lint level is defined here
+  --> $DIR/refine.rs:2:9
+   |
+LL | #![deny(refining_impl_trait)]
+   |         ^^^^^^^^^^^^^^^^^^^
+help: replace the return type so that it matches the trait
+   |
+LL |     fn bar() -> impl Sized {}
+   |                 ~~~~~~~~~~
+
+error: impl trait in impl method signature does not match trait method signature
+  --> $DIR/refine.rs:16:5
+   |
+LL |     fn bar() -> impl Sized;
+   |                 ---------- return type from trait method defined here
+...
+LL |     fn bar() {}
+   |     ^^^^^^^^
+   |
+   = note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
+help: replace the return type so that it matches the trait
+   |
+LL |     fn bar() -> impl Sized {}
+   |              +++++++++++++
+
+error: impl trait in impl method signature does not match trait method signature
+  --> $DIR/refine.rs:22:17
+   |
+LL |     fn bar() -> impl Sized;
+   |                 ---------- return type from trait method defined here
+...
+LL |     fn bar() -> () {}
+   |                 ^^
+   |
+   = note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
+help: replace the return type so that it matches the trait
+   |
+LL |     fn bar() -> impl Sized {}
+   |                 ~~~~~~~~~~
+
+error: impl trait in impl method signature does not match trait method signature
+  --> $DIR/refine.rs:44:27
+   |
+LL |     fn bar<'a>(&'a self) -> impl Sized + 'a;
+   |                             --------------- return type from trait method defined here
+...
+LL |     fn bar(&self) -> impl Copy + '_ {}
+   |                           ^^^^ this bound is stronger than that defined on the trait
+   |
+   = note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
+help: replace the return type so that it matches the trait
+   |
+LL |     fn bar(&self) -> impl Sized + '_ {}
+   |                      ~~~~~~~~~~~~~~~
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/impl-trait/in-trait/reveal.rs b/tests/ui/impl-trait/in-trait/reveal.rs
index d6ede1cc495..b1b46d75b8f 100644
--- a/tests/ui/impl-trait/in-trait/reveal.rs
+++ b/tests/ui/impl-trait/in-trait/reveal.rs
@@ -1,13 +1,14 @@
 // check-pass
 
-#![feature(return_position_impl_trait_in_trait)]
+#![feature(return_position_impl_trait_in_trait, lint_reasons)]
 #![allow(incomplete_features)]
 
-trait Foo {
+pub trait Foo {
     fn f() -> Box<impl Sized>;
 }
 
 impl Foo for () {
+    #[expect(refining_impl_trait)]
     fn f() -> Box<String> {
         Box::new(String::new())
     }
diff --git a/tests/ui/impl-trait/in-trait/rpitit-shadowed-by-missing-adt.rs b/tests/ui/impl-trait/in-trait/rpitit-shadowed-by-missing-adt.rs
index 7682884f879..44a2b430344 100644
--- a/tests/ui/impl-trait/in-trait/rpitit-shadowed-by-missing-adt.rs
+++ b/tests/ui/impl-trait/in-trait/rpitit-shadowed-by-missing-adt.rs
@@ -1,6 +1,6 @@
 // issue: 113903
 
-#![feature(return_position_impl_trait_in_trait)]
+#![feature(return_position_impl_trait_in_trait, lint_reasons)]
 
 use std::ops::Deref;
 
@@ -10,6 +10,7 @@ pub trait Tr {
 }
 
 impl Tr for () {
+    #[expect(refining_impl_trait)]
     fn w() -> &'static () {
         &()
     }
diff --git a/tests/ui/impl-trait/in-trait/signature-mismatch.failure.stderr b/tests/ui/impl-trait/in-trait/signature-mismatch.failure.stderr
index 186580f5756..468cf12f1bc 100644
--- a/tests/ui/impl-trait/in-trait/signature-mismatch.failure.stderr
+++ b/tests/ui/impl-trait/in-trait/signature-mismatch.failure.stderr
@@ -1,5 +1,5 @@
 error[E0623]: lifetime mismatch
-  --> $DIR/signature-mismatch.rs:77:10
+  --> $DIR/signature-mismatch.rs:79:10
    |
 LL |         &'a self,
    |         -------- this parameter and the return type are declared with different lifetimes...
diff --git a/tests/ui/impl-trait/in-trait/signature-mismatch.rs b/tests/ui/impl-trait/in-trait/signature-mismatch.rs
index c84a3b8f46b..685c0f06e88 100644
--- a/tests/ui/impl-trait/in-trait/signature-mismatch.rs
+++ b/tests/ui/impl-trait/in-trait/signature-mismatch.rs
@@ -2,18 +2,17 @@
 // revisions: success failure
 //[success] check-pass
 
-#![feature(return_position_impl_trait_in_trait)]
-#![allow(incomplete_features)]
+#![feature(return_position_impl_trait_in_trait, lint_reasons)]
 
 use std::future::Future;
 
-trait Captures<'a> {}
+pub trait Captures<'a> {}
 impl<T> Captures<'_> for T {}
 
-trait Captures2<'a, 'b> {}
+pub trait Captures2<'a, 'b> {}
 impl<T> Captures2<'_, '_> for T {}
 
-trait AsyncTrait {
+pub trait AsyncTrait {
     #[cfg(success)]
     fn async_fn(&self, buff: &[u8]) -> impl Future<Output = Vec<u8>>;
 
@@ -45,6 +44,7 @@ impl AsyncTrait for Struct {
     // Does not capture more lifetimes that trait def'n, since trait def'n
     // implicitly captures all in-scope lifetimes.
     #[cfg(success)]
+    #[expect(refining_impl_trait)]
     fn async_fn<'a>(&self, buff: &'a [u8]) -> impl Future<Output = Vec<u8>> + 'a {
         async move { buff.to_vec() }
     }
@@ -52,6 +52,7 @@ impl AsyncTrait for Struct {
     // Does not capture more lifetimes that trait def'n, since trait def'n
     // implicitly captures all in-scope lifetimes.
     #[cfg(success)]
+    #[expect(refining_impl_trait)]
     fn async_fn_early<'a: 'a>(&self, buff: &'a [u8]) -> impl Future<Output = Vec<u8>> + 'a {
         async move { buff.to_vec() }
     }
@@ -59,6 +60,7 @@ impl AsyncTrait for Struct {
     // Does not capture more lifetimes that trait def'n, since trait def'n
     // implicitly captures all in-scope lifetimes.
     #[cfg(success)]
+    #[expect(refining_impl_trait)]
     fn async_fn_multiple<'a, 'b>(
         &'a self,
         buff: &'b [u8],
diff --git a/tests/ui/impl-trait/in-trait/specialization-substs-remap.rs b/tests/ui/impl-trait/in-trait/specialization-substs-remap.rs
index c9ee877db8e..41fc285883a 100644
--- a/tests/ui/impl-trait/in-trait/specialization-substs-remap.rs
+++ b/tests/ui/impl-trait/in-trait/specialization-substs-remap.rs
@@ -1,10 +1,10 @@
 // check-pass
 
 #![feature(specialization)]
-#![feature(return_position_impl_trait_in_trait)]
+#![feature(return_position_impl_trait_in_trait, lint_reasons)]
 #![allow(incomplete_features)]
 
-trait Foo {
+pub trait Foo {
     fn bar(&self) -> impl Sized;
 }
 
@@ -12,6 +12,7 @@ impl<U> Foo for U
 where
     U: Copy,
 {
+    #[expect(refining_impl_trait)]
     fn bar(&self) -> U {
         *self
     }
diff --git a/tests/ui/impl-trait/in-trait/success.rs b/tests/ui/impl-trait/in-trait/success.rs
index 4cbe682b46f..7d415ea17a4 100644
--- a/tests/ui/impl-trait/in-trait/success.rs
+++ b/tests/ui/impl-trait/in-trait/success.rs
@@ -1,29 +1,32 @@
 // check-pass
 
-#![feature(return_position_impl_trait_in_trait)]
+#![feature(return_position_impl_trait_in_trait, lint_reasons)]
 #![allow(incomplete_features)]
 
 use std::fmt::Display;
 
-trait Foo {
+pub trait Foo {
     fn bar(&self) -> impl Display;
 }
 
 impl Foo for i32 {
+    #[expect(refining_impl_trait)]
     fn bar(&self) -> i32 {
         *self
     }
 }
 
 impl Foo for &'static str {
+    #[expect(refining_impl_trait)]
     fn bar(&self) -> &'static str {
         *self
     }
 }
 
-struct Yay;
+pub struct Yay;
 
 impl Foo for Yay {
+    #[expect(refining_impl_trait)]
     fn bar(&self) -> String {
         String::from(":^)")
     }
diff --git a/tests/ui/impl-trait/lifetime-ambiguity-regression.rs b/tests/ui/impl-trait/lifetime-ambiguity-regression.rs
new file mode 100644
index 00000000000..ce6ae3786e1
--- /dev/null
+++ b/tests/ui/impl-trait/lifetime-ambiguity-regression.rs
@@ -0,0 +1,13 @@
+//! This test shows a situation where through subtle compiler changes we can
+//! suddenly infer a different lifetime in the hidden type, and thus not meet
+//! the opaque type bounds anymore. In this case `'a` and `'b` are equal, so
+//! picking either is fine, but then we'll fail an identity check of the hidden
+//! type and the expected hidden type.
+
+// check-pass
+
+fn test<'a: 'b, 'b: 'a>() -> impl IntoIterator<Item = (&'a u8, impl Into<(&'b u8, &'a u8)>)> {
+    None::<(_, (_, _))>
+}
+
+fn main() {}
diff --git a/tests/ui/imports/import-after-macro-expand-10.rs b/tests/ui/imports/import-after-macro-expand-10.rs
new file mode 100644
index 00000000000..b3520c42dfc
--- /dev/null
+++ b/tests/ui/imports/import-after-macro-expand-10.rs
@@ -0,0 +1,17 @@
+// check-pass
+
+mod b {
+    pub mod http {
+        pub struct HeaderMap;
+    }
+
+    pub use self::http::*;
+    #[derive(Debug)]
+    pub struct HeaderMap;
+}
+
+use crate::b::*;
+
+fn main() {
+    let h: crate::b::HeaderMap = HeaderMap;
+}
diff --git a/tests/ui/imports/import-after-macro-expand-11.rs b/tests/ui/imports/import-after-macro-expand-11.rs
new file mode 100644
index 00000000000..2b81dfc236a
--- /dev/null
+++ b/tests/ui/imports/import-after-macro-expand-11.rs
@@ -0,0 +1,21 @@
+// check-pass
+
+#[derive(Debug)]
+struct H;
+
+mod p {
+    use super::*;
+
+    #[derive(Clone)]
+    struct H;
+
+    mod t {
+        use super::*;
+
+        fn f() {
+           let h: crate::p::H = H;
+        }
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/imports/import-after-macro-expand-12.rs b/tests/ui/imports/import-after-macro-expand-12.rs
new file mode 100644
index 00000000000..f92e8c12fca
--- /dev/null
+++ b/tests/ui/imports/import-after-macro-expand-12.rs
@@ -0,0 +1,34 @@
+// check-pass
+// https://github.com/rust-lang/rust/issues/115377
+
+use module::*;
+
+mod module {
+    pub enum B {}
+    impl B {
+        pub const ASSOC: u8 = 0;
+    }
+}
+
+#[derive()]
+pub enum B {}
+impl B {
+    pub const ASSOC: u16 = 0;
+}
+
+macro_rules! m {
+    ($right:expr) => {
+        $right
+    };
+}
+
+fn main() {
+    let a: u16 = {
+        use self::*;
+        B::ASSOC
+    };
+    let b: u16 = m!({
+        use self::*;
+        B::ASSOC
+    });
+}
diff --git a/tests/ui/imports/import-after-macro-expand-13.rs b/tests/ui/imports/import-after-macro-expand-13.rs
new file mode 100644
index 00000000000..fd640002c3e
--- /dev/null
+++ b/tests/ui/imports/import-after-macro-expand-13.rs
@@ -0,0 +1,22 @@
+// check-pass
+// similar as `import-after-macro-expand-6.rs`
+
+use crate::a::HeaderMap;
+
+mod b {
+    pub mod http {
+        pub struct HeaderMap;
+    }
+
+    pub use self::http::*;
+    #[derive(Debug)]
+    pub struct HeaderMap;
+}
+
+mod a {
+    pub use crate::b::*;
+}
+
+fn main() {
+    let h: crate::b::HeaderMap = HeaderMap;
+}
diff --git a/tests/ui/imports/import-after-macro-expand-14.rs b/tests/ui/imports/import-after-macro-expand-14.rs
new file mode 100644
index 00000000000..4d461a0e20c
--- /dev/null
+++ b/tests/ui/imports/import-after-macro-expand-14.rs
@@ -0,0 +1,22 @@
+// check-pass
+
+use crate::a::HeaderMap;
+
+mod b {
+    pub mod http {
+        #[derive(Clone)]
+        pub struct HeaderMap;
+    }
+
+    pub use self::http::*;
+    #[derive(Debug)]
+    pub struct HeaderMap;
+}
+
+mod a {
+    pub use crate::b::*;
+}
+
+fn main() {
+    let h: crate::b::HeaderMap = HeaderMap;
+}
diff --git a/tests/ui/imports/import-after-macro-expand-2.rs b/tests/ui/imports/import-after-macro-expand-2.rs
index b3996d48840..ff773fc8272 100644
--- a/tests/ui/imports/import-after-macro-expand-2.rs
+++ b/tests/ui/imports/import-after-macro-expand-2.rs
@@ -12,9 +12,7 @@ mod tests {
     use super::*;
 
     fn test_thing() {
-        let thing: crate::thing::Thing = Thing::Bar;
-        // FIXME: `thing` should refer to `crate::Thing`,
-        // FIXME: but doesn't currently refer to it due to backward compatibility
+        let thing: crate::Thing = Thing::Foo;
     }
 }
 
diff --git a/tests/ui/imports/import-after-macro-expand-4.rs b/tests/ui/imports/import-after-macro-expand-4.rs
index 02cc3f01af9..fc0a232a93c 100644
--- a/tests/ui/imports/import-after-macro-expand-4.rs
+++ b/tests/ui/imports/import-after-macro-expand-4.rs
@@ -1,3 +1,4 @@
+// check-pass
 // https://github.com/rust-lang/rust/pull/113242#issuecomment-1616034904
 // similar with `import-after-macro-expand-2.rs`
 
@@ -10,16 +11,6 @@ pub use a::*;
 mod c {
     use crate::*;
     pub struct S(Vec<P>);
-    //~^ ERROR the size for values of type
-    //~| WARNING trait objects without an explicit
-    //~| WARNING this is accepted in the current edition
-    //~| WARNING trait objects without an explicit
-    //~| WARNING this is accepted in the current edition
-    //~| WARNING trait objects without an explicit
-    //~| WARNING this is accepted in the current edition
-
-    // FIXME: should works, but doesn't currently refer
-    // to it due to backward compatibility
 }
 
 #[derive(Clone)]
diff --git a/tests/ui/imports/import-after-macro-expand-4.stderr b/tests/ui/imports/import-after-macro-expand-4.stderr
deleted file mode 100644
index 01f70cfc5bf..00000000000
--- a/tests/ui/imports/import-after-macro-expand-4.stderr
+++ /dev/null
@@ -1,53 +0,0 @@
-warning: trait objects without an explicit `dyn` are deprecated
-  --> $DIR/import-after-macro-expand-4.rs:12:22
-   |
-LL |     pub struct S(Vec<P>);
-   |                      ^
-   |
-   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!
-   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>
-   = note: `#[warn(bare_trait_objects)]` on by default
-help: use `dyn`
-   |
-LL |     pub struct S(Vec<dyn P>);
-   |                      +++
-
-warning: trait objects without an explicit `dyn` are deprecated
-  --> $DIR/import-after-macro-expand-4.rs:12:22
-   |
-LL |     pub struct S(Vec<P>);
-   |                      ^
-   |
-   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!
-   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>
-help: use `dyn`
-   |
-LL |     pub struct S(Vec<dyn P>);
-   |                      +++
-
-warning: trait objects without an explicit `dyn` are deprecated
-  --> $DIR/import-after-macro-expand-4.rs:12:22
-   |
-LL |     pub struct S(Vec<P>);
-   |                      ^
-   |
-   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!
-   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>
-help: use `dyn`
-   |
-LL |     pub struct S(Vec<dyn P>);
-   |                      +++
-
-error[E0277]: the size for values of type `(dyn a::P + 'static)` cannot be known at compilation time
-  --> $DIR/import-after-macro-expand-4.rs:12:18
-   |
-LL |     pub struct S(Vec<P>);
-   |                  ^^^^^^ doesn't have a size known at compile-time
-   |
-   = help: the trait `Sized` is not implemented for `(dyn a::P + 'static)`
-note: required by a bound in `Vec`
-  --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
-
-error: aborting due to previous error; 3 warnings emitted
-
-For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/imports/import-after-macro-expand-6.rs b/tests/ui/imports/import-after-macro-expand-6.rs
index ab5bb37a175..bff8efebca6 100644
--- a/tests/ui/imports/import-after-macro-expand-6.rs
+++ b/tests/ui/imports/import-after-macro-expand-6.rs
@@ -18,7 +18,5 @@ mod b {
 use crate::a::HeaderMap;
 
 fn main() {
-    let h: crate::b::http::HeaderMap = HeaderMap;
-    // FIXME: should refer to `crate::b::HeaderMap`,
-    // FIXME: but doesn't currently refer to it due to backward compatibility
+    let h: crate::b::HeaderMap = HeaderMap;
 }
diff --git a/tests/ui/imports/import-after-macro-expand-9.rs b/tests/ui/imports/import-after-macro-expand-9.rs
new file mode 100644
index 00000000000..deee42c3b84
--- /dev/null
+++ b/tests/ui/imports/import-after-macro-expand-9.rs
@@ -0,0 +1,17 @@
+// check-pass
+
+use crate::b::*;
+
+mod b {
+    pub mod http {
+        pub struct HeaderMap;
+    }
+
+    pub use self::http::*;
+    #[derive(Debug)]
+    pub struct HeaderMap;
+}
+
+fn main() {
+    let h: crate::b::HeaderMap = HeaderMap;
+}
diff --git a/tests/ui/inner-static-type-parameter.rs b/tests/ui/inner-static-type-parameter.rs
index c08ccd29d80..a1994e7529c 100644
--- a/tests/ui/inner-static-type-parameter.rs
+++ b/tests/ui/inner-static-type-parameter.rs
@@ -4,7 +4,7 @@ enum Bar<T> { What } //~ ERROR parameter `T` is never used
 
 fn foo<T>() {
     static a: Bar<T> = Bar::What;
-//~^ ERROR can't use generic parameters from outer function
+//~^ ERROR can't use generic parameters from outer item
 }
 
 fn main() {
diff --git a/tests/ui/inner-static-type-parameter.stderr b/tests/ui/inner-static-type-parameter.stderr
index e4e449e4159..ff6558e494b 100644
--- a/tests/ui/inner-static-type-parameter.stderr
+++ b/tests/ui/inner-static-type-parameter.stderr
@@ -1,10 +1,10 @@
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/inner-static-type-parameter.rs:6:19
    |
 LL | fn foo<T>() {
-   |        - type parameter from outer function
+   |        - type parameter from outer item
 LL |     static a: Bar<T> = Bar::What;
-   |                   ^ use of generic parameter from outer function
+   |                   ^ use of generic parameter from outer item
 
 error[E0392]: parameter `T` is never used
   --> $DIR/inner-static-type-parameter.rs:3:10
diff --git a/tests/ui/issues/issue-18611.stderr b/tests/ui/issues/issue-18611.stderr
index bd18d46223e..784b9b984e9 100644
--- a/tests/ui/issues/issue-18611.stderr
+++ b/tests/ui/issues/issue-18611.stderr
@@ -3,6 +3,12 @@ error[E0277]: the trait bound `isize: HasState` is not satisfied
    |
 LL | fn add_state(op: <isize as HasState>::State) {
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `HasState` is not implemented for `isize`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-18611.rs:5:1
+   |
+LL | trait HasState {
+   | ^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/tests/ui/issues/issue-25076.stderr b/tests/ui/issues/issue-25076.stderr
index 159cc484c5d..065bf7def42 100644
--- a/tests/ui/issues/issue-25076.stderr
+++ b/tests/ui/issues/issue-25076.stderr
@@ -6,6 +6,11 @@ LL |     do_fold(bot(), ());
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-25076.rs:3:1
+   |
+LL | trait InOut<T> { type Out; }
+   | ^^^^^^^^^^^^^^
 note: required by a bound in `do_fold`
   --> $DIR/issue-25076.rs:5:18
    |
diff --git a/tests/ui/issues/issue-3214.rs b/tests/ui/issues/issue-3214.rs
index e3c07bb3f72..b2c27f5be95 100644
--- a/tests/ui/issues/issue-3214.rs
+++ b/tests/ui/issues/issue-3214.rs
@@ -1,6 +1,6 @@
 fn foo<T>() {
     struct Foo {
-        x: T, //~ ERROR can't use generic parameters from outer function
+        x: T, //~ ERROR can't use generic parameters from outer item
     }
 
     impl<T> Drop for Foo<T> {
diff --git a/tests/ui/issues/issue-3214.stderr b/tests/ui/issues/issue-3214.stderr
index 7a2d772f0a1..5b57c1baf90 100644
--- a/tests/ui/issues/issue-3214.stderr
+++ b/tests/ui/issues/issue-3214.stderr
@@ -1,12 +1,12 @@
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/issue-3214.rs:3:12
    |
 LL | fn foo<T>() {
-   |        - type parameter from outer function
+   |        - type parameter from outer item
 LL |     struct Foo {
-   |               - help: try using a local generic parameter instead: `<T>`
+   |               - help: try introducing a local generic parameter here: `<T>`
 LL |         x: T,
-   |            ^ use of generic parameter from outer function
+   |            ^ use of generic parameter from outer item
 
 error[E0107]: struct takes 0 generic arguments but 1 generic argument was supplied
   --> $DIR/issue-3214.rs:6:22
diff --git a/tests/ui/issues/issue-35570.stderr b/tests/ui/issues/issue-35570.stderr
index 2697d46bdb2..197e80ac097 100644
--- a/tests/ui/issues/issue-35570.stderr
+++ b/tests/ui/issues/issue-35570.stderr
@@ -3,6 +3,12 @@ error[E0277]: the trait bound `for<'a> (): Trait2<'a>` is not satisfied
    |
 LL | fn _ice(param: Box<dyn for <'a> Trait1<<() as Trait2<'a>>::Ty>>) {
    |                                        ^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> Trait2<'a>` is not implemented for `()`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-35570.rs:4:1
+   |
+LL | trait Trait2<'a> {
+   | ^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/tests/ui/issues/issue-5997-enum.rs b/tests/ui/issues/issue-5997-enum.rs
index 3ff4e036c60..0b1857ae3df 100644
--- a/tests/ui/issues/issue-5997-enum.rs
+++ b/tests/ui/issues/issue-5997-enum.rs
@@ -1,6 +1,6 @@
 fn f<Z>() -> bool {
     enum E { V(Z) }
-    //~^ ERROR can't use generic parameters from outer function
+    //~^ ERROR can't use generic parameters from outer item
     true
 }
 
diff --git a/tests/ui/issues/issue-5997-enum.stderr b/tests/ui/issues/issue-5997-enum.stderr
index 3a79215d3ae..d07258ea7a2 100644
--- a/tests/ui/issues/issue-5997-enum.stderr
+++ b/tests/ui/issues/issue-5997-enum.stderr
@@ -1,12 +1,12 @@
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/issue-5997-enum.rs:2:16
    |
 LL | fn f<Z>() -> bool {
-   |      - type parameter from outer function
+   |      - type parameter from outer item
 LL |     enum E { V(Z) }
-   |           -    ^ use of generic parameter from outer function
+   |           -    ^ use of generic parameter from outer item
    |           |
-   |           help: try using a local generic parameter instead: `<Z>`
+   |           help: try introducing a local generic parameter here: `<Z>`
 
 error: aborting due to previous error
 
diff --git a/tests/ui/issues/issue-5997-struct.rs b/tests/ui/issues/issue-5997-struct.rs
index 6cf510b0a9d..19d994b0dfb 100644
--- a/tests/ui/issues/issue-5997-struct.rs
+++ b/tests/ui/issues/issue-5997-struct.rs
@@ -1,5 +1,5 @@
 fn f<T>() -> bool {
-    struct S(T); //~ ERROR can't use generic parameters from outer function
+    struct S(T); //~ ERROR can't use generic parameters from outer item
 
     true
 }
diff --git a/tests/ui/issues/issue-5997-struct.stderr b/tests/ui/issues/issue-5997-struct.stderr
index d2e97f76771..83229e02c6c 100644
--- a/tests/ui/issues/issue-5997-struct.stderr
+++ b/tests/ui/issues/issue-5997-struct.stderr
@@ -1,12 +1,12 @@
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/issue-5997-struct.rs:2:14
    |
 LL | fn f<T>() -> bool {
-   |      - type parameter from outer function
+   |      - type parameter from outer item
 LL |     struct S(T);
-   |             -^ use of generic parameter from outer function
+   |             -^ use of generic parameter from outer item
    |             |
-   |             help: try using a local generic parameter instead: `<T>`
+   |             help: try introducing a local generic parameter here: `<T>`
 
 error: aborting due to previous error
 
diff --git a/tests/ui/issues/issue-60218.stderr b/tests/ui/issues/issue-60218.stderr
index 563690c9a5d..ae3c4d12025 100644
--- a/tests/ui/issues/issue-60218.stderr
+++ b/tests/ui/issues/issue-60218.stderr
@@ -6,6 +6,11 @@ LL |     trigger_error(vec![], |x: &u32| x)
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-60218.rs:7:1
+   |
+LL | pub trait Foo {}
+   | ^^^^^^^^^^^^^
 note: required by a bound in `trigger_error`
   --> $DIR/issue-60218.rs:13:72
    |
diff --git a/tests/ui/issues/issue-66353.stderr b/tests/ui/issues/issue-66353.stderr
index 71530f58220..7ab7547b42d 100644
--- a/tests/ui/issues/issue-66353.stderr
+++ b/tests/ui/issues/issue-66353.stderr
@@ -3,6 +3,12 @@ error[E0277]: the trait bound `(): _A` is not satisfied
    |
 LL |     _Func::< <() as _A>::AssocT >::func(());
    |               ^^ the trait `_A` is not implemented for `()`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-66353.rs:7:1
+   |
+LL | trait _A {
+   | ^^^^^^^^
 
 error[E0277]: the trait bound `(): _Func<_>` is not satisfied
   --> $DIR/issue-66353.rs:12:41
@@ -11,6 +17,12 @@ LL |     _Func::< <() as _A>::AssocT >::func(());
    |     ----------------------------------- ^^ the trait `_Func<_>` is not implemented for `()`
    |     |
    |     required by a bound introduced by this call
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-66353.rs:3:1
+   |
+LL | trait _Func<T> {
+   | ^^^^^^^^^^^^^^
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/layout/debug.rs b/tests/ui/layout/debug.rs
index b74a8d3b917..65f2f3b89af 100644
--- a/tests/ui/layout/debug.rs
+++ b/tests/ui/layout/debug.rs
@@ -17,6 +17,9 @@ type Test = Result<i32, i32>; //~ ERROR: layout_of
 
 #[rustc_layout(debug)]
 type T = impl std::fmt::Debug; //~ ERROR: layout_of
+fn f() -> T {
+    0i32
+}
 
 #[rustc_layout(debug)]
 pub union V { //~ ERROR: layout_of
@@ -63,6 +66,13 @@ union P5 { zst: [u16; 0], byte: u8 } //~ ERROR: layout_of
 #[rustc_layout(debug)]
 type X = std::mem::MaybeUninit<u8>; //~ ERROR: layout_of
 
-fn f() -> T {
-    0i32
+#[rustc_layout(debug)]
+const C: () = (); //~ ERROR: can only be applied to
+
+impl S {
+    #[rustc_layout(debug)]
+    const C: () = (); //~ ERROR: can only be applied to
 }
+
+#[rustc_layout(debug)]
+type Impossible = (str, str); //~ ERROR: cannot be known at compilation time
diff --git a/tests/ui/layout/debug.stderr b/tests/ui/layout/debug.stderr
index c20a0198ccb..5162a771b4d 100644
--- a/tests/ui/layout/debug.stderr
+++ b/tests/ui/layout/debug.stderr
@@ -162,7 +162,7 @@ error: layout_of(U) = Layout {
 LL | union U { f1: (i32, i32), f3: i32 }
    | ^^^^^^^
 
-error: layout_of(std::result::Result<i32, i32>) = Layout {
+error: layout_of(Result<i32, i32>) = Layout {
            size: Size(8 bytes),
            align: AbiAndPrefAlign {
                abi: Align(4 bytes),
@@ -344,7 +344,7 @@ error: layout_of(V) = Layout {
            max_repr_align: None,
            unadjusted_abi_align: Align(2 bytes),
        }
-  --> $DIR/debug.rs:22:1
+  --> $DIR/debug.rs:25:1
    |
 LL | pub union V {
    | ^^^^^^^^^^^
@@ -368,7 +368,7 @@ error: layout_of(W) = Layout {
            max_repr_align: None,
            unadjusted_abi_align: Align(2 bytes),
        }
-  --> $DIR/debug.rs:28:1
+  --> $DIR/debug.rs:31:1
    |
 LL | pub union W {
    | ^^^^^^^^^^^
@@ -392,7 +392,7 @@ error: layout_of(Y) = Layout {
            max_repr_align: None,
            unadjusted_abi_align: Align(2 bytes),
        }
-  --> $DIR/debug.rs:34:1
+  --> $DIR/debug.rs:37:1
    |
 LL | pub union Y {
    | ^^^^^^^^^^^
@@ -416,7 +416,7 @@ error: layout_of(P1) = Layout {
            max_repr_align: None,
            unadjusted_abi_align: Align(1 bytes),
        }
-  --> $DIR/debug.rs:41:1
+  --> $DIR/debug.rs:44:1
    |
 LL | union P1 { x: u32 }
    | ^^^^^^^^
@@ -440,7 +440,7 @@ error: layout_of(P2) = Layout {
            max_repr_align: None,
            unadjusted_abi_align: Align(1 bytes),
        }
-  --> $DIR/debug.rs:45:1
+  --> $DIR/debug.rs:48:1
    |
 LL | union P2 { x: (u32, u32) }
    | ^^^^^^^^
@@ -464,7 +464,7 @@ error: layout_of(P3) = Layout {
            max_repr_align: None,
            unadjusted_abi_align: Align(1 bytes),
        }
-  --> $DIR/debug.rs:53:1
+  --> $DIR/debug.rs:56:1
    |
 LL | union P3 { x: F32x4 }
    | ^^^^^^^^
@@ -488,7 +488,7 @@ error: layout_of(P4) = Layout {
            max_repr_align: None,
            unadjusted_abi_align: Align(1 bytes),
        }
-  --> $DIR/debug.rs:57:1
+  --> $DIR/debug.rs:60:1
    |
 LL | union P4 { x: E }
    | ^^^^^^^^
@@ -517,12 +517,12 @@ error: layout_of(P5) = Layout {
            max_repr_align: None,
            unadjusted_abi_align: Align(1 bytes),
        }
-  --> $DIR/debug.rs:61:1
+  --> $DIR/debug.rs:64:1
    |
 LL | union P5 { zst: [u16; 0], byte: u8 }
    | ^^^^^^^^
 
-error: layout_of(std::mem::MaybeUninit<u8>) = Layout {
+error: layout_of(MaybeUninit<u8>) = Layout {
            size: Size(1 bytes),
            align: AbiAndPrefAlign {
                abi: Align(1 bytes),
@@ -546,10 +546,32 @@ error: layout_of(std::mem::MaybeUninit<u8>) = Layout {
            max_repr_align: None,
            unadjusted_abi_align: Align(1 bytes),
        }
-  --> $DIR/debug.rs:64:1
+  --> $DIR/debug.rs:67:1
    |
 LL | type X = std::mem::MaybeUninit<u8>;
    | ^^^^^^
 
-error: aborting due to 14 previous errors
+error: `#[rustc_layout]` can only be applied to `struct`/`enum`/`union` declarations and type aliases
+  --> $DIR/debug.rs:70:1
+   |
+LL | const C: () = ();
+   | ^^^^^^^^^^^
+
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/debug.rs:78:19
+   |
+LL | type Impossible = (str, str);
+   |                   ^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `str`
+   = note: only the last element of a tuple may have a dynamically sized type
+
+error: `#[rustc_layout]` can only be applied to `struct`/`enum`/`union` declarations and type aliases
+  --> $DIR/debug.rs:74:5
+   |
+LL |     const C: () = ();
+   |     ^^^^^^^^^^^
+
+error: aborting due to 17 previous errors
 
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/layout/homogeneous-aggr-transparent.rs b/tests/ui/layout/homogeneous-aggr-transparent.rs
new file mode 100644
index 00000000000..9703d2bf294
--- /dev/null
+++ b/tests/ui/layout/homogeneous-aggr-transparent.rs
@@ -0,0 +1,44 @@
+#![feature(rustc_attrs)]
+#![feature(transparent_unions)]
+use std::marker::PhantomData;
+
+// Regression test for #115664. We want to ensure that `repr(transparent)` wrappers do not affect
+// the result of `homogeneous_aggregate`.
+
+type Tuple = (f32, f32, f32);
+
+struct Zst;
+
+#[repr(transparent)]
+struct Wrapper1<T>(T);
+#[repr(transparent)]
+struct Wrapper2<T>((), Zst, T);
+#[repr(transparent)]
+struct Wrapper3<T>(T, [u8; 0], PhantomData<u64>);
+#[repr(transparent)]
+union WrapperUnion<T: Copy> {
+    nothing: (),
+    something: T,
+}
+
+#[rustc_layout(homogeneous_aggregate)]
+pub type Test0 = Tuple;
+//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) }))
+
+#[rustc_layout(homogeneous_aggregate)]
+pub type Test1 = Wrapper1<Tuple>;
+//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) }))
+
+#[rustc_layout(homogeneous_aggregate)]
+pub type Test2 = Wrapper2<Tuple>;
+//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) }))
+
+#[rustc_layout(homogeneous_aggregate)]
+pub type Test3 = Wrapper3<Tuple>;
+//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) }))
+
+#[rustc_layout(homogeneous_aggregate)]
+pub type Test4 = WrapperUnion<Tuple>;
+//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) }))
+
+fn main() {}
diff --git a/tests/ui/layout/homogeneous-aggr-transparent.stderr b/tests/ui/layout/homogeneous-aggr-transparent.stderr
new file mode 100644
index 00000000000..99eb703ac82
--- /dev/null
+++ b/tests/ui/layout/homogeneous-aggr-transparent.stderr
@@ -0,0 +1,32 @@
+error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) }))
+  --> $DIR/homogeneous-aggr-transparent.rs:25:1
+   |
+LL | pub type Test0 = Tuple;
+   | ^^^^^^^^^^^^^^
+
+error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) }))
+  --> $DIR/homogeneous-aggr-transparent.rs:29:1
+   |
+LL | pub type Test1 = Wrapper1<Tuple>;
+   | ^^^^^^^^^^^^^^
+
+error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) }))
+  --> $DIR/homogeneous-aggr-transparent.rs:33:1
+   |
+LL | pub type Test2 = Wrapper2<Tuple>;
+   | ^^^^^^^^^^^^^^
+
+error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) }))
+  --> $DIR/homogeneous-aggr-transparent.rs:37:1
+   |
+LL | pub type Test3 = Wrapper3<Tuple>;
+   | ^^^^^^^^^^^^^^
+
+error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) }))
+  --> $DIR/homogeneous-aggr-transparent.rs:41:1
+   |
+LL | pub type Test4 = WrapperUnion<Tuple>;
+   | ^^^^^^^^^^^^^^
+
+error: aborting due to 5 previous errors
+
diff --git a/tests/ui/layout/zero-sized-array-enum-niche.stderr b/tests/ui/layout/zero-sized-array-enum-niche.stderr
index df9f1cc8d10..8161f97dde0 100644
--- a/tests/ui/layout/zero-sized-array-enum-niche.stderr
+++ b/tests/ui/layout/zero-sized-array-enum-niche.stderr
@@ -1,4 +1,4 @@
-error: layout_of(std::result::Result<[u32; 0], bool>) = Layout {
+error: layout_of(Result<[u32; 0], bool>) = Layout {
            size: Size(4 bytes),
            align: AbiAndPrefAlign {
                abi: Align(4 bytes),
@@ -232,7 +232,7 @@ error: layout_of(MultipleAlignments) = Layout {
 LL | enum MultipleAlignments {
    | ^^^^^^^^^^^^^^^^^^^^^^^
 
-error: layout_of(std::result::Result<[u32; 0], Packed<std::num::NonZeroU16>>) = Layout {
+error: layout_of(Result<[u32; 0], Packed<NonZeroU16>>) = Layout {
            size: Size(4 bytes),
            align: AbiAndPrefAlign {
                abi: Align(4 bytes),
@@ -337,7 +337,7 @@ error: layout_of(std::result::Result<[u32; 0], Packed<std::num::NonZeroU16>>) =
 LL | type NicheLosesToTagged = Result<[u32; 0], Packed<std::num::NonZeroU16>>;
    | ^^^^^^^^^^^^^^^^^^^^^^^
 
-error: layout_of(std::result::Result<[u32; 0], Packed<U16IsZero>>) = Layout {
+error: layout_of(Result<[u32; 0], Packed<U16IsZero>>) = Layout {
            size: Size(4 bytes),
            align: AbiAndPrefAlign {
                abi: Align(4 bytes),
diff --git a/tests/ui/lifetimes/anonymize-unnamed-bound-vars-in-binders.rs b/tests/ui/lifetimes/anonymize-unnamed-bound-vars-in-binders.rs
new file mode 100644
index 00000000000..05e3763e9d1
--- /dev/null
+++ b/tests/ui/lifetimes/anonymize-unnamed-bound-vars-in-binders.rs
@@ -0,0 +1,27 @@
+// build-pass
+// issue: #115807
+
+trait Chip: for<'a> TraitWithLifetime<'a> + SomeMarker {
+    fn compute(&self);
+}
+
+trait SomeMarker {}
+
+trait TraitWithLifetime<'a>: SomeMarker {}
+
+trait Machine {
+    fn run();
+}
+
+struct BasicMachine;
+
+impl Machine for BasicMachine {
+    fn run() {
+        let chips: [&dyn Chip; 0] = [];
+        let _ = chips.map(|chip| chip.compute());
+    }
+}
+
+fn main() {
+    BasicMachine::run();
+}
diff --git a/tests/ui/lifetimes/issue-95023.stderr b/tests/ui/lifetimes/issue-95023.stderr
index 5b93eff8614..6361d8ad30b 100644
--- a/tests/ui/lifetimes/issue-95023.stderr
+++ b/tests/ui/lifetimes/issue-95023.stderr
@@ -36,7 +36,7 @@ error[E0220]: associated type `B` not found for `Self`
   --> $DIR/issue-95023.rs:6:44
    |
 LL |     fn foo<const N: usize>(&self) -> Self::B<{N}>;
-   |                                            ^ associated type `B` not found
+   |                                            ^ help: `Self` has the following associated type: `Output`
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/lifetimes/lifetime-elision-return-type-trait.stderr b/tests/ui/lifetimes/lifetime-elision-return-type-trait.stderr
index ef1127c59ac..421ab3fcf4e 100644
--- a/tests/ui/lifetimes/lifetime-elision-return-type-trait.stderr
+++ b/tests/ui/lifetimes/lifetime-elision-return-type-trait.stderr
@@ -3,6 +3,12 @@ error[E0277]: the trait bound `Result<(), _>: Future` is not satisfied
    |
 LL | fn foo() -> impl Future<Item=(), Error=Box<dyn Error>> {
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Future` is not implemented for `Result<(), _>`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/lifetime-elision-return-type-trait.rs:1:1
+   |
+LL | trait Future {
+   | ^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/tests/ui/limits/issue-55878.stderr b/tests/ui/limits/issue-55878.stderr
index 510b36edd8f..f0c7210dde7 100644
--- a/tests/ui/limits/issue-55878.stderr
+++ b/tests/ui/limits/issue-55878.stderr
@@ -1,6 +1,8 @@
-error[E0080]: values of the type `[u8; usize::MAX]` are too big for the current architecture
+error[E0080]: evaluation of constant value failed
   --> $SRC_DIR/core/src/mem/mod.rs:LL:COL
    |
+   = note: values of the type `[u8; usize::MAX]` are too big for the current architecture
+   |
 note: inside `std::mem::size_of::<[u8; usize::MAX]>`
   --> $SRC_DIR/core/src/mem/mod.rs:LL:COL
 note: inside `main`
diff --git a/tests/ui/limits/issue-56762.rs b/tests/ui/limits/issue-56762.rs
index ed7ee4da85d..1c7facb045d 100644
--- a/tests/ui/limits/issue-56762.rs
+++ b/tests/ui/limits/issue-56762.rs
@@ -14,8 +14,10 @@ impl TooBigArray {
 }
 
 static MY_TOO_BIG_ARRAY_1: TooBigArray = TooBigArray::new();
-//~^ ERROR values of the type `[u8; 2305843009213693951]` are too big
+//~^ ERROR could not evaluate static initializer
+//~| too big
 static MY_TOO_BIG_ARRAY_2: [u8; HUGE_SIZE] = [0x00; HUGE_SIZE];
-//~^ ERROR values of the type `[u8; 2305843009213693951]` are too big
+//~^ ERROR could not evaluate static initializer
+//~| too big
 
 fn main() { }
diff --git a/tests/ui/limits/issue-56762.stderr b/tests/ui/limits/issue-56762.stderr
index 29f2a8859ee..3a6c3559ac1 100644
--- a/tests/ui/limits/issue-56762.stderr
+++ b/tests/ui/limits/issue-56762.stderr
@@ -1,14 +1,14 @@
-error[E0080]: values of the type `[u8; 2305843009213693951]` are too big for the current architecture
+error[E0080]: could not evaluate static initializer
   --> $DIR/issue-56762.rs:16:1
    |
 LL | static MY_TOO_BIG_ARRAY_1: TooBigArray = TooBigArray::new();
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ values of the type `[u8; 2305843009213693951]` are too big for the current architecture
 
-error[E0080]: values of the type `[u8; 2305843009213693951]` are too big for the current architecture
-  --> $DIR/issue-56762.rs:18:1
+error[E0080]: could not evaluate static initializer
+  --> $DIR/issue-56762.rs:19:1
    |
 LL | static MY_TOO_BIG_ARRAY_2: [u8; HUGE_SIZE] = [0x00; HUGE_SIZE];
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ values of the type `[u8; 2305843009213693951]` are too big for the current architecture
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/linkage-attr/common-linkage-non-zero-init.rs b/tests/ui/linkage-attr/common-linkage-non-zero-init.rs
new file mode 100644
index 00000000000..ce8d9848e42
--- /dev/null
+++ b/tests/ui/linkage-attr/common-linkage-non-zero-init.rs
@@ -0,0 +1,14 @@
+// build-fail
+// failure-status: 101
+// known-bug: #109681
+
+// This test verifies that we continue to hit the LLVM error for common linkage with non-zero
+// initializers, since it generates invalid LLVM IR.
+// Linkages are internal features marked as perma-unstable, so we don't need to fix the issue
+// for now.
+#![crate_type="lib"]
+#![feature(linkage)]
+
+#[linkage = "common"]
+#[no_mangle]
+pub static TEST: bool = true;
diff --git a/tests/ui/linkage-attr/common-linkage-non-zero-init.stderr b/tests/ui/linkage-attr/common-linkage-non-zero-init.stderr
new file mode 100644
index 00000000000..667bb3ec130
--- /dev/null
+++ b/tests/ui/linkage-attr/common-linkage-non-zero-init.stderr
@@ -0,0 +1,3 @@
+'common' global must have a zero initializer!
+ptr @TEST
+LLVM ERROR: Broken module found, compilation aborted!
diff --git a/tests/ui/lint/cli-unknown-force-warn.rs b/tests/ui/lint/cli-unknown-force-warn.rs
index f3dea87a6b6..a9e4e4a6017 100644
--- a/tests/ui/lint/cli-unknown-force-warn.rs
+++ b/tests/ui/lint/cli-unknown-force-warn.rs
@@ -1,7 +1,11 @@
 // Checks that rustc correctly errors when passed an invalid lint with
 // `--force-warn`. This is a regression test for issue #86958.
-//
+
+// check-pass
 // compile-flags: --force-warn foo-qux
+
 // error-pattern: unknown lint: `foo_qux`
+// error-pattern: requested on the command line with `--force-warn foo_qux`
+// error-pattern: `#[warn(unknown_lints)]` on by default
 
 fn main() {}
diff --git a/tests/ui/lint/cli-unknown-force-warn.stderr b/tests/ui/lint/cli-unknown-force-warn.stderr
index 9ce9f405aee..2ee718a8c8e 100644
--- a/tests/ui/lint/cli-unknown-force-warn.stderr
+++ b/tests/ui/lint/cli-unknown-force-warn.stderr
@@ -1,11 +1,16 @@
-error[E0602]: unknown lint: `foo_qux`
+warning[E0602]: unknown lint: `foo_qux`
    |
    = note: requested on the command line with `--force-warn foo_qux`
+   = note: `#[warn(unknown_lints)]` on by default
 
-error[E0602]: unknown lint: `foo_qux`
+warning[E0602]: unknown lint: `foo_qux`
    |
    = note: requested on the command line with `--force-warn foo_qux`
 
-error: aborting due to 2 previous errors
+warning[E0602]: unknown lint: `foo_qux`
+   |
+   = note: requested on the command line with `--force-warn foo_qux`
+
+warning: 3 warnings emitted
 
 For more information about this error, try `rustc --explain E0602`.
diff --git a/tests/ui/lint/expr-field.rs b/tests/ui/lint/expr-field.rs
new file mode 100644
index 00000000000..638fbf521c4
--- /dev/null
+++ b/tests/ui/lint/expr-field.rs
@@ -0,0 +1,15 @@
+// check-pass
+
+pub struct A {
+    pub x: u32,
+}
+
+#[deny(unused_comparisons)]
+pub fn foo(y: u32) -> A {
+    A {
+        #[allow(unused_comparisons)]
+        x: if y < 0 { 1 } else { 2 },
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/lint/lint-ctypes-94223.rs b/tests/ui/lint/lint-ctypes-94223.rs
index 70dd2a71f26..ac24f61b0ac 100644
--- a/tests/ui/lint/lint-ctypes-94223.rs
+++ b/tests/ui/lint/lint-ctypes-94223.rs
@@ -24,6 +24,13 @@ enum BadUnion {
 type Foo = extern "C" fn([u8]);
 //~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe
 
+pub trait FooTrait {
+    type FooType;
+}
+
+pub type Foo2<T> = extern "C" fn(Option<&<T as FooTrait>::FooType>);
+//~^ ERROR `extern` fn uses type `Option<&<T as FooTrait>::FooType>`, which is not FFI-safe
+
 pub struct FfiUnsafe;
 
 #[allow(improper_ctypes_definitions)]
diff --git a/tests/ui/lint/lint-ctypes-94223.stderr b/tests/ui/lint/lint-ctypes-94223.stderr
index 49e64ed5140..bd127cf6004 100644
--- a/tests/ui/lint/lint-ctypes-94223.stderr
+++ b/tests/ui/lint/lint-ctypes-94223.stderr
@@ -66,8 +66,17 @@ LL | type Foo = extern "C" fn([u8]);
    = help: consider using a raw pointer instead
    = note: slices have no C equivalent
 
+error: `extern` fn uses type `Option<&<T as FooTrait>::FooType>`, which is not FFI-safe
+  --> $DIR/lint-ctypes-94223.rs:31:20
+   |
+LL | pub type Foo2<T> = extern "C" fn(Option<&<T as FooTrait>::FooType>);
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
+   |
+   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
+   = note: enum has no representation hint
+
 error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe
-  --> $DIR/lint-ctypes-94223.rs:34:17
+  --> $DIR/lint-ctypes-94223.rs:41:17
    |
 LL | pub static BAD: extern "C" fn(FfiUnsafe) = f;
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -75,13 +84,13 @@ LL | pub static BAD: extern "C" fn(FfiUnsafe) = f;
    = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
    = note: this struct has unspecified layout
 note: the type is defined here
-  --> $DIR/lint-ctypes-94223.rs:27:1
+  --> $DIR/lint-ctypes-94223.rs:34:1
    |
 LL | pub struct FfiUnsafe;
    | ^^^^^^^^^^^^^^^^^^^^
 
 error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe
-  --> $DIR/lint-ctypes-94223.rs:37:30
+  --> $DIR/lint-ctypes-94223.rs:44:30
    |
 LL | pub static BAD_TWICE: Result<extern "C" fn(FfiUnsafe), extern "C" fn(FfiUnsafe)> = Ok(f);
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -89,13 +98,13 @@ LL | pub static BAD_TWICE: Result<extern "C" fn(FfiUnsafe), extern "C" fn(FfiUns
    = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
    = note: this struct has unspecified layout
 note: the type is defined here
-  --> $DIR/lint-ctypes-94223.rs:27:1
+  --> $DIR/lint-ctypes-94223.rs:34:1
    |
 LL | pub struct FfiUnsafe;
    | ^^^^^^^^^^^^^^^^^^^^
 
 error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe
-  --> $DIR/lint-ctypes-94223.rs:37:56
+  --> $DIR/lint-ctypes-94223.rs:44:56
    |
 LL | pub static BAD_TWICE: Result<extern "C" fn(FfiUnsafe), extern "C" fn(FfiUnsafe)> = Ok(f);
    |                                                        ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -103,13 +112,13 @@ LL | pub static BAD_TWICE: Result<extern "C" fn(FfiUnsafe), extern "C" fn(FfiUns
    = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
    = note: this struct has unspecified layout
 note: the type is defined here
-  --> $DIR/lint-ctypes-94223.rs:27:1
+  --> $DIR/lint-ctypes-94223.rs:34:1
    |
 LL | pub struct FfiUnsafe;
    | ^^^^^^^^^^^^^^^^^^^^
 
 error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe
-  --> $DIR/lint-ctypes-94223.rs:41:22
+  --> $DIR/lint-ctypes-94223.rs:48:22
    |
 LL | pub const BAD_CONST: extern "C" fn(FfiUnsafe) = f;
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -117,10 +126,10 @@ LL | pub const BAD_CONST: extern "C" fn(FfiUnsafe) = f;
    = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
    = note: this struct has unspecified layout
 note: the type is defined here
-  --> $DIR/lint-ctypes-94223.rs:27:1
+  --> $DIR/lint-ctypes-94223.rs:34:1
    |
 LL | pub struct FfiUnsafe;
    | ^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 11 previous errors
+error: aborting due to 12 previous errors
 
diff --git a/tests/ui/lint/lint-ctypes-option-nonnull-unsized.rs b/tests/ui/lint/lint-ctypes-option-nonnull-unsized.rs
new file mode 100644
index 00000000000..ca08eb23a57
--- /dev/null
+++ b/tests/ui/lint/lint-ctypes-option-nonnull-unsized.rs
@@ -0,0 +1,8 @@
+#![deny(improper_ctypes_definitions)]
+
+extern "C" fn foo<T: ?Sized + 'static>() -> Option<&'static T> {
+    //~^ ERROR `extern` fn uses type `Option<&T>`, which is not FFI-safe
+    None
+}
+
+fn main() {}
diff --git a/tests/ui/lint/lint-ctypes-option-nonnull-unsized.stderr b/tests/ui/lint/lint-ctypes-option-nonnull-unsized.stderr
new file mode 100644
index 00000000000..f59fb3cc750
--- /dev/null
+++ b/tests/ui/lint/lint-ctypes-option-nonnull-unsized.stderr
@@ -0,0 +1,16 @@
+error: `extern` fn uses type `Option<&T>`, which is not FFI-safe
+  --> $DIR/lint-ctypes-option-nonnull-unsized.rs:3:45
+   |
+LL | extern "C" fn foo<T: ?Sized + 'static>() -> Option<&'static T> {
+   |                                             ^^^^^^^^^^^^^^^^^^ not FFI-safe
+   |
+   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
+   = note: enum has no representation hint
+note: the lint level is defined here
+  --> $DIR/lint-ctypes-option-nonnull-unsized.rs:1:9
+   |
+LL | #![deny(improper_ctypes_definitions)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/lint/lint-removed-cmdline-deny.rs b/tests/ui/lint/lint-removed-cmdline-deny.rs
new file mode 100644
index 00000000000..8cf91cf60eb
--- /dev/null
+++ b/tests/ui/lint/lint-removed-cmdline-deny.rs
@@ -0,0 +1,13 @@
+// The raw_pointer_derived lint warns about its removal
+// cc #30346
+
+// compile-flags:-D renamed-and-removed-lints -D raw_pointer_derive
+
+// error-pattern:lint `raw_pointer_derive` has been removed
+// error-pattern:requested on the command line with `-D raw_pointer_derive`
+// error-pattern:requested on the command line with `-D renamed-and-removed-lints`
+
+#![warn(unused)]
+
+#[deny(warnings)]
+fn main() { let unused = (); }
diff --git a/tests/ui/lint/lint-removed-cmdline-deny.stderr b/tests/ui/lint/lint-removed-cmdline-deny.stderr
new file mode 100644
index 00000000000..80c85d01e53
--- /dev/null
+++ b/tests/ui/lint/lint-removed-cmdline-deny.stderr
@@ -0,0 +1,28 @@
+error: lint `raw_pointer_derive` has been removed: using derive with raw pointers is ok
+   |
+   = note: requested on the command line with `-D raw_pointer_derive`
+   = note: requested on the command line with `-D renamed-and-removed-lints`
+
+error: lint `raw_pointer_derive` has been removed: using derive with raw pointers is ok
+   |
+   = note: requested on the command line with `-D raw_pointer_derive`
+
+error: lint `raw_pointer_derive` has been removed: using derive with raw pointers is ok
+   |
+   = note: requested on the command line with `-D raw_pointer_derive`
+
+error: unused variable: `unused`
+  --> $DIR/lint-removed-cmdline-deny.rs:13:17
+   |
+LL | fn main() { let unused = (); }
+   |                 ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused`
+   |
+note: the lint level is defined here
+  --> $DIR/lint-removed-cmdline-deny.rs:12:8
+   |
+LL | #[deny(warnings)]
+   |        ^^^^^^^^
+   = note: `#[deny(unused_variables)]` implied by `#[deny(warnings)]`
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/lint/lint-removed-cmdline.rs b/tests/ui/lint/lint-removed-cmdline.rs
index 462beabb945..34373df3a9c 100644
--- a/tests/ui/lint/lint-removed-cmdline.rs
+++ b/tests/ui/lint/lint-removed-cmdline.rs
@@ -4,6 +4,7 @@
 // compile-flags:-D raw_pointer_derive
 
 // error-pattern:lint `raw_pointer_derive` has been removed
+// error-pattern:`#[warn(renamed_and_removed_lints)]` on by default
 // error-pattern:requested on the command line with `-D raw_pointer_derive`
 
 #![warn(unused)]
diff --git a/tests/ui/lint/lint-removed-cmdline.stderr b/tests/ui/lint/lint-removed-cmdline.stderr
index 9be532ef234..ebfae34ade9 100644
--- a/tests/ui/lint/lint-removed-cmdline.stderr
+++ b/tests/ui/lint/lint-removed-cmdline.stderr
@@ -1,6 +1,7 @@
 warning: lint `raw_pointer_derive` has been removed: using derive with raw pointers is ok
    |
    = note: requested on the command line with `-D raw_pointer_derive`
+   = note: `#[warn(renamed_and_removed_lints)]` on by default
 
 warning: lint `raw_pointer_derive` has been removed: using derive with raw pointers is ok
    |
@@ -11,13 +12,13 @@ warning: lint `raw_pointer_derive` has been removed: using derive with raw point
    = note: requested on the command line with `-D raw_pointer_derive`
 
 error: unused variable: `unused`
-  --> $DIR/lint-removed-cmdline.rs:12:17
+  --> $DIR/lint-removed-cmdline.rs:13:17
    |
 LL | fn main() { let unused = (); }
    |                 ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused`
    |
 note: the lint level is defined here
-  --> $DIR/lint-removed-cmdline.rs:11:8
+  --> $DIR/lint-removed-cmdline.rs:12:8
    |
 LL | #[deny(warnings)]
    |        ^^^^^^^^
diff --git a/tests/ui/lint/lint-renamed-cmdline-deny.rs b/tests/ui/lint/lint-renamed-cmdline-deny.rs
new file mode 100644
index 00000000000..01629aaca80
--- /dev/null
+++ b/tests/ui/lint/lint-renamed-cmdline-deny.rs
@@ -0,0 +1,10 @@
+// compile-flags:-D renamed-and-removed-lints -D bare_trait_object
+
+// error-pattern:lint `bare_trait_object` has been renamed to `bare_trait_objects`
+// error-pattern:use the new name `bare_trait_objects`
+// error-pattern:requested on the command line with `-D bare_trait_object`
+// error-pattern:requested on the command line with `-D renamed-and-removed-lints`
+// error-pattern:unused
+
+#[deny(unused)]
+fn main() { let unused = (); }
diff --git a/tests/ui/lint/lint-renamed-cmdline-deny.stderr b/tests/ui/lint/lint-renamed-cmdline-deny.stderr
new file mode 100644
index 00000000000..df22ef60daf
--- /dev/null
+++ b/tests/ui/lint/lint-renamed-cmdline-deny.stderr
@@ -0,0 +1,31 @@
+error: lint `bare_trait_object` has been renamed to `bare_trait_objects`
+   |
+   = help: use the new name `bare_trait_objects`
+   = note: requested on the command line with `-D bare_trait_object`
+   = note: requested on the command line with `-D renamed-and-removed-lints`
+
+error: lint `bare_trait_object` has been renamed to `bare_trait_objects`
+   |
+   = help: use the new name `bare_trait_objects`
+   = note: requested on the command line with `-D bare_trait_object`
+
+error: lint `bare_trait_object` has been renamed to `bare_trait_objects`
+   |
+   = help: use the new name `bare_trait_objects`
+   = note: requested on the command line with `-D bare_trait_object`
+
+error: unused variable: `unused`
+  --> $DIR/lint-renamed-cmdline-deny.rs:10:17
+   |
+LL | fn main() { let unused = (); }
+   |                 ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused`
+   |
+note: the lint level is defined here
+  --> $DIR/lint-renamed-cmdline-deny.rs:9:8
+   |
+LL | #[deny(unused)]
+   |        ^^^^^^
+   = note: `#[deny(unused_variables)]` implied by `#[deny(unused)]`
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/lint/lint-renamed-cmdline.rs b/tests/ui/lint/lint-renamed-cmdline.rs
index c873771e308..fba7c33311d 100644
--- a/tests/ui/lint/lint-renamed-cmdline.rs
+++ b/tests/ui/lint/lint-renamed-cmdline.rs
@@ -2,6 +2,7 @@
 
 // error-pattern:lint `bare_trait_object` has been renamed to `bare_trait_objects`
 // error-pattern:requested on the command line with `-D bare_trait_object`
+// error-pattern:`#[warn(renamed_and_removed_lints)]` on by default
 // error-pattern:unused
 
 #[deny(unused)]
diff --git a/tests/ui/lint/lint-renamed-cmdline.stderr b/tests/ui/lint/lint-renamed-cmdline.stderr
index 8dfd61ac927..a41284003ed 100644
--- a/tests/ui/lint/lint-renamed-cmdline.stderr
+++ b/tests/ui/lint/lint-renamed-cmdline.stderr
@@ -1,23 +1,27 @@
 warning: lint `bare_trait_object` has been renamed to `bare_trait_objects`
    |
+   = help: use the new name `bare_trait_objects`
    = note: requested on the command line with `-D bare_trait_object`
+   = note: `#[warn(renamed_and_removed_lints)]` on by default
 
 warning: lint `bare_trait_object` has been renamed to `bare_trait_objects`
    |
+   = help: use the new name `bare_trait_objects`
    = note: requested on the command line with `-D bare_trait_object`
 
 warning: lint `bare_trait_object` has been renamed to `bare_trait_objects`
    |
+   = help: use the new name `bare_trait_objects`
    = note: requested on the command line with `-D bare_trait_object`
 
 error: unused variable: `unused`
-  --> $DIR/lint-renamed-cmdline.rs:8:17
+  --> $DIR/lint-renamed-cmdline.rs:9:17
    |
 LL | fn main() { let unused = (); }
    |                 ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused`
    |
 note: the lint level is defined here
-  --> $DIR/lint-renamed-cmdline.rs:7:8
+  --> $DIR/lint-renamed-cmdline.rs:8:8
    |
 LL | #[deny(unused)]
    |        ^^^^^^
diff --git a/tests/ui/lint/lint-unexported-no-mangle.stderr b/tests/ui/lint/lint-unexported-no-mangle.stderr
index a11ee769c7c..85852782222 100644
--- a/tests/ui/lint/lint-unexported-no-mangle.stderr
+++ b/tests/ui/lint/lint-unexported-no-mangle.stderr
@@ -1,6 +1,7 @@
 warning: lint `private_no_mangle_fns` has been removed: no longer a warning, `#[no_mangle]` functions always exported
    |
    = note: requested on the command line with `-F private_no_mangle_fns`
+   = note: `#[warn(renamed_and_removed_lints)]` on by default
 
 warning: lint `private_no_mangle_statics` has been removed: no longer a warning, `#[no_mangle]` statics always exported
    |
diff --git a/tests/ui/lint/lint-unknown-lint-cmdline-allow.rs b/tests/ui/lint/lint-unknown-lint-cmdline-allow.rs
new file mode 100644
index 00000000000..c7f8d434c04
--- /dev/null
+++ b/tests/ui/lint/lint-unknown-lint-cmdline-allow.rs
@@ -0,0 +1,4 @@
+// check-pass
+// compile-flags:-A unknown-lints -D bogus -D dead_cod
+
+fn main() { }
diff --git a/tests/ui/lint/lint-unknown-lint-cmdline-deny.rs b/tests/ui/lint/lint-unknown-lint-cmdline-deny.rs
new file mode 100644
index 00000000000..31bc2047356
--- /dev/null
+++ b/tests/ui/lint/lint-unknown-lint-cmdline-deny.rs
@@ -0,0 +1,9 @@
+// compile-flags:-D unknown-lints -D bogus -D dead_cod
+
+// error-pattern:unknown lint: `bogus`
+// error-pattern:requested on the command line with `-D bogus`
+// error-pattern:requested on the command line with `-D dead_cod`
+// error-pattern:requested on the command line with `-D unknown-lints`
+// error-pattern:did you mean: `dead_code`
+
+fn main() { }
diff --git a/tests/ui/lint/lint-unknown-lint-cmdline-deny.stderr b/tests/ui/lint/lint-unknown-lint-cmdline-deny.stderr
new file mode 100644
index 00000000000..677b5edc894
--- /dev/null
+++ b/tests/ui/lint/lint-unknown-lint-cmdline-deny.stderr
@@ -0,0 +1,31 @@
+error[E0602]: unknown lint: `bogus`
+   |
+   = note: requested on the command line with `-D bogus`
+   = note: requested on the command line with `-D unknown-lints`
+
+error[E0602]: unknown lint: `dead_cod`
+   |
+   = help: did you mean: `dead_code`
+   = note: requested on the command line with `-D dead_cod`
+
+error[E0602]: unknown lint: `bogus`
+   |
+   = note: requested on the command line with `-D bogus`
+
+error[E0602]: unknown lint: `dead_cod`
+   |
+   = help: did you mean: `dead_code`
+   = note: requested on the command line with `-D dead_cod`
+
+error[E0602]: unknown lint: `bogus`
+   |
+   = note: requested on the command line with `-D bogus`
+
+error[E0602]: unknown lint: `dead_cod`
+   |
+   = help: did you mean: `dead_code`
+   = note: requested on the command line with `-D dead_cod`
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0602`.
diff --git a/tests/ui/lint/lint-unknown-lint-cmdline.rs b/tests/ui/lint/lint-unknown-lint-cmdline.rs
index 7f3f55fbad0..81539cb6dc1 100644
--- a/tests/ui/lint/lint-unknown-lint-cmdline.rs
+++ b/tests/ui/lint/lint-unknown-lint-cmdline.rs
@@ -1,7 +1,9 @@
+// check-pass
 // compile-flags:-D bogus -D dead_cod
 
 // error-pattern:unknown lint: `bogus`
 // error-pattern:requested on the command line with `-D bogus`
+// error-pattern:`#[warn(unknown_lints)]` on by default
 // error-pattern:unknown lint: `dead_cod`
 // error-pattern:requested on the command line with `-D dead_cod`
 // error-pattern:did you mean: `dead_code`
diff --git a/tests/ui/lint/lint-unknown-lint-cmdline.stderr b/tests/ui/lint/lint-unknown-lint-cmdline.stderr
index 3855d552792..10db76ac4f1 100644
--- a/tests/ui/lint/lint-unknown-lint-cmdline.stderr
+++ b/tests/ui/lint/lint-unknown-lint-cmdline.stderr
@@ -1,21 +1,31 @@
-error[E0602]: unknown lint: `bogus`
+warning[E0602]: unknown lint: `bogus`
    |
    = note: requested on the command line with `-D bogus`
+   = note: `#[warn(unknown_lints)]` on by default
 
-error[E0602]: unknown lint: `dead_cod`
+warning[E0602]: unknown lint: `dead_cod`
    |
    = help: did you mean: `dead_code`
    = note: requested on the command line with `-D dead_cod`
 
-error[E0602]: unknown lint: `bogus`
+warning[E0602]: unknown lint: `bogus`
    |
    = note: requested on the command line with `-D bogus`
 
-error[E0602]: unknown lint: `dead_cod`
+warning[E0602]: unknown lint: `dead_cod`
    |
    = help: did you mean: `dead_code`
    = note: requested on the command line with `-D dead_cod`
 
-error: aborting due to 4 previous errors
+warning[E0602]: unknown lint: `bogus`
+   |
+   = note: requested on the command line with `-D bogus`
+
+warning[E0602]: unknown lint: `dead_cod`
+   |
+   = help: did you mean: `dead_code`
+   = note: requested on the command line with `-D dead_cod`
+
+warning: 6 warnings emitted
 
 For more information about this error, try `rustc --explain E0602`.
diff --git a/tests/ui/lint/no-coverage.rs b/tests/ui/lint/no-coverage.rs
index 07906a43472..907d25d333e 100644
--- a/tests/ui/lint/no-coverage.rs
+++ b/tests/ui/lint/no-coverage.rs
@@ -1,55 +1,55 @@
 #![feature(extern_types)]
-#![feature(no_coverage)]
+#![feature(coverage_attribute)]
 #![feature(impl_trait_in_assoc_type)]
 #![warn(unused_attributes)]
-#![no_coverage]
-//~^ WARN: `#[no_coverage]` does not propagate into items and must be applied to the contained functions directly
+#![coverage(off)]
+//~^ WARN: `#[coverage]` does not propagate into items and must be applied to the contained functions directly
 
-#[no_coverage]
-//~^ WARN: `#[no_coverage]` does not propagate into items and must be applied to the contained functions directly
+#[coverage(off)]
+//~^ WARN: `#[coverage]` does not propagate into items and must be applied to the contained functions directly
 trait Trait {
-    #[no_coverage] //~ ERROR `#[no_coverage]` must be applied to coverable code
+    #[coverage(off)] //~ ERROR `#[coverage]` must be applied to coverable code
     const X: u32;
 
-    #[no_coverage] //~ ERROR `#[no_coverage]` must be applied to coverable code
+    #[coverage(off)] //~ ERROR `#[coverage]` must be applied to coverable code
     type T;
 
     type U;
 }
 
-#[no_coverage]
-//~^ WARN: `#[no_coverage]` does not propagate into items and must be applied to the contained functions directly
+#[coverage(off)]
+//~^ WARN: `#[coverage]` does not propagate into items and must be applied to the contained functions directly
 impl Trait for () {
     const X: u32 = 0;
 
-    #[no_coverage] //~ ERROR `#[no_coverage]` must be applied to coverable code
+    #[coverage(off)] //~ ERROR `#[coverage]` must be applied to coverable code
     type T = Self;
 
-    #[no_coverage] //~ ERROR `#[no_coverage]` must be applied to coverable code
+    #[coverage(off)] //~ ERROR `#[coverage]` must be applied to coverable code
     type U = impl Trait; //~ ERROR unconstrained opaque type
 }
 
 extern "C" {
-    #[no_coverage] //~ ERROR `#[no_coverage]` must be applied to coverable code
+    #[coverage(off)] //~ ERROR `#[coverage]` must be applied to coverable code
     static X: u32;
 
-    #[no_coverage] //~ ERROR `#[no_coverage]` must be applied to coverable code
+    #[coverage(off)] //~ ERROR `#[coverage]` must be applied to coverable code
     type T;
 }
 
-#[no_coverage]
+#[coverage(off)]
 fn main() {
-    #[no_coverage]
-    //~^ WARN `#[no_coverage]` may only be applied to function definitions
+    #[coverage(off)]
+    //~^ WARN `#[coverage]` may only be applied to function definitions
     let _ = ();
 
     match () {
-        #[no_coverage]
-        //~^ WARN `#[no_coverage]` may only be applied to function definitions
+        #[coverage(off)]
+        //~^ WARN `#[coverage]` may only be applied to function definitions
         () => (),
     }
 
-    #[no_coverage]
-    //~^ WARN `#[no_coverage]` may only be applied to function definitions
+    #[coverage(off)]
+    //~^ WARN `#[coverage]` may only be applied to function definitions
     return ();
 }
diff --git a/tests/ui/lint/no-coverage.stderr b/tests/ui/lint/no-coverage.stderr
index 404efbeac1e..a87b0fb49f0 100644
--- a/tests/ui/lint/no-coverage.stderr
+++ b/tests/ui/lint/no-coverage.stderr
@@ -1,8 +1,8 @@
-warning: `#[no_coverage]` does not propagate into items and must be applied to the contained functions directly
+warning: `#[coverage]` does not propagate into items and must be applied to the contained functions directly
   --> $DIR/no-coverage.rs:8:1
    |
-LL | #[no_coverage]
-   | ^^^^^^^^^^^^^^
+LL | #[coverage(off)]
+   | ^^^^^^^^^^^^^^^^
    |
 note: the lint level is defined here
   --> $DIR/no-coverage.rs:4:9
@@ -10,83 +10,83 @@ note: the lint level is defined here
 LL | #![warn(unused_attributes)]
    |         ^^^^^^^^^^^^^^^^^
 
-warning: `#[no_coverage]` does not propagate into items and must be applied to the contained functions directly
+warning: `#[coverage]` does not propagate into items and must be applied to the contained functions directly
   --> $DIR/no-coverage.rs:20:1
    |
-LL | #[no_coverage]
-   | ^^^^^^^^^^^^^^
+LL | #[coverage(off)]
+   | ^^^^^^^^^^^^^^^^
 
-warning: `#[no_coverage]` may only be applied to function definitions
+warning: `#[coverage]` may only be applied to function definitions
   --> $DIR/no-coverage.rs:42:5
    |
-LL |     #[no_coverage]
-   |     ^^^^^^^^^^^^^^
+LL |     #[coverage(off)]
+   |     ^^^^^^^^^^^^^^^^
 
-warning: `#[no_coverage]` may only be applied to function definitions
+warning: `#[coverage]` may only be applied to function definitions
   --> $DIR/no-coverage.rs:47:9
    |
-LL |         #[no_coverage]
-   |         ^^^^^^^^^^^^^^
+LL |         #[coverage(off)]
+   |         ^^^^^^^^^^^^^^^^
 
-warning: `#[no_coverage]` may only be applied to function definitions
+warning: `#[coverage]` may only be applied to function definitions
   --> $DIR/no-coverage.rs:52:5
    |
-LL |     #[no_coverage]
-   |     ^^^^^^^^^^^^^^
+LL |     #[coverage(off)]
+   |     ^^^^^^^^^^^^^^^^
 
-error[E0788]: `#[no_coverage]` must be applied to coverable code
+error[E0788]: `#[coverage]` must be applied to coverable code
   --> $DIR/no-coverage.rs:11:5
    |
-LL |     #[no_coverage]
-   |     ^^^^^^^^^^^^^^
+LL |     #[coverage(off)]
+   |     ^^^^^^^^^^^^^^^^
 LL |     const X: u32;
    |     ------------- not coverable code
 
-error[E0788]: `#[no_coverage]` must be applied to coverable code
+error[E0788]: `#[coverage]` must be applied to coverable code
   --> $DIR/no-coverage.rs:14:5
    |
-LL |     #[no_coverage]
-   |     ^^^^^^^^^^^^^^
+LL |     #[coverage(off)]
+   |     ^^^^^^^^^^^^^^^^
 LL |     type T;
    |     ------- not coverable code
 
-error[E0788]: `#[no_coverage]` must be applied to coverable code
+error[E0788]: `#[coverage]` must be applied to coverable code
   --> $DIR/no-coverage.rs:25:5
    |
-LL |     #[no_coverage]
-   |     ^^^^^^^^^^^^^^
+LL |     #[coverage(off)]
+   |     ^^^^^^^^^^^^^^^^
 LL |     type T = Self;
    |     -------------- not coverable code
 
-error[E0788]: `#[no_coverage]` must be applied to coverable code
+error[E0788]: `#[coverage]` must be applied to coverable code
   --> $DIR/no-coverage.rs:28:5
    |
-LL |     #[no_coverage]
-   |     ^^^^^^^^^^^^^^
+LL |     #[coverage(off)]
+   |     ^^^^^^^^^^^^^^^^
 LL |     type U = impl Trait;
    |     -------------------- not coverable code
 
-error[E0788]: `#[no_coverage]` must be applied to coverable code
+error[E0788]: `#[coverage]` must be applied to coverable code
   --> $DIR/no-coverage.rs:33:5
    |
-LL |     #[no_coverage]
-   |     ^^^^^^^^^^^^^^
+LL |     #[coverage(off)]
+   |     ^^^^^^^^^^^^^^^^
 LL |     static X: u32;
    |     -------------- not coverable code
 
-error[E0788]: `#[no_coverage]` must be applied to coverable code
+error[E0788]: `#[coverage]` must be applied to coverable code
   --> $DIR/no-coverage.rs:36:5
    |
-LL |     #[no_coverage]
-   |     ^^^^^^^^^^^^^^
+LL |     #[coverage(off)]
+   |     ^^^^^^^^^^^^^^^^
 LL |     type T;
    |     ------- not coverable code
 
-warning: `#[no_coverage]` does not propagate into items and must be applied to the contained functions directly
+warning: `#[coverage]` does not propagate into items and must be applied to the contained functions directly
   --> $DIR/no-coverage.rs:5:1
    |
-LL | #![no_coverage]
-   | ^^^^^^^^^^^^^^^
+LL | #![coverage(off)]
+   | ^^^^^^^^^^^^^^^^^
 
 error: unconstrained opaque type
   --> $DIR/no-coverage.rs:29:14
diff --git a/tests/ui/lint/ptr_null_checks.rs b/tests/ui/lint/ptr_null_checks.rs
index e677ea3094d..3028084e962 100644
--- a/tests/ui/lint/ptr_null_checks.rs
+++ b/tests/ui/lint/ptr_null_checks.rs
@@ -38,15 +38,15 @@ fn main() {
     if (&mut 8 as *mut i32).is_null() {}
     //~^ WARN references are not nullable
     if ptr::from_mut(&mut 8).is_null() {}
-    //~^ WARN references are not nullable
+    //~^ WARN call is never null
     if (&8 as *const i32).is_null() {}
     //~^ WARN references are not nullable
     if ptr::from_ref(&8).is_null() {}
-    //~^ WARN references are not nullable
+    //~^ WARN call is never null
     if ptr::from_ref(&8).cast_mut().is_null() {}
-    //~^ WARN references are not nullable
+    //~^ WARN call is never null
     if (ptr::from_ref(&8).cast_mut() as *mut i32).is_null() {}
-    //~^ WARN references are not nullable
+    //~^ WARN call is never null
     if (&8 as *const i32) == std::ptr::null() {}
     //~^ WARN references are not nullable
     let ref_num = &8;
@@ -65,6 +65,12 @@ fn main() {
     if (&*{ static_i32() } as *const i32).is_null() {}
     //~^ WARN references are not nullable
 
+    // ---------------- Functions -------------------
+    if ptr::NonNull::new(&mut 8).unwrap().as_ptr().is_null() {}
+    //~^ WARN call is never null
+    if ptr::NonNull::<u8>::dangling().as_ptr().is_null() {}
+    //~^ WARN call is never null
+
     // ----------------------------------------------
     const ZPTR: *const () = 0 as *const _;
     const NOT_ZPTR: *const () = 1 as *const _;
diff --git a/tests/ui/lint/ptr_null_checks.stderr b/tests/ui/lint/ptr_null_checks.stderr
index 3cee1804b62..0edc1b86536 100644
--- a/tests/ui/lint/ptr_null_checks.stderr
+++ b/tests/ui/lint/ptr_null_checks.stderr
@@ -117,13 +117,11 @@ LL |     if (&mut 8 as *mut i32).is_null() {}
    |         |
    |         expression has type `&mut i32`
 
-warning: references are not nullable, so checking them for null will always return false
+warning: returned pointer of `from_mut` call is never null, so checking it for null will always return false
   --> $DIR/ptr_null_checks.rs:40:8
    |
 LL |     if ptr::from_mut(&mut 8).is_null() {}
-   |        ^^^^^^^^^^^^^^------^^^^^^^^^^^
-   |                      |
-   |                      expression has type `&mut i32`
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: references are not nullable, so checking them for null will always return false
   --> $DIR/ptr_null_checks.rs:42:8
@@ -133,29 +131,23 @@ LL |     if (&8 as *const i32).is_null() {}
    |         |
    |         expression has type `&i32`
 
-warning: references are not nullable, so checking them for null will always return false
+warning: returned pointer of `from_ref` call is never null, so checking it for null will always return false
   --> $DIR/ptr_null_checks.rs:44:8
    |
 LL |     if ptr::from_ref(&8).is_null() {}
-   |        ^^^^^^^^^^^^^^--^^^^^^^^^^^
-   |                      |
-   |                      expression has type `&i32`
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-warning: references are not nullable, so checking them for null will always return false
+warning: returned pointer of `from_ref` call is never null, so checking it for null will always return false
   --> $DIR/ptr_null_checks.rs:46:8
    |
 LL |     if ptr::from_ref(&8).cast_mut().is_null() {}
-   |        ^^^^^^^^^^^^^^--^^^^^^^^^^^^^^^^^^^^^^
-   |                      |
-   |                      expression has type `&i32`
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-warning: references are not nullable, so checking them for null will always return false
+warning: returned pointer of `from_ref` call is never null, so checking it for null will always return false
   --> $DIR/ptr_null_checks.rs:48:8
    |
 LL |     if (ptr::from_ref(&8).cast_mut() as *mut i32).is_null() {}
-   |        ^^^^^^^^^^^^^^^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |                       |
-   |                       expression has type `&i32`
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: references are not nullable, so checking them for null will always return false
   --> $DIR/ptr_null_checks.rs:50:8
@@ -221,5 +213,17 @@ LL |     if (&*{ static_i32() } as *const i32).is_null() {}
    |         |
    |         expression has type `&i32`
 
-warning: 25 warnings emitted
+warning: returned pointer of `as_ptr` call is never null, so checking it for null will always return false
+  --> $DIR/ptr_null_checks.rs:69:8
+   |
+LL |     if ptr::NonNull::new(&mut 8).unwrap().as_ptr().is_null() {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: returned pointer of `as_ptr` call is never null, so checking it for null will always return false
+  --> $DIR/ptr_null_checks.rs:71:8
+   |
+LL |     if ptr::NonNull::<u8>::dangling().as_ptr().is_null() {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: 27 warnings emitted
 
diff --git a/tests/ui/lint/reference_casting.rs b/tests/ui/lint/reference_casting.rs
index 92d985948ec..7745d4ef4c3 100644
--- a/tests/ui/lint/reference_casting.rs
+++ b/tests/ui/lint/reference_casting.rs
@@ -36,6 +36,10 @@ unsafe fn ref_to_mut() {
     //~^ ERROR casting `&T` to `&mut T` is undefined behavior
     let _num = &mut *std::mem::transmute::<_, *mut i32>(num);
     //~^ ERROR casting `&T` to `&mut T` is undefined behavior
+    let _num = &mut *std::cell::UnsafeCell::raw_get(
+    //~^ ERROR casting `&T` to `&mut T` is undefined behavior
+        num as *const i32 as *const std::cell::UnsafeCell<i32>
+    );
 
     let deferred = num as *const i32 as *mut i32;
     let _num = &mut *deferred;
@@ -50,6 +54,16 @@ unsafe fn ref_to_mut() {
         &mut *((this as *const _) as *mut _)
         //~^ ERROR casting `&T` to `&mut T` is undefined behavior
     }
+
+    fn as_mut<T>(x: &T) -> &mut T {
+        unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
+        //~^ ERROR casting `&T` to `&mut T` is undefined behavior
+    }
+
+    fn as_mut_i32(x: &i32) -> &mut i32 {
+        unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
+        //~^ ERROR casting `&T` to `&mut T` is undefined behavior
+    }
 }
 
 unsafe fn assign_to_ref() {
@@ -111,6 +125,20 @@ unsafe fn no_warn() {
     let mut value = 3;
     let value: *const i32 = &mut value;
     *(value as *const i16 as *mut i16) = 42;
+
+    fn safe_as_mut<T>(x: &std::cell::UnsafeCell<T>) -> &mut T {
+        unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
+    }
+
+    fn cell_as_mut(x: &std::cell::Cell<i32>) -> &mut i32 {
+        unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
+    }
+
+    #[repr(transparent)]
+    struct DoesContainUnsafeCell(std::cell::UnsafeCell<i32>);
+    fn safe_as_mut2(x: &DoesContainUnsafeCell) -> &mut DoesContainUnsafeCell {
+        unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
+    }
 }
 
 fn main() {}
diff --git a/tests/ui/lint/reference_casting.stderr b/tests/ui/lint/reference_casting.stderr
index 47b95460ec3..1189942c809 100644
--- a/tests/ui/lint/reference_casting.stderr
+++ b/tests/ui/lint/reference_casting.stderr
@@ -80,7 +80,19 @@ LL |     let _num = &mut *std::mem::transmute::<_, *mut i32>(num);
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:41:16
+  --> $DIR/reference_casting.rs:39:16
+   |
+LL |       let _num = &mut *std::cell::UnsafeCell::raw_get(
+   |  ________________^
+LL | |
+LL | |         num as *const i32 as *const std::cell::UnsafeCell<i32>
+LL | |     );
+   | |_____^
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
+
+error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
+  --> $DIR/reference_casting.rs:45:16
    |
 LL |     let deferred = num as *const i32 as *mut i32;
    |                    ----------------------------- casting happend here
@@ -90,7 +102,7 @@ LL |     let _num = &mut *deferred;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:44:16
+  --> $DIR/reference_casting.rs:48:16
    |
 LL |     let deferred = (std::ptr::from_ref(num) as *const i32 as *const i32).cast_mut() as *mut i32;
    |                    ---------------------------------------------------------------------------- casting happend here
@@ -100,7 +112,7 @@ LL |     let _num = &mut *deferred;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:46:16
+  --> $DIR/reference_casting.rs:50:16
    |
 LL |     let _num = &mut *(num as *const _ as usize as *mut i32);
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -108,15 +120,31 @@ LL |     let _num = &mut *(num as *const _ as usize as *mut i32);
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:50:9
+  --> $DIR/reference_casting.rs:54:9
    |
 LL |         &mut *((this as *const _) as *mut _)
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
+error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
+  --> $DIR/reference_casting.rs:59:18
+   |
+LL |         unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
+
+error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
+  --> $DIR/reference_casting.rs:64:18
+   |
+LL |         unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
+
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:60:5
+  --> $DIR/reference_casting.rs:74:5
    |
 LL |     *(a as *const _ as *mut _) = String::from("Replaced");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -124,7 +152,7 @@ LL |     *(a as *const _ as *mut _) = String::from("Replaced");
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:62:5
+  --> $DIR/reference_casting.rs:76:5
    |
 LL |     *(a as *const _ as *mut String) += " world";
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -132,7 +160,7 @@ LL |     *(a as *const _ as *mut String) += " world";
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:64:5
+  --> $DIR/reference_casting.rs:78:5
    |
 LL |     *std::ptr::from_ref(num).cast_mut() += 1;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -140,7 +168,7 @@ LL |     *std::ptr::from_ref(num).cast_mut() += 1;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:66:5
+  --> $DIR/reference_casting.rs:80:5
    |
 LL |     *std::ptr::from_ref({ num }).cast_mut() += 1;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -148,7 +176,7 @@ LL |     *std::ptr::from_ref({ num }).cast_mut() += 1;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:68:5
+  --> $DIR/reference_casting.rs:82:5
    |
 LL |     *{ std::ptr::from_ref(num) }.cast_mut() += 1;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -156,7 +184,7 @@ LL |     *{ std::ptr::from_ref(num) }.cast_mut() += 1;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:70:5
+  --> $DIR/reference_casting.rs:84:5
    |
 LL |     *(std::ptr::from_ref({ num }) as *mut i32) += 1;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -164,7 +192,7 @@ LL |     *(std::ptr::from_ref({ num }) as *mut i32) += 1;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:72:5
+  --> $DIR/reference_casting.rs:86:5
    |
 LL |     *std::mem::transmute::<_, *mut i32>(num) += 1;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -172,7 +200,7 @@ LL |     *std::mem::transmute::<_, *mut i32>(num) += 1;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:74:5
+  --> $DIR/reference_casting.rs:88:5
    |
 LL | /     std::ptr::write(
 LL | |
@@ -184,7 +212,7 @@ LL | |     );
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:81:5
+  --> $DIR/reference_casting.rs:95:5
    |
 LL |     let value = num as *const i32 as *mut i32;
    |                 ----------------------------- casting happend here
@@ -194,7 +222,7 @@ LL |     *value = 1;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:83:5
+  --> $DIR/reference_casting.rs:97:5
    |
 LL |     *(num as *const i32).cast::<i32>().cast_mut() = 2;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -202,7 +230,7 @@ LL |     *(num as *const i32).cast::<i32>().cast_mut() = 2;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:85:5
+  --> $DIR/reference_casting.rs:99:5
    |
 LL |     *(num as *const _ as usize as *mut i32) = 2;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -210,7 +238,7 @@ LL |     *(num as *const _ as usize as *mut i32) = 2;
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:87:5
+  --> $DIR/reference_casting.rs:101:5
    |
 LL |     let value = num as *const i32 as *mut i32;
    |                 ----------------------------- casting happend here
@@ -221,7 +249,7 @@ LL |     std::ptr::write(value, 2);
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:89:5
+  --> $DIR/reference_casting.rs:103:5
    |
 LL |     let value = num as *const i32 as *mut i32;
    |                 ----------------------------- casting happend here
@@ -232,7 +260,7 @@ LL |     std::ptr::write_unaligned(value, 2);
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:91:5
+  --> $DIR/reference_casting.rs:105:5
    |
 LL |     let value = num as *const i32 as *mut i32;
    |                 ----------------------------- casting happend here
@@ -243,12 +271,12 @@ LL |     std::ptr::write_volatile(value, 2);
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
 error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
-  --> $DIR/reference_casting.rs:95:9
+  --> $DIR/reference_casting.rs:109:9
    |
 LL |         *(this as *const _ as *mut _) = a;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
 
-error: aborting due to 29 previous errors
+error: aborting due to 32 previous errors
 
diff --git a/tests/ui/mir/issue-92893.rs b/tests/ui/mir/issue-92893.rs
index 635050f376c..6127d267ebc 100644
--- a/tests/ui/mir/issue-92893.rs
+++ b/tests/ui/mir/issue-92893.rs
@@ -1,7 +1,5 @@
 struct Bug<A = [(); (let a = (), 1).1]> {
-    //~^ `let` expressions are not supported here
-    //~| `let` expressions in this position are unstable [E0658]
-    //~| expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     a: A
 }
 
diff --git a/tests/ui/mir/issue-92893.stderr b/tests/ui/mir/issue-92893.stderr
index 4a0fcce31d7..6c1a9dc0317 100644
--- a/tests/ui/mir/issue-92893.stderr
+++ b/tests/ui/mir/issue-92893.stderr
@@ -3,24 +3,8 @@ error: expected expression, found `let` statement
    |
 LL | struct Bug<A = [(); (let a = (), 1).1]> {
    |                      ^^^
-
-error: `let` expressions are not supported here
-  --> $DIR/issue-92893.rs:1:22
-   |
-LL | struct Bug<A = [(); (let a = (), 1).1]> {
-   |                      ^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/issue-92893.rs:1:22
-   |
-LL | struct Bug<A = [(); (let a = (), 1).1]> {
-   |                      ^^^^^^^^^^
-   |
-   = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
-   = help: add `#![feature(let_chains)]` to the crate attributes to enable
-
-error: aborting due to 3 previous errors
+error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/mismatched_types/cast-rfc0401.stderr b/tests/ui/mismatched_types/cast-rfc0401.stderr
index 6b9ac3c5852..0cea60746bf 100644
--- a/tests/ui/mismatched_types/cast-rfc0401.stderr
+++ b/tests/ui/mismatched_types/cast-rfc0401.stderr
@@ -82,13 +82,13 @@ error[E0606]: casting `f32` as `*const u8` is invalid
 LL |     let _ = f as *const u8;
    |             ^^^^^^^^^^^^^^
 
-error[E0054]: cannot cast as `bool`
+error[E0054]: cannot cast `i32` as `bool`
   --> $DIR/cast-rfc0401.rs:39:13
    |
 LL |     let _ = 3_i32 as bool;
    |             ^^^^^^^^^^^^^ help: compare with zero instead: `3_i32 != 0`
 
-error[E0054]: cannot cast as `bool`
+error[E0054]: cannot cast `E` as `bool`
   --> $DIR/cast-rfc0401.rs:40:13
    |
 LL |     let _ = E::A as bool;
diff --git a/tests/ui/namespace/namespace-mix.stderr b/tests/ui/namespace/namespace-mix.stderr
index 3ac5e96c574..4eff08ead42 100644
--- a/tests/ui/namespace/namespace-mix.stderr
+++ b/tests/ui/namespace/namespace-mix.stderr
@@ -114,6 +114,11 @@ LL |     check(m1::S{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -128,6 +133,11 @@ LL |     check(m2::S{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -142,6 +152,11 @@ LL |     check(m2::S);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -156,6 +171,11 @@ LL |     check(xm1::S{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -170,6 +190,11 @@ LL |     check(xm2::S{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -184,6 +209,11 @@ LL |     check(xm2::S);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -198,6 +228,11 @@ LL |     check(m3::TS{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -212,6 +247,11 @@ LL |     check(m3::TS);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -226,6 +266,11 @@ LL |     check(m4::TS{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -240,6 +285,11 @@ LL |     check(m4::TS);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -254,6 +304,11 @@ LL |     check(xm3::TS{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -268,6 +323,11 @@ LL |     check(xm3::TS);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -282,6 +342,11 @@ LL |     check(xm4::TS{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -296,6 +361,11 @@ LL |     check(xm4::TS);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -310,6 +380,11 @@ LL |     check(m5::US{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -324,6 +399,11 @@ LL |     check(m5::US);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -338,6 +418,11 @@ LL |     check(m6::US{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -352,6 +437,11 @@ LL |     check(m6::US);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -366,6 +456,11 @@ LL |     check(xm5::US{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -380,6 +475,11 @@ LL |     check(xm5::US);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -394,6 +494,11 @@ LL |     check(xm6::US{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -408,6 +513,11 @@ LL |     check(xm6::US);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -422,6 +532,11 @@ LL |     check(m7::V{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -436,6 +551,11 @@ LL |     check(m8::V{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -450,6 +570,11 @@ LL |     check(m8::V);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -464,6 +589,11 @@ LL |     check(xm7::V{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -478,6 +608,11 @@ LL |     check(xm8::V{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -492,6 +627,11 @@ LL |     check(xm8::V);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -506,6 +646,11 @@ LL |     check(m9::TV{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -520,6 +665,11 @@ LL |     check(m9::TV);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -534,6 +684,11 @@ LL |     check(mA::TV{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -548,6 +703,11 @@ LL |     check(mA::TV);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -562,6 +722,11 @@ LL |     check(xm9::TV{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -576,6 +741,11 @@ LL |     check(xm9::TV);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -590,6 +760,11 @@ LL |     check(xmA::TV{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -604,6 +779,11 @@ LL |     check(xmA::TV);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -618,6 +798,11 @@ LL |     check(mB::UV{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -632,6 +817,11 @@ LL |     check(mB::UV);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -646,6 +836,11 @@ LL |     check(mC::UV{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -660,6 +855,11 @@ LL |     check(mC::UV);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -674,6 +874,11 @@ LL |     check(xmB::UV{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -688,6 +893,11 @@ LL |     check(xmB::UV);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -702,6 +912,11 @@ LL |     check(xmC::UV{});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
@@ -716,6 +931,11 @@ LL |     check(xmC::UV);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/namespace-mix.rs:20:1
+   |
+LL | trait Impossible {}
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `check`
   --> $DIR/namespace-mix.rs:21:13
    |
diff --git a/tests/ui/nested-ty-params.rs b/tests/ui/nested-ty-params.rs
index 85413acdb14..25bac1ba24b 100644
--- a/tests/ui/nested-ty-params.rs
+++ b/tests/ui/nested-ty-params.rs
@@ -1,4 +1,4 @@
-// error-pattern:can't use generic parameters from outer function
+// error-pattern:can't use generic parameters from outer item
 fn hd<U>(v: Vec<U> ) -> U {
     fn hd1(w: [U]) -> U { return w[0]; }
 
diff --git a/tests/ui/nested-ty-params.stderr b/tests/ui/nested-ty-params.stderr
index 8f4746f5ec3..a9cdec66719 100644
--- a/tests/ui/nested-ty-params.stderr
+++ b/tests/ui/nested-ty-params.stderr
@@ -1,22 +1,22 @@
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/nested-ty-params.rs:3:16
    |
 LL | fn hd<U>(v: Vec<U> ) -> U {
-   |       - type parameter from outer function
+   |       - type parameter from outer item
 LL |     fn hd1(w: [U]) -> U { return w[0]; }
-   |           -    ^ use of generic parameter from outer function
+   |           -    ^ use of generic parameter from outer item
    |           |
-   |           help: try using a local generic parameter instead: `<U>`
+   |           help: try introducing a local generic parameter here: `<U>`
 
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/nested-ty-params.rs:3:23
    |
 LL | fn hd<U>(v: Vec<U> ) -> U {
-   |       - type parameter from outer function
+   |       - type parameter from outer item
 LL |     fn hd1(w: [U]) -> U { return w[0]; }
-   |           -           ^ use of generic parameter from outer function
+   |           -           ^ use of generic parameter from outer item
    |           |
-   |           help: try using a local generic parameter instead: `<U>`
+   |           help: try introducing a local generic parameter here: `<U>`
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/never_type/feature-gate-never_type_fallback.stderr b/tests/ui/never_type/feature-gate-never_type_fallback.stderr
index 2db1cc4b776..56aafbb4ce8 100644
--- a/tests/ui/never_type/feature-gate-never_type_fallback.stderr
+++ b/tests/ui/never_type/feature-gate-never_type_fallback.stderr
@@ -8,6 +8,11 @@ LL |     foo(panic!())
    |     |   this tail expression is of type `()`
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/feature-gate-never_type_fallback.rs:7:1
+   |
+LL | trait T {}
+   | ^^^^^^^
 note: required by a bound in `foo`
   --> $DIR/feature-gate-never_type_fallback.rs:13:16
    |
diff --git a/tests/ui/never_type/impl_trait_fallback3.stderr b/tests/ui/never_type/impl_trait_fallback3.stderr
index 5d5d216fb9b..821d141569e 100644
--- a/tests/ui/never_type/impl_trait_fallback3.stderr
+++ b/tests/ui/never_type/impl_trait_fallback3.stderr
@@ -3,6 +3,12 @@ error[E0277]: the trait bound `(): T` is not satisfied
    |
 LL | fn a() -> Foo {
    |           ^^^ the trait `T` is not implemented for `()`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/impl_trait_fallback3.rs:5:1
+   |
+LL | trait T {
+   | ^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/tests/ui/never_type/impl_trait_fallback4.stderr b/tests/ui/never_type/impl_trait_fallback4.stderr
index f2e216e9044..67421ba8da7 100644
--- a/tests/ui/never_type/impl_trait_fallback4.stderr
+++ b/tests/ui/never_type/impl_trait_fallback4.stderr
@@ -3,6 +3,12 @@ error[E0277]: the trait bound `(): T` is not satisfied
    |
 LL | fn foo() -> impl T {
    |             ^^^^^^ the trait `T` is not implemented for `()`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/impl_trait_fallback4.rs:3:1
+   |
+LL | trait T {
+   | ^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/tests/ui/on-unimplemented/on-trait.stderr b/tests/ui/on-unimplemented/on-trait.stderr
index 4b040f1ac5a..4847a1a5a61 100644
--- a/tests/ui/on-unimplemented/on-trait.stderr
+++ b/tests/ui/on-unimplemented/on-trait.stderr
@@ -5,6 +5,11 @@ LL |     let y: Option<Vec<u8>> = collect(x.iter()); // this should give approxi
    |                              ^^^^^^^ a collection of type `Option<Vec<u8>>` cannot be built from an iterator over elements of type `&u8`
    |
    = help: the trait `MyFromIterator<&u8>` is not implemented for `Option<Vec<u8>>`
+help: this trait has no implementations, consider adding one
+  --> $DIR/on-trait.rs:17:1
+   |
+LL | trait MyFromIterator<A> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^
 note: required by a bound in `collect`
   --> $DIR/on-trait.rs:22:39
    |
@@ -18,6 +23,11 @@ LL |     let x: String = foobar();
    |                     ^^^^^^ test error `String` with `u8` `_` `u32` in `Foo`
    |
    = help: the trait `Foo<u8, _, u32>` is not implemented for `String`
+help: this trait has no implementations, consider adding one
+  --> $DIR/on-trait.rs:7:3
+   |
+LL |   pub trait Foo<Bar, Baz, Quux> {}
+   |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 note: required by a bound in `foobar`
   --> $DIR/on-trait.rs:12:24
    |
diff --git a/tests/ui/on-unimplemented/parent-label.stderr b/tests/ui/on-unimplemented/parent-label.stderr
index 8cd7412fd9d..101a41512d2 100644
--- a/tests/ui/on-unimplemented/parent-label.stderr
+++ b/tests/ui/on-unimplemented/parent-label.stderr
@@ -8,6 +8,11 @@ LL |         f(Foo {});
    |         |
    |         required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/parent-label.rs:6:1
+   |
+LL | trait Trait {}
+   | ^^^^^^^^^^^
 note: required by a bound in `f`
   --> $DIR/parent-label.rs:10:9
    |
@@ -24,6 +29,11 @@ LL |             f(Foo {});
    |             |
    |             required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/parent-label.rs:6:1
+   |
+LL | trait Trait {}
+   | ^^^^^^^^^^^
 note: required by a bound in `f`
   --> $DIR/parent-label.rs:10:9
    |
@@ -41,6 +51,11 @@ LL |             f(Foo {});
    |             |
    |             required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/parent-label.rs:6:1
+   |
+LL | trait Trait {}
+   | ^^^^^^^^^^^
 note: required by a bound in `f`
   --> $DIR/parent-label.rs:10:9
    |
@@ -58,6 +73,11 @@ LL |     f(Foo {});
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/parent-label.rs:6:1
+   |
+LL | trait Trait {}
+   | ^^^^^^^^^^^
 note: required by a bound in `f`
   --> $DIR/parent-label.rs:10:9
    |
diff --git a/tests/ui/parser/default-unmatched.stderr b/tests/ui/parser/default-unmatched.stderr
index 331e003f63c..de142411d69 100644
--- a/tests/ui/parser/default-unmatched.stderr
+++ b/tests/ui/parser/default-unmatched.stderr
@@ -11,6 +11,8 @@ error: expected item, found reserved keyword `do`
    |
 LL |     default do
    |             ^^ expected item
+   |
+   = note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/parser/impl-parsing.stderr b/tests/ui/parser/impl-parsing.stderr
index 755addf1452..a57cc075ccc 100644
--- a/tests/ui/parser/impl-parsing.stderr
+++ b/tests/ui/parser/impl-parsing.stderr
@@ -35,6 +35,8 @@ error: expected item, found keyword `unsafe`
    |
 LL | default unsafe FAIL
    |         ^^^^^^ expected item
+   |
+   = note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
 
 error: aborting due to 6 previous errors
 
diff --git a/tests/ui/parser/issue-101477-enum.stderr b/tests/ui/parser/issue-101477-enum.stderr
index 1edca391e8f..94130671f1c 100644
--- a/tests/ui/parser/issue-101477-enum.stderr
+++ b/tests/ui/parser/issue-101477-enum.stderr
@@ -11,6 +11,8 @@ error: expected item, found `==`
    |
 LL |     B == 2
    |       ^^ expected item
+   |
+   = note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/parser/issues/issue-113110-non-item-at-module-root.rs b/tests/ui/parser/issues/issue-113110-non-item-at-module-root.rs
new file mode 100644
index 00000000000..3b6f4304369
--- /dev/null
+++ b/tests/ui/parser/issues/issue-113110-non-item-at-module-root.rs
@@ -0,0 +1 @@
+ 5 //~ ERROR expected item, found `5`
diff --git a/tests/ui/parser/issues/issue-113110-non-item-at-module-root.stderr b/tests/ui/parser/issues/issue-113110-non-item-at-module-root.stderr
new file mode 100644
index 00000000000..0789c4548a0
--- /dev/null
+++ b/tests/ui/parser/issues/issue-113110-non-item-at-module-root.stderr
@@ -0,0 +1,10 @@
+error: expected item, found `5`
+  --> $DIR/issue-113110-non-item-at-module-root.rs:1:2
+   |
+LL |  5
+   |  ^ expected item
+   |
+   = note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
+
+error: aborting due to previous error
+
diff --git a/tests/ui/parser/issues/issue-115780-pat-lt-bracket-in-macro-call.rs b/tests/ui/parser/issues/issue-115780-pat-lt-bracket-in-macro-call.rs
new file mode 100644
index 00000000000..3421333b8a0
--- /dev/null
+++ b/tests/ui/parser/issues/issue-115780-pat-lt-bracket-in-macro-call.rs
@@ -0,0 +1,21 @@
+// Regression test for issue #115780.
+// Ensure that we don't emit a parse error for the token sequence `Ident "<" Ty` in pattern position
+// if we are inside a macro call since it can be valid input for a subsequent macro rule.
+// See also #103534.
+
+// check-pass
+
+macro_rules! mdo {
+    ($p: pat =<< $e: expr ; $( $t: tt )*) => {
+        $e.and_then(|$p| mdo! { $( $t )* })
+    };
+    (ret<$ty: ty> $e: expr;) => { Some::<$ty>($e) };
+}
+
+fn main() {
+    mdo! {
+        x_val =<< Some(0);
+        y_val =<< Some(1);
+        ret<(i32, i32)> (x_val, y_val);
+    };
+}
diff --git a/tests/ui/parser/issues/issue-17904-2.stderr b/tests/ui/parser/issues/issue-17904-2.stderr
index 9c7fdf6ccb4..7185a5e5752 100644
--- a/tests/ui/parser/issues/issue-17904-2.stderr
+++ b/tests/ui/parser/issues/issue-17904-2.stderr
@@ -3,6 +3,8 @@ error: expected item, found keyword `where`
    |
 LL | struct Bar<T> { x: T } where T: Copy
    |                        ^^^^^ expected item
+   |
+   = note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
 
 error: aborting due to previous error
 
diff --git a/tests/ui/parser/issues/issue-43196.stderr b/tests/ui/parser/issues/issue-43196.stderr
index 4f7ed5cc6fd..15bbb158cd1 100644
--- a/tests/ui/parser/issues/issue-43196.stderr
+++ b/tests/ui/parser/issues/issue-43196.stderr
@@ -11,6 +11,8 @@ error: expected item, found `|`
    |
 LL | |
    | ^ expected item
+   |
+   = note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/parser/issues/issue-62913.stderr b/tests/ui/parser/issues/issue-62913.stderr
index 6f385e8dc17..c33e4683728 100644
--- a/tests/ui/parser/issues/issue-62913.stderr
+++ b/tests/ui/parser/issues/issue-62913.stderr
@@ -17,6 +17,8 @@ error: expected item, found `"\u\"`
    |
 LL | "\u\"
    | ^^^^^^ expected item
+   |
+   = note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/parser/issues/issue-68890.stderr b/tests/ui/parser/issues/issue-68890.stderr
index 2a3bf6b41f0..0d7b53a67c5 100644
--- a/tests/ui/parser/issues/issue-68890.stderr
+++ b/tests/ui/parser/issues/issue-68890.stderr
@@ -15,6 +15,8 @@ error: expected item, found `)`
    |
 LL | enum e{A((?'a a+?+l))}
    |                     ^ expected item
+   |
+   = note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/parser/shebang/shebang-doc-comment.stderr b/tests/ui/parser/shebang/shebang-doc-comment.stderr
index 2227d45ec5a..a36b2a2f72b 100644
--- a/tests/ui/parser/shebang/shebang-doc-comment.stderr
+++ b/tests/ui/parser/shebang/shebang-doc-comment.stderr
@@ -3,6 +3,8 @@ error: expected item, found `[`
    |
 LL | [allow(unused_variables)]
    | ^ expected item
+   |
+   = note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
 
 error: aborting due to previous error
 
diff --git a/tests/ui/parser/virtual-structs.stderr b/tests/ui/parser/virtual-structs.stderr
index a5211d83f84..268fc105796 100644
--- a/tests/ui/parser/virtual-structs.stderr
+++ b/tests/ui/parser/virtual-structs.stderr
@@ -3,6 +3,8 @@ error: expected item, found reserved keyword `virtual`
    |
 LL | virtual struct SuperStruct {
    | ^^^^^^^ expected item
+   |
+   = note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
 
 error: aborting due to previous error
 
diff --git a/tests/ui/pattern/issue-114896.rs b/tests/ui/pattern/issue-114896.rs
new file mode 100644
index 00000000000..cde37f658d6
--- /dev/null
+++ b/tests/ui/pattern/issue-114896.rs
@@ -0,0 +1,7 @@
+fn main() {
+    fn x(a: &char) {
+        let &b = a;
+        b.make_ascii_uppercase();
+//~^ cannot borrow `b` as mutable, as it is not declared as mutable
+    }
+}
diff --git a/tests/ui/pattern/issue-114896.stderr b/tests/ui/pattern/issue-114896.stderr
new file mode 100644
index 00000000000..ffeb7bc1365
--- /dev/null
+++ b/tests/ui/pattern/issue-114896.stderr
@@ -0,0 +1,11 @@
+error[E0596]: cannot borrow `b` as mutable, as it is not declared as mutable
+  --> $DIR/issue-114896.rs:4:9
+   |
+LL |         let &b = a;
+   |             -- help: consider changing this to be mutable: `&(mut b)`
+LL |         b.make_ascii_uppercase();
+   |         ^ cannot borrow as mutable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/tests/ui/privacy/private-bounds-locally-allowed.rs b/tests/ui/privacy/private-bounds-locally-allowed.rs
new file mode 100644
index 00000000000..96a007a64f6
--- /dev/null
+++ b/tests/ui/privacy/private-bounds-locally-allowed.rs
@@ -0,0 +1,7 @@
+// check-pass
+// compile-flags: --crate-type=lib
+
+#[allow(private_bounds)]
+pub trait Foo: FooImpl {}
+
+trait FooImpl {}
diff --git a/tests/ui/privacy/unnameable_types.rs b/tests/ui/privacy/unnameable_types.rs
index e35aaec5b3b..c6c5561c3c4 100644
--- a/tests/ui/privacy/unnameable_types.rs
+++ b/tests/ui/privacy/unnameable_types.rs
@@ -20,10 +20,10 @@ mod m {
     }
 }
 
-pub trait Voldemort<T> {}
+pub trait Unnameable<T> {}
 
-impl Voldemort<m::PubStruct> for i32 {}
-impl Voldemort<m::PubE> for i32 {}
-impl<T> Voldemort<T> for u32 where T: m::PubTr {}
+impl Unnameable<m::PubStruct> for i32 {}
+impl Unnameable<m::PubE> for i32 {}
+impl<T> Unnameable<T> for u32 where T: m::PubTr {}
 
 fn main() {}
diff --git a/tests/ui/proc-macro/bad-projection.stderr b/tests/ui/proc-macro/bad-projection.stderr
index 8a8246376fe..8716defa17a 100644
--- a/tests/ui/proc-macro/bad-projection.stderr
+++ b/tests/ui/proc-macro/bad-projection.stderr
@@ -3,6 +3,12 @@ error[E0277]: the trait bound `(): Project` is not satisfied
    |
 LL | pub fn uwu() -> <() as Project>::Assoc {}
    |                 ^^^^^^^^^^^^^^^^^^^^^^ the trait `Project` is not implemented for `()`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/bad-projection.rs:9:1
+   |
+LL | trait Project {
+   | ^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/tests/ui/pub/pub-restricted-error-fn.stderr b/tests/ui/pub/pub-restricted-error-fn.stderr
index 0511a821a7a..ca5d8e1b58e 100644
--- a/tests/ui/pub/pub-restricted-error-fn.stderr
+++ b/tests/ui/pub/pub-restricted-error-fn.stderr
@@ -11,6 +11,8 @@ error: expected item, found `(`
    |
 LL | pub(crate) () fn foo() {}
    |            ^ expected item
+   |
+   = note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/range/range-1.stderr b/tests/ui/range/range-1.stderr
index ecfc56961ee..96c1ffb2f7e 100644
--- a/tests/ui/range/range-1.stderr
+++ b/tests/ui/range/range-1.stderr
@@ -19,7 +19,7 @@ LL |     for i in false..true {}
              i64
              i128
              usize
-           and 6 others
+           and 8 others
    = note: required for `std::ops::Range<bool>` to implement `Iterator`
    = note: required for `std::ops::Range<bool>` to implement `IntoIterator`
 
diff --git a/tests/ui/resolve/bad-type-env-capture.stderr b/tests/ui/resolve/bad-type-env-capture.stderr
index b6282c2d070..941b6b7a68c 100644
--- a/tests/ui/resolve/bad-type-env-capture.stderr
+++ b/tests/ui/resolve/bad-type-env-capture.stderr
@@ -1,12 +1,12 @@
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/bad-type-env-capture.rs:2:15
    |
 LL | fn foo<T>() {
-   |        - type parameter from outer function
+   |        - type parameter from outer item
 LL |     fn bar(b: T) { }
-   |           -   ^ use of generic parameter from outer function
+   |           -   ^ use of generic parameter from outer item
    |           |
-   |           help: try using a local generic parameter instead: `<T>`
+   |           help: try introducing a local generic parameter here: `<T>`
 
 error: aborting due to previous error
 
diff --git a/tests/ui/resolve/generic-params-from-outer-item-in-const-item.default.stderr b/tests/ui/resolve/generic-params-from-outer-item-in-const-item.default.stderr
new file mode 100644
index 00000000000..4f853829279
--- /dev/null
+++ b/tests/ui/resolve/generic-params-from-outer-item-in-const-item.default.stderr
@@ -0,0 +1,28 @@
+error[E0401]: can't use generic parameters from outer item
+  --> $DIR/generic-params-from-outer-item-in-const-item.rs:12:20
+   |
+LL | fn outer<T: Tr>() { // outer function
+   |          - type parameter from outer item
+LL |     const K: u32 = T::C;
+   |                    ^^^^ use of generic parameter from outer item
+
+error[E0401]: can't use generic parameters from outer item
+  --> $DIR/generic-params-from-outer-item-in-const-item.rs:19:24
+   |
+LL | impl<T> Tr for T { // outer impl block
+   |      - type parameter from outer item
+LL |     const C: u32 = {
+LL |         const I: u32 = T::C;
+   |                        ^^^^ use of generic parameter from outer item
+
+error[E0401]: can't use generic parameters from outer item
+  --> $DIR/generic-params-from-outer-item-in-const-item.rs:27:20
+   |
+LL | struct S<T: Tr>(U32<{ // outer struct
+   |          - type parameter from outer item
+LL |     const _: u32 = T::C;
+   |                    ^^^^ use of generic parameter from outer item
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0401`.
diff --git a/tests/ui/resolve/generic-params-from-outer-item-in-const-item.generic_const_items.stderr b/tests/ui/resolve/generic-params-from-outer-item-in-const-item.generic_const_items.stderr
new file mode 100644
index 00000000000..1cb55842bc6
--- /dev/null
+++ b/tests/ui/resolve/generic-params-from-outer-item-in-const-item.generic_const_items.stderr
@@ -0,0 +1,34 @@
+error[E0401]: can't use generic parameters from outer item
+  --> $DIR/generic-params-from-outer-item-in-const-item.rs:12:20
+   |
+LL | fn outer<T: Tr>() { // outer function
+   |          - type parameter from outer item
+LL |     const K: u32 = T::C;
+   |            -       ^^^^ use of generic parameter from outer item
+   |            |
+   |            help: try introducing a local generic parameter here: `<T>`
+
+error[E0401]: can't use generic parameters from outer item
+  --> $DIR/generic-params-from-outer-item-in-const-item.rs:19:24
+   |
+LL | impl<T> Tr for T { // outer impl block
+   |      - type parameter from outer item
+LL |     const C: u32 = {
+LL |         const I: u32 = T::C;
+   |                -       ^^^^ use of generic parameter from outer item
+   |                |
+   |                help: try introducing a local generic parameter here: `<T>`
+
+error[E0401]: can't use generic parameters from outer item
+  --> $DIR/generic-params-from-outer-item-in-const-item.rs:27:20
+   |
+LL | struct S<T: Tr>(U32<{ // outer struct
+   |          - type parameter from outer item
+LL |     const _: u32 = T::C;
+   |            -       ^^^^ use of generic parameter from outer item
+   |            |
+   |            help: try introducing a local generic parameter here: `<T>`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0401`.
diff --git a/tests/ui/resolve/generic-params-from-outer-item-in-const-item.rs b/tests/ui/resolve/generic-params-from-outer-item-in-const-item.rs
new file mode 100644
index 00000000000..e5647d72cba
--- /dev/null
+++ b/tests/ui/resolve/generic-params-from-outer-item-in-const-item.rs
@@ -0,0 +1,39 @@
+// Regression test for issue #115720.
+// If a const item contains generic params from an outer items, only suggest
+// turning the const item generic if the feature `generic_const_items` is enabled.
+
+// revisions: default generic_const_items
+
+#![cfg_attr(generic_const_items, feature(generic_const_items))]
+#![feature(generic_const_exprs)] // only used for the test case "outer struct"
+#![allow(incomplete_features)]
+
+fn outer<T: Tr>() { // outer function
+    const K: u32 = T::C;
+    //~^ ERROR can't use generic parameters from outer item
+    //[generic_const_items]~| HELP try introducing a local generic parameter here
+}
+
+impl<T> Tr for T { // outer impl block
+    const C: u32 = {
+        const I: u32 = T::C;
+        //~^ ERROR can't use generic parameters from outer item
+        //[generic_const_items]~| HELP try introducing a local generic parameter here
+        I
+    };
+}
+
+struct S<T: Tr>(U32<{ // outer struct
+    const _: u32 = T::C;
+    //~^ ERROR can't use generic parameters from outer item
+    //[generic_const_items]~| HELP try introducing a local generic parameter here
+    0
+}>);
+
+trait Tr {
+    const C: u32;
+}
+
+struct U32<const N: u32>;
+
+fn main() {}
diff --git a/tests/ui/resolve/issue-12796.rs b/tests/ui/resolve/issue-12796.rs
index 942d6b9a568..de3e73437f0 100644
--- a/tests/ui/resolve/issue-12796.rs
+++ b/tests/ui/resolve/issue-12796.rs
@@ -1,7 +1,7 @@
 trait Trait {
     fn outer(&self) {
         fn inner(_: &Self) {
-            //~^ ERROR can't use generic parameters from outer function
+            //~^ ERROR can't use generic parameters from outer item
         }
     }
 }
diff --git a/tests/ui/resolve/issue-12796.stderr b/tests/ui/resolve/issue-12796.stderr
index a01fd2d6542..ef59d00360b 100644
--- a/tests/ui/resolve/issue-12796.stderr
+++ b/tests/ui/resolve/issue-12796.stderr
@@ -1,10 +1,10 @@
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/issue-12796.rs:3:22
    |
 LL |         fn inner(_: &Self) {
    |                      ^^^^
    |                      |
-   |                      use of generic parameter from outer function
+   |                      use of generic parameter from outer item
    |                      can't use `Self` here
 
 error: aborting due to previous error
diff --git a/tests/ui/resolve/issue-3021-c.rs b/tests/ui/resolve/issue-3021-c.rs
index 94ed1fdf781..bd21d124423 100644
--- a/tests/ui/resolve/issue-3021-c.rs
+++ b/tests/ui/resolve/issue-3021-c.rs
@@ -1,8 +1,8 @@
 fn siphash<T>() {
 
     trait U {
-        fn g(&self, x: T) -> T;  //~ ERROR can't use generic parameters from outer function
-        //~^ ERROR can't use generic parameters from outer function
+        fn g(&self, x: T) -> T;  //~ ERROR can't use generic parameters from outer item
+        //~^ ERROR can't use generic parameters from outer item
     }
 }
 
diff --git a/tests/ui/resolve/issue-3021-c.stderr b/tests/ui/resolve/issue-3021-c.stderr
index 5176efc3a6b..537bbaf7b6a 100644
--- a/tests/ui/resolve/issue-3021-c.stderr
+++ b/tests/ui/resolve/issue-3021-c.stderr
@@ -1,24 +1,24 @@
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/issue-3021-c.rs:4:24
    |
 LL | fn siphash<T>() {
-   |            - type parameter from outer function
+   |            - type parameter from outer item
 LL |
 LL |     trait U {
-   |            - help: try using a local generic parameter instead: `<T>`
+   |            - help: try introducing a local generic parameter here: `<T>`
 LL |         fn g(&self, x: T) -> T;
-   |                        ^ use of generic parameter from outer function
+   |                        ^ use of generic parameter from outer item
 
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/issue-3021-c.rs:4:30
    |
 LL | fn siphash<T>() {
-   |            - type parameter from outer function
+   |            - type parameter from outer item
 LL |
 LL |     trait U {
-   |            - help: try using a local generic parameter instead: `<T>`
+   |            - help: try introducing a local generic parameter here: `<T>`
 LL |         fn g(&self, x: T) -> T;
-   |                              ^ use of generic parameter from outer function
+   |                              ^ use of generic parameter from outer item
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/resolve/issue-65025-extern-static-parent-generics.rs b/tests/ui/resolve/issue-65025-extern-static-parent-generics.rs
index ce45f630e48..4fa3f12d024 100644
--- a/tests/ui/resolve/issue-65025-extern-static-parent-generics.rs
+++ b/tests/ui/resolve/issue-65025-extern-static-parent-generics.rs
@@ -1,7 +1,7 @@
 unsafe fn foo<A>() {
     extern "C" {
         static baz: *const A;
-        //~^ ERROR can't use generic parameters from outer function
+        //~^ ERROR can't use generic parameters from outer item
     }
 
     let bar: *const u64 = core::mem::transmute(&baz);
diff --git a/tests/ui/resolve/issue-65025-extern-static-parent-generics.stderr b/tests/ui/resolve/issue-65025-extern-static-parent-generics.stderr
index 6bbf76dd1fb..3e9c3fd11b7 100644
--- a/tests/ui/resolve/issue-65025-extern-static-parent-generics.stderr
+++ b/tests/ui/resolve/issue-65025-extern-static-parent-generics.stderr
@@ -1,11 +1,11 @@
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/issue-65025-extern-static-parent-generics.rs:3:28
    |
 LL | unsafe fn foo<A>() {
-   |               - type parameter from outer function
+   |               - type parameter from outer item
 LL |     extern "C" {
 LL |         static baz: *const A;
-   |                            ^ use of generic parameter from outer function
+   |                            ^ use of generic parameter from outer item
 
 error: aborting due to previous error
 
diff --git a/tests/ui/resolve/issue-65035-static-with-parent-generics.rs b/tests/ui/resolve/issue-65035-static-with-parent-generics.rs
index f96c04841dd..bc99584a8d2 100644
--- a/tests/ui/resolve/issue-65035-static-with-parent-generics.rs
+++ b/tests/ui/resolve/issue-65035-static-with-parent-generics.rs
@@ -1,26 +1,26 @@
 fn f<T>() {
     extern "C" {
         static a: *const T;
-        //~^ ERROR can't use generic parameters from outer function
+        //~^ ERROR can't use generic parameters from outer item
     }
 }
 
 fn g<T: Default>() {
     static a: *const T = Default::default();
-    //~^ ERROR can't use generic parameters from outer function
+    //~^ ERROR can't use generic parameters from outer item
 }
 
 fn h<const N: usize>() {
     extern "C" {
         static a: [u8; N];
-        //~^ ERROR can't use generic parameters from outer function
+        //~^ ERROR can't use generic parameters from outer item
     }
 }
 
 fn i<const N: usize>() {
     static a: [u8; N] = [0; N];
-    //~^ ERROR can't use generic parameters from outer function
-    //~| ERROR can't use generic parameters from outer function
+    //~^ ERROR can't use generic parameters from outer item
+    //~| ERROR can't use generic parameters from outer item
 }
 
 fn main() {}
diff --git a/tests/ui/resolve/issue-65035-static-with-parent-generics.stderr b/tests/ui/resolve/issue-65035-static-with-parent-generics.stderr
index 7ed572f80b8..f1fe1a6002c 100644
--- a/tests/ui/resolve/issue-65035-static-with-parent-generics.stderr
+++ b/tests/ui/resolve/issue-65035-static-with-parent-generics.stderr
@@ -1,44 +1,44 @@
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/issue-65035-static-with-parent-generics.rs:3:26
    |
 LL | fn f<T>() {
-   |      - type parameter from outer function
+   |      - type parameter from outer item
 LL |     extern "C" {
 LL |         static a: *const T;
-   |                          ^ use of generic parameter from outer function
+   |                          ^ use of generic parameter from outer item
 
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/issue-65035-static-with-parent-generics.rs:9:22
    |
 LL | fn g<T: Default>() {
-   |      - type parameter from outer function
+   |      - type parameter from outer item
 LL |     static a: *const T = Default::default();
-   |                      ^ use of generic parameter from outer function
+   |                      ^ use of generic parameter from outer item
 
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/issue-65035-static-with-parent-generics.rs:15:24
    |
 LL | fn h<const N: usize>() {
-   |            - const parameter from outer function
+   |            - const parameter from outer item
 LL |     extern "C" {
 LL |         static a: [u8; N];
-   |                        ^ use of generic parameter from outer function
+   |                        ^ use of generic parameter from outer item
 
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/issue-65035-static-with-parent-generics.rs:21:20
    |
 LL | fn i<const N: usize>() {
-   |            - const parameter from outer function
+   |            - const parameter from outer item
 LL |     static a: [u8; N] = [0; N];
-   |                    ^ use of generic parameter from outer function
+   |                    ^ use of generic parameter from outer item
 
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/issue-65035-static-with-parent-generics.rs:21:29
    |
 LL | fn i<const N: usize>() {
-   |            - const parameter from outer function
+   |            - const parameter from outer item
 LL |     static a: [u8; N] = [0; N];
-   |                             ^ use of generic parameter from outer function
+   |                             ^ use of generic parameter from outer item
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/resolve/resolve-type-param-in-item-in-trait.rs b/tests/ui/resolve/resolve-type-param-in-item-in-trait.rs
index c77a66524f7..2d5f34c62a6 100644
--- a/tests/ui/resolve/resolve-type-param-in-item-in-trait.rs
+++ b/tests/ui/resolve/resolve-type-param-in-item-in-trait.rs
@@ -6,7 +6,7 @@ trait TraitA<A> {
     fn outer(&self) {
         enum Foo<B> {
             Variance(A)
-                //~^ ERROR can't use generic parameters from outer function
+                //~^ ERROR can't use generic parameters from outer item
         }
     }
 }
@@ -14,21 +14,21 @@ trait TraitA<A> {
 trait TraitB<A> {
     fn outer(&self) {
         struct Foo<B>(A);
-                //~^ ERROR can't use generic parameters from outer function
+                //~^ ERROR can't use generic parameters from outer item
     }
 }
 
 trait TraitC<A> {
     fn outer(&self) {
         struct Foo<B> { a: A }
-                //~^ ERROR can't use generic parameters from outer function
+                //~^ ERROR can't use generic parameters from outer item
     }
 }
 
 trait TraitD<A> {
     fn outer(&self) {
         fn foo<B>(a: A) { }
-                //~^ ERROR can't use generic parameters from outer function
+                //~^ ERROR can't use generic parameters from outer item
     }
 }
 
diff --git a/tests/ui/resolve/resolve-type-param-in-item-in-trait.stderr b/tests/ui/resolve/resolve-type-param-in-item-in-trait.stderr
index 0a6d1cc3bcd..1ab56fdc504 100644
--- a/tests/ui/resolve/resolve-type-param-in-item-in-trait.stderr
+++ b/tests/ui/resolve/resolve-type-param-in-item-in-trait.stderr
@@ -1,46 +1,46 @@
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/resolve-type-param-in-item-in-trait.rs:8:22
    |
 LL | trait TraitA<A> {
-   |              - type parameter from outer function
+   |              - type parameter from outer item
 LL |     fn outer(&self) {
 LL |         enum Foo<B> {
-   |                  - help: try using a local generic parameter instead: `A,`
+   |                  - help: try introducing a local generic parameter here: `A,`
 LL |             Variance(A)
-   |                      ^ use of generic parameter from outer function
+   |                      ^ use of generic parameter from outer item
 
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/resolve-type-param-in-item-in-trait.rs:16:23
    |
 LL | trait TraitB<A> {
-   |              - type parameter from outer function
+   |              - type parameter from outer item
 LL |     fn outer(&self) {
 LL |         struct Foo<B>(A);
-   |                    -  ^ use of generic parameter from outer function
+   |                    -  ^ use of generic parameter from outer item
    |                    |
-   |                    help: try using a local generic parameter instead: `A,`
+   |                    help: try introducing a local generic parameter here: `A,`
 
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/resolve-type-param-in-item-in-trait.rs:23:28
    |
 LL | trait TraitC<A> {
-   |              - type parameter from outer function
+   |              - type parameter from outer item
 LL |     fn outer(&self) {
 LL |         struct Foo<B> { a: A }
-   |                    -       ^ use of generic parameter from outer function
+   |                    -       ^ use of generic parameter from outer item
    |                    |
-   |                    help: try using a local generic parameter instead: `A,`
+   |                    help: try introducing a local generic parameter here: `A,`
 
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/resolve-type-param-in-item-in-trait.rs:30:22
    |
 LL | trait TraitD<A> {
-   |              - type parameter from outer function
+   |              - type parameter from outer item
 LL |     fn outer(&self) {
 LL |         fn foo<B>(a: A) { }
-   |                -     ^ use of generic parameter from outer function
+   |                -     ^ use of generic parameter from outer item
    |                |
-   |                help: try using a local generic parameter instead: `A,`
+   |                help: try introducing a local generic parameter here: `A,`
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/resolve/suggest-import-without-clobbering-attrs.fixed b/tests/ui/resolve/suggest-import-without-clobbering-attrs.fixed
new file mode 100644
index 00000000000..fc68884fe9c
--- /dev/null
+++ b/tests/ui/resolve/suggest-import-without-clobbering-attrs.fixed
@@ -0,0 +1,16 @@
+// run-rustfix
+// compile-flags: --cfg=whatever -Aunused
+
+use y::z;
+#[cfg(whatever)]
+use y::Whatever;
+
+mod y {
+    pub(crate) fn z() {}
+    pub(crate) struct Whatever;
+}
+
+fn main() {
+    z();
+    //~^ ERROR cannot find function `z` in this scope
+}
diff --git a/tests/ui/resolve/suggest-import-without-clobbering-attrs.rs b/tests/ui/resolve/suggest-import-without-clobbering-attrs.rs
new file mode 100644
index 00000000000..38a1095703b
--- /dev/null
+++ b/tests/ui/resolve/suggest-import-without-clobbering-attrs.rs
@@ -0,0 +1,15 @@
+// run-rustfix
+// compile-flags: --cfg=whatever -Aunused
+
+#[cfg(whatever)]
+use y::Whatever;
+
+mod y {
+    pub(crate) fn z() {}
+    pub(crate) struct Whatever;
+}
+
+fn main() {
+    z();
+    //~^ ERROR cannot find function `z` in this scope
+}
diff --git a/tests/ui/resolve/suggest-import-without-clobbering-attrs.stderr b/tests/ui/resolve/suggest-import-without-clobbering-attrs.stderr
new file mode 100644
index 00000000000..d3574851d5c
--- /dev/null
+++ b/tests/ui/resolve/suggest-import-without-clobbering-attrs.stderr
@@ -0,0 +1,14 @@
+error[E0425]: cannot find function `z` in this scope
+  --> $DIR/suggest-import-without-clobbering-attrs.rs:13:5
+   |
+LL |     z();
+   |     ^ not found in this scope
+   |
+help: consider importing this function
+   |
+LL + use y::z;
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0425`.
diff --git a/tests/ui/resolve/use-self-in-inner-fn.rs b/tests/ui/resolve/use-self-in-inner-fn.rs
index eccb315feb1..f4dfa4c40ab 100644
--- a/tests/ui/resolve/use-self-in-inner-fn.rs
+++ b/tests/ui/resolve/use-self-in-inner-fn.rs
@@ -4,9 +4,9 @@ impl A {
 //~^ NOTE `Self` type implicitly declared here, by this `impl`
     fn banana(&mut self) {
         fn peach(this: &Self) {
-        //~^ ERROR can't use generic parameters from outer function
-        //~| NOTE use of generic parameter from outer function
-        //~| NOTE use a type here instead
+        //~^ ERROR can't use generic parameters from outer item
+        //~| NOTE use of generic parameter from outer item
+        //~| NOTE refer to the type directly here instead
         }
     }
 }
diff --git a/tests/ui/resolve/use-self-in-inner-fn.stderr b/tests/ui/resolve/use-self-in-inner-fn.stderr
index 96609349924..832aaacaf49 100644
--- a/tests/ui/resolve/use-self-in-inner-fn.stderr
+++ b/tests/ui/resolve/use-self-in-inner-fn.stderr
@@ -1,4 +1,4 @@
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/use-self-in-inner-fn.rs:6:25
    |
 LL | impl A {
@@ -7,8 +7,8 @@ LL | impl A {
 LL |         fn peach(this: &Self) {
    |                         ^^^^
    |                         |
-   |                         use of generic parameter from outer function
-   |                         use a type here instead
+   |                         use of generic parameter from outer item
+   |                         refer to the type directly here instead
 
 error: aborting due to previous error
 
diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.rs
index 3beb20f0a37..b8c0eb3e6d6 100644
--- a/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.rs
+++ b/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.rs
@@ -8,14 +8,10 @@ fn _if_let_guard() {
         //~^ ERROR `if let` guards are experimental
 
         () if (let 0 = 1) => {}
-        //~^ ERROR `let` expressions in this position are unstable
-        //~| ERROR expected expression, found `let` statement
-        //~| ERROR `let` expressions are not supported here
+        //~^ ERROR expected expression, found `let` statement
 
         () if (((let 0 = 1))) => {}
-        //~^ ERROR `let` expressions in this position are unstable
-        //~| ERROR expected expression, found `let` statement
-        //~| ERROR `let` expressions are not supported here
+        //~^ ERROR expected expression, found `let` statement
 
         () if true && let 0 = 1 => {}
         //~^ ERROR `if let` guards are experimental
@@ -26,36 +22,22 @@ fn _if_let_guard() {
         //~| ERROR `let` expressions in this position are unstable
 
         () if (let 0 = 1) && true => {}
-        //~^ ERROR `let` expressions in this position are unstable
-        //~| ERROR expected expression, found `let` statement
-        //~| ERROR `let` expressions are not supported here
+        //~^ ERROR expected expression, found `let` statement
 
         () if true && (let 0 = 1) => {}
-        //~^ ERROR `let` expressions in this position are unstable
-        //~| ERROR expected expression, found `let` statement
-        //~| ERROR `let` expressions are not supported here
+        //~^ ERROR expected expression, found `let` statement
 
         () if (let 0 = 1) && (let 0 = 1) => {}
-        //~^ ERROR `let` expressions in this position are unstable
-        //~| ERROR `let` expressions in this position are unstable
+        //~^ ERROR expected expression, found `let` statement
         //~| ERROR expected expression, found `let` statement
-        //~| ERROR expected expression, found `let` statement
-        //~| ERROR `let` expressions are not supported here
-        //~| ERROR `let` expressions are not supported here
 
         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
         //~^ ERROR `if let` guards are experimental
         //~| ERROR `let` expressions in this position are unstable
         //~| ERROR `let` expressions in this position are unstable
-        //~| ERROR `let` expressions in this position are unstable
-        //~| ERROR `let` expressions in this position are unstable
-        //~| ERROR `let` expressions in this position are unstable
         //~| ERROR expected expression, found `let` statement
         //~| ERROR expected expression, found `let` statement
         //~| ERROR expected expression, found `let` statement
-        //~| ERROR `let` expressions are not supported here
-        //~| ERROR `let` expressions are not supported here
-        //~| ERROR `let` expressions are not supported here
 
 
         () if let Range { start: _, end: _ } = (true..true) && false => {}
@@ -76,13 +58,9 @@ fn _macros() {
         }
     }
     use_expr!((let 0 = 1 && 0 == 0));
-    //~^ ERROR `let` expressions in this position are unstable
-    //~| ERROR expected expression, found `let` statement
-    //~| ERROR `let` expressions are not supported here
+    //~^ ERROR expected expression, found `let` statement
     use_expr!((let 0 = 1));
-    //~^ ERROR `let` expressions in this position are unstable
-    //~| ERROR expected expression, found `let` statement
-    //~| ERROR `let` expressions are not supported here
+    //~^ ERROR expected expression, found `let` statement
     match () {
         #[cfg(FALSE)]
         () if let 0 = 1 => {}
diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.stderr b/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.stderr
index dc182ce464a..62534b555b2 100644
--- a/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.stderr
+++ b/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.stderr
@@ -2,87 +2,6 @@ error: expected expression, found `let` statement
   --> $DIR/feature-gate.rs:10:16
    |
 LL |         () if (let 0 = 1) => {}
-   |                ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/feature-gate.rs:15:18
-   |
-LL |         () if (((let 0 = 1))) => {}
-   |                  ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/feature-gate.rs:28:16
-   |
-LL |         () if (let 0 = 1) && true => {}
-   |                ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/feature-gate.rs:33:24
-   |
-LL |         () if true && (let 0 = 1) => {}
-   |                        ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/feature-gate.rs:38:16
-   |
-LL |         () if (let 0 = 1) && (let 0 = 1) => {}
-   |                ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/feature-gate.rs:38:31
-   |
-LL |         () if (let 0 = 1) && (let 0 = 1) => {}
-   |                               ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/feature-gate.rs:46:42
-   |
-LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
-   |                                          ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/feature-gate.rs:46:55
-   |
-LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
-   |                                                       ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/feature-gate.rs:46:68
-   |
-LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
-   |                                                                    ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/feature-gate.rs:78:16
-   |
-LL |     use_expr!((let 0 = 1 && 0 == 0));
-   |                ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/feature-gate.rs:82:16
-   |
-LL |     use_expr!((let 0 = 1));
-   |                ^^^
-
-error: no rules expected the token `let`
-  --> $DIR/feature-gate.rs:92:15
-   |
-LL |     macro_rules! use_expr {
-   |     --------------------- when calling this macro
-...
-LL |     use_expr!(let 0 = 1);
-   |               ^^^ no rules expected this token in macro call
-   |
-note: while trying to match meta-variable `$e:expr`
-  --> $DIR/feature-gate.rs:71:10
-   |
-LL |         ($e:expr) => {
-   |          ^^^^^^^
-
-error: `let` expressions are not supported here
-  --> $DIR/feature-gate.rs:10:16
-   |
-LL |         () if (let 0 = 1) => {}
    |                ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
@@ -92,135 +11,140 @@ note: `let`s wrapped in parentheses are not supported in a context with let chai
 LL |         () if (let 0 = 1) => {}
    |                ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/feature-gate.rs:15:18
+error: expected expression, found `let` statement
+  --> $DIR/feature-gate.rs:13:18
    |
 LL |         () if (((let 0 = 1))) => {}
    |                  ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/feature-gate.rs:15:18
+  --> $DIR/feature-gate.rs:13:18
    |
 LL |         () if (((let 0 = 1))) => {}
    |                  ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/feature-gate.rs:28:16
+error: expected expression, found `let` statement
+  --> $DIR/feature-gate.rs:24:16
    |
 LL |         () if (let 0 = 1) && true => {}
    |                ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/feature-gate.rs:28:16
+  --> $DIR/feature-gate.rs:24:16
    |
 LL |         () if (let 0 = 1) && true => {}
    |                ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/feature-gate.rs:33:24
+error: expected expression, found `let` statement
+  --> $DIR/feature-gate.rs:27:24
    |
 LL |         () if true && (let 0 = 1) => {}
    |                        ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/feature-gate.rs:33:24
+  --> $DIR/feature-gate.rs:27:24
    |
 LL |         () if true && (let 0 = 1) => {}
    |                        ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/feature-gate.rs:38:16
+error: expected expression, found `let` statement
+  --> $DIR/feature-gate.rs:30:16
    |
 LL |         () if (let 0 = 1) && (let 0 = 1) => {}
    |                ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/feature-gate.rs:38:16
+  --> $DIR/feature-gate.rs:30:16
    |
 LL |         () if (let 0 = 1) && (let 0 = 1) => {}
    |                ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/feature-gate.rs:38:31
+error: expected expression, found `let` statement
+  --> $DIR/feature-gate.rs:30:31
    |
 LL |         () if (let 0 = 1) && (let 0 = 1) => {}
    |                               ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/feature-gate.rs:38:31
+  --> $DIR/feature-gate.rs:30:31
    |
 LL |         () if (let 0 = 1) && (let 0 = 1) => {}
    |                               ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/feature-gate.rs:46:42
+error: expected expression, found `let` statement
+  --> $DIR/feature-gate.rs:34:42
    |
 LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
    |                                          ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/feature-gate.rs:46:42
+  --> $DIR/feature-gate.rs:34:42
    |
 LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
    |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/feature-gate.rs:46:55
+error: expected expression, found `let` statement
+  --> $DIR/feature-gate.rs:34:55
    |
 LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
    |                                                       ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/feature-gate.rs:46:42
+  --> $DIR/feature-gate.rs:34:42
    |
 LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
    |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/feature-gate.rs:46:68
+error: expected expression, found `let` statement
+  --> $DIR/feature-gate.rs:34:68
    |
 LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
    |                                                                    ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/feature-gate.rs:46:42
+  --> $DIR/feature-gate.rs:34:42
    |
 LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
    |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/feature-gate.rs:78:16
+error: expected expression, found `let` statement
+  --> $DIR/feature-gate.rs:60:16
    |
 LL |     use_expr!((let 0 = 1 && 0 == 0));
-   |                ^^^^^^^^^
+   |                ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/feature-gate.rs:78:16
-   |
-LL |     use_expr!((let 0 = 1 && 0 == 0));
-   |                ^^^^^^^^^^^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/feature-gate.rs:82:16
+error: expected expression, found `let` statement
+  --> $DIR/feature-gate.rs:62:16
    |
 LL |     use_expr!((let 0 = 1));
-   |                ^^^^^^^^^
+   |                ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/feature-gate.rs:82:16
+
+error: no rules expected the token `let`
+  --> $DIR/feature-gate.rs:70:15
    |
-LL |     use_expr!((let 0 = 1));
-   |                ^^^^^^^^^
+LL |     macro_rules! use_expr {
+   |     --------------------- when calling this macro
+...
+LL |     use_expr!(let 0 = 1);
+   |               ^^^ no rules expected this token in macro call
+   |
+note: while trying to match meta-variable `$e:expr`
+  --> $DIR/feature-gate.rs:53:10
+   |
+LL |         ($e:expr) => {
+   |          ^^^^^^^
 
 error[E0658]: `if let` guards are experimental
   --> $DIR/feature-gate.rs:7:12
@@ -233,7 +157,7 @@ LL |         () if let 0 = 1 => {}
    = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
 
 error[E0658]: `if let` guards are experimental
-  --> $DIR/feature-gate.rs:20:12
+  --> $DIR/feature-gate.rs:16:12
    |
 LL |         () if true && let 0 = 1 => {}
    |            ^^^^^^^^^^^^^^^^^^^^
@@ -243,7 +167,7 @@ LL |         () if true && let 0 = 1 => {}
    = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
 
 error[E0658]: `if let` guards are experimental
-  --> $DIR/feature-gate.rs:24:12
+  --> $DIR/feature-gate.rs:20:12
    |
 LL |         () if let 0 = 1 && true => {}
    |            ^^^^^^^^^^^^^^^^^^^^
@@ -253,7 +177,7 @@ LL |         () if let 0 = 1 && true => {}
    = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
 
 error[E0658]: `if let` guards are experimental
-  --> $DIR/feature-gate.rs:46:12
+  --> $DIR/feature-gate.rs:34:12
    |
 LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -263,7 +187,7 @@ LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 =
    = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
 
 error[E0658]: `if let` guards are experimental
-  --> $DIR/feature-gate.rs:61:12
+  --> $DIR/feature-gate.rs:43:12
    |
 LL |         () if let Range { start: _, end: _ } = (true..true) && false => {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -273,7 +197,7 @@ LL |         () if let Range { start: _, end: _ } = (true..true) && false => {}
    = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
 
 error[E0658]: `if let` guards are experimental
-  --> $DIR/feature-gate.rs:88:12
+  --> $DIR/feature-gate.rs:66:12
    |
 LL |         () if let 0 = 1 => {}
    |            ^^^^^^^^^^^^
@@ -283,25 +207,7 @@ LL |         () if let 0 = 1 => {}
    = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:10:16
-   |
-LL |         () if (let 0 = 1) => {}
-   |                ^^^^^^^^^
-   |
-   = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
-   = help: add `#![feature(let_chains)]` to the crate attributes to enable
-
-error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:15:18
-   |
-LL |         () if (((let 0 = 1))) => {}
-   |                  ^^^^^^^^^
-   |
-   = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
-   = help: add `#![feature(let_chains)]` to the crate attributes to enable
-
-error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:20:23
+  --> $DIR/feature-gate.rs:16:23
    |
 LL |         () if true && let 0 = 1 => {}
    |                       ^^^^^^^^^
@@ -310,7 +216,7 @@ LL |         () if true && let 0 = 1 => {}
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:24:15
+  --> $DIR/feature-gate.rs:20:15
    |
 LL |         () if let 0 = 1 && true => {}
    |               ^^^^^^^^^
@@ -319,43 +225,7 @@ LL |         () if let 0 = 1 && true => {}
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:28:16
-   |
-LL |         () if (let 0 = 1) && true => {}
-   |                ^^^^^^^^^
-   |
-   = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
-   = help: add `#![feature(let_chains)]` to the crate attributes to enable
-
-error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:33:24
-   |
-LL |         () if true && (let 0 = 1) => {}
-   |                        ^^^^^^^^^
-   |
-   = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
-   = help: add `#![feature(let_chains)]` to the crate attributes to enable
-
-error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:38:16
-   |
-LL |         () if (let 0 = 1) && (let 0 = 1) => {}
-   |                ^^^^^^^^^
-   |
-   = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
-   = help: add `#![feature(let_chains)]` to the crate attributes to enable
-
-error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:38:31
-   |
-LL |         () if (let 0 = 1) && (let 0 = 1) => {}
-   |                               ^^^^^^^^^
-   |
-   = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
-   = help: add `#![feature(let_chains)]` to the crate attributes to enable
-
-error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:46:15
+  --> $DIR/feature-gate.rs:34:15
    |
 LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
    |               ^^^^^^^^^
@@ -364,7 +234,7 @@ LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 =
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:46:28
+  --> $DIR/feature-gate.rs:34:28
    |
 LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
    |                            ^^^^^^^^^
@@ -373,34 +243,7 @@ LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 =
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:46:42
-   |
-LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
-   |                                          ^^^^^^^^^
-   |
-   = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
-   = help: add `#![feature(let_chains)]` to the crate attributes to enable
-
-error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:46:55
-   |
-LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
-   |                                                       ^^^^^^^^^
-   |
-   = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
-   = help: add `#![feature(let_chains)]` to the crate attributes to enable
-
-error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:46:68
-   |
-LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
-   |                                                                    ^^^^^^^^^
-   |
-   = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
-   = help: add `#![feature(let_chains)]` to the crate attributes to enable
-
-error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:61:15
+  --> $DIR/feature-gate.rs:43:15
    |
 LL |         () if let Range { start: _, end: _ } = (true..true) && false => {}
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -408,24 +251,6 @@ LL |         () if let Range { start: _, end: _ } = (true..true) && false => {}
    = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
-error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:78:16
-   |
-LL |     use_expr!((let 0 = 1 && 0 == 0));
-   |                ^^^^^^^^^
-   |
-   = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
-   = help: add `#![feature(let_chains)]` to the crate attributes to enable
-
-error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:82:16
-   |
-LL |     use_expr!((let 0 = 1));
-   |                ^^^^^^^^^
-   |
-   = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
-   = help: add `#![feature(let_chains)]` to the crate attributes to enable
-
-error: aborting due to 45 previous errors
+error: aborting due to 23 previous errors
 
 For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.stderr b/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.stderr
index 41a20bf8ae1..00c1c303d2b 100644
--- a/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.stderr
+++ b/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.stderr
@@ -7,6 +7,7 @@ LL |     ($e:expr) => { let Some(x) = $e }
 LL |         () if m!(Some(5)) => {}
    |               ----------- in this macro invocation
    |
+   = note: only supported directly in conditions of `if` and `while` expressions
    = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to previous error
diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/parens.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/parens.rs
index 9cb27c73b14..f12824db9c0 100644
--- a/tests/ui/rfcs/rfc-2294-if-let-guard/parens.rs
+++ b/tests/ui/rfcs/rfc-2294-if-let-guard/parens.rs
@@ -19,9 +19,7 @@ fn main() {
         () if let 0 = 1 => {}
         () if (let 0 = 1) => {}
         //~^ ERROR expected expression, found `let` statement
-        //~| ERROR `let` expressions are not supported here
         () if (((let 0 = 1))) => {}
         //~^ ERROR expected expression, found `let` statement
-        //~| ERROR `let` expressions are not supported here
     }
 }
diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/parens.stderr b/tests/ui/rfcs/rfc-2294-if-let-guard/parens.stderr
index 85df360daab..0c16d9c5442 100644
--- a/tests/ui/rfcs/rfc-2294-if-let-guard/parens.stderr
+++ b/tests/ui/rfcs/rfc-2294-if-let-guard/parens.stderr
@@ -2,27 +2,29 @@ error: expected expression, found `let` statement
   --> $DIR/parens.rs:10:16
    |
 LL |         () if (let 0 = 1) => {}
-   |                ^^^
+   |                ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/parens.rs:10:16
+   |
+LL |         () if (let 0 = 1) => {}
+   |                ^^^^^^^^^
 
 error: expected expression, found `let` statement
   --> $DIR/parens.rs:12:18
    |
 LL |         () if (((let 0 = 1))) => {}
-   |                  ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/parens.rs:20:16
+   |                  ^^^^^^^^^
    |
-LL |         () if (let 0 = 1) => {}
-   |                ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/parens.rs:23:18
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/parens.rs:12:18
    |
 LL |         () if (((let 0 = 1))) => {}
-   |                  ^^^
+   |                  ^^^^^^^^^
 
-error: `let` expressions are not supported here
+error: expected expression, found `let` statement
   --> $DIR/parens.rs:20:16
    |
 LL |         () if (let 0 = 1) => {}
@@ -35,18 +37,18 @@ note: `let`s wrapped in parentheses are not supported in a context with let chai
 LL |         () if (let 0 = 1) => {}
    |                ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/parens.rs:23:18
+error: expected expression, found `let` statement
+  --> $DIR/parens.rs:22:18
    |
 LL |         () if (((let 0 = 1))) => {}
    |                  ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/parens.rs:23:18
+  --> $DIR/parens.rs:22:18
    |
 LL |         () if (((let 0 = 1))) => {}
    |                  ^^^^^^^^^
 
-error: aborting due to 6 previous errors
+error: aborting due to 4 previous errors
 
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs
index e6dee2a1d06..cb3be59bee0 100644
--- a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs
@@ -3,7 +3,7 @@
 fn let_or_guard(x: Result<Option<i32>, ()>) {
     match x {
         Ok(opt) if let Some(4) = opt || false  => {}
-        //~^ ERROR `let` expressions are not supported here
+        //~^ ERROR expected expression, found `let` statement
         _ => {}
     }
 }
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr
index 26850998cc4..4b85fdd5050 100644
--- a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr
@@ -1,4 +1,4 @@
-error: `let` expressions are not supported here
+error: expected expression, found `let` statement
   --> $DIR/ast-validate-guards.rs:5:20
    |
 LL |         Ok(opt) if let Some(4) = opt || false  => {}
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/avoid-invalid-mir.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/avoid-invalid-mir.rs
new file mode 100644
index 00000000000..530458064b2
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/avoid-invalid-mir.rs
@@ -0,0 +1,14 @@
+// Regression test for #104172
+
+const N: usize = {
+    struct U;
+    !let y = 42;
+    //~^ ERROR expected expression, found `let` statement
+    3
+};
+
+struct S {
+    x: [(); N]
+}
+
+fn main() {}
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/avoid-invalid-mir.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/avoid-invalid-mir.stderr
new file mode 100644
index 00000000000..3eaccde3b74
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/avoid-invalid-mir.stderr
@@ -0,0 +1,10 @@
+error: expected expression, found `let` statement
+  --> $DIR/avoid-invalid-mir.rs:5:6
+   |
+LL |     !let y = 42;
+   |      ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: aborting due to previous error
+
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions-without-feature-gate.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions-without-feature-gate.rs
new file mode 100644
index 00000000000..096036bb133
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions-without-feature-gate.rs
@@ -0,0 +1,340 @@
+// Check that we don't suggest enabling a feature for code that's
+// not accepted even with that feature.
+
+#![allow(irrefutable_let_patterns)]
+
+use std::ops::Range;
+
+fn main() {}
+
+fn _if() {
+    if (let 0 = 1) {}
+    //~^ ERROR expected expression, found `let` statement
+
+    if (((let 0 = 1))) {}
+    //~^ ERROR expected expression, found `let` statement
+
+    if (let 0 = 1) && true {}
+    //~^ ERROR expected expression, found `let` statement
+
+    if true && (let 0 = 1) {}
+    //~^ ERROR expected expression, found `let` statement
+
+    if (let 0 = 1) && (let 0 = 1) {}
+    //~^ ERROR expected expression, found `let` statement
+    //~| ERROR expected expression, found `let` statement
+
+    if (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+    //~^ ERROR expected expression, found `let` statement
+    //~| ERROR expected expression, found `let` statement
+    //~| ERROR expected expression, found `let` statement
+}
+
+fn _while() {
+    while (let 0 = 1) {}
+    //~^ ERROR expected expression, found `let` statement
+
+    while (((let 0 = 1))) {}
+    //~^ ERROR expected expression, found `let` statement
+
+    while (let 0 = 1) && true {}
+    //~^ ERROR expected expression, found `let` statement
+
+    while true && (let 0 = 1) {}
+    //~^ ERROR expected expression, found `let` statement
+
+    while (let 0 = 1) && (let 0 = 1) {}
+    //~^ ERROR expected expression, found `let` statement
+    //~| ERROR expected expression, found `let` statement
+
+    while (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+    //~^ ERROR expected expression, found `let` statement
+    //~| ERROR expected expression, found `let` statement
+    //~| ERROR expected expression, found `let` statement
+}
+
+fn _macros() {
+    macro_rules! use_expr {
+        ($e:expr) => {
+            if $e {}
+            while $e {}
+        }
+    }
+    use_expr!((let 0 = 1 && 0 == 0));
+    //~^ ERROR expected expression, found `let` statement
+    use_expr!((let 0 = 1));
+    //~^ ERROR expected expression, found `let` statement
+}
+
+fn nested_within_if_expr() {
+    if &let 0 = 0 {}
+    //~^ ERROR expected expression, found `let` statement
+
+    if !let 0 = 0 {}
+    //~^ ERROR expected expression, found `let` statement
+    if *let 0 = 0 {}
+    //~^ ERROR expected expression, found `let` statement
+    if -let 0 = 0 {}
+    //~^ ERROR expected expression, found `let` statement
+
+    fn _check_try_binds_tighter() -> Result<(), ()> {
+        if let 0 = 0? {}
+        //~^ ERROR the `?` operator can only be applied to values that implement `Try`
+        Ok(())
+    }
+    if (let 0 = 0)? {}
+    //~^ ERROR expected expression, found `let` statement
+
+    if true || let 0 = 0 {}
+    //~^ ERROR expected expression, found `let` statement
+    if (true || let 0 = 0) {}
+    //~^ ERROR expected expression, found `let` statement
+    if true && (true || let 0 = 0) {}
+    //~^ ERROR expected expression, found `let` statement
+    if true || (true && let 0 = 0) {}
+    //~^ ERROR expected expression, found `let` statement
+
+    let mut x = true;
+    if x = let 0 = 0 {}
+    //~^ ERROR expected expression, found `let` statement
+
+    if true..(let 0 = 0) {}
+    //~^ ERROR expected expression, found `let` statement
+    //~| ERROR mismatched types
+    if ..(let 0 = 0) {}
+    //~^ ERROR expected expression, found `let` statement
+    if (let 0 = 0).. {}
+    //~^ ERROR expected expression, found `let` statement
+
+    // Binds as `(let ... = true)..true &&/|| false`.
+    if let Range { start: _, end: _ } = true..true && false {}
+    //~^ ERROR expected expression, found `let` statement
+    //~| ERROR mismatched types
+    if let Range { start: _, end: _ } = true..true || false {}
+    //~^ ERROR expected expression, found `let` statement
+    //~| ERROR mismatched types
+
+    // Binds as `(let Range { start: F, end } = F)..(|| true)`.
+    const F: fn() -> bool = || true;
+    if let Range { start: F, end } = F..|| true {}
+    //~^ ERROR expected expression, found `let` statement
+    //~| ERROR mismatched types
+
+    // Binds as `(let Range { start: true, end } = t)..(&&false)`.
+    let t = &&true;
+    if let Range { start: true, end } = t..&&false {}
+    //~^ ERROR expected expression, found `let` statement
+    //~| ERROR mismatched types
+
+    if let true = let true = true {}
+    //~^ ERROR expected expression, found `let` statement
+}
+
+fn nested_within_while_expr() {
+    while &let 0 = 0 {}
+    //~^ ERROR expected expression, found `let` statement
+
+    while !let 0 = 0 {}
+    //~^ ERROR expected expression, found `let` statement
+    while *let 0 = 0 {}
+    //~^ ERROR expected expression, found `let` statement
+    while -let 0 = 0 {}
+    //~^ ERROR expected expression, found `let` statement
+
+    fn _check_try_binds_tighter() -> Result<(), ()> {
+        while let 0 = 0? {}
+        //~^ ERROR the `?` operator can only be applied to values that implement `Try`
+        Ok(())
+    }
+    while (let 0 = 0)? {}
+    //~^ ERROR expected expression, found `let` statement
+
+    while true || let 0 = 0 {}
+    //~^ ERROR expected expression, found `let` statement
+    while (true || let 0 = 0) {}
+    //~^ ERROR expected expression, found `let` statement
+    while true && (true || let 0 = 0) {}
+    //~^ ERROR expected expression, found `let` statement
+    while true || (true && let 0 = 0) {}
+    //~^ ERROR expected expression, found `let` statement
+
+    let mut x = true;
+    while x = let 0 = 0 {}
+    //~^ ERROR expected expression, found `let` statement
+
+    while true..(let 0 = 0) {}
+    //~^ ERROR expected expression, found `let` statement
+    //~| ERROR mismatched types
+    while ..(let 0 = 0) {}
+    //~^ ERROR expected expression, found `let` statement
+    while (let 0 = 0).. {}
+    //~^ ERROR expected expression, found `let` statement
+
+    // Binds as `(let ... = true)..true &&/|| false`.
+    while let Range { start: _, end: _ } = true..true && false {}
+    //~^ ERROR expected expression, found `let` statement
+    //~| ERROR mismatched types
+    while let Range { start: _, end: _ } = true..true || false {}
+    //~^ ERROR expected expression, found `let` statement
+    //~| ERROR mismatched types
+
+    // Binds as `(let Range { start: F, end } = F)..(|| true)`.
+    const F: fn() -> bool = || true;
+    while let Range { start: F, end } = F..|| true {}
+    //~^ ERROR expected expression, found `let` statement
+    //~| ERROR mismatched types
+
+    // Binds as `(let Range { start: true, end } = t)..(&&false)`.
+    let t = &&true;
+    while let Range { start: true, end } = t..&&false {}
+    //~^ ERROR expected expression, found `let` statement
+    //~| ERROR mismatched types
+
+    while let true = let true = true {}
+    //~^ ERROR expected expression, found `let` statement
+}
+
+fn not_error_because_clarified_intent() {
+    if let Range { start: _, end: _ } = (true..true || false) { }
+
+    if let Range { start: _, end: _ } = (true..true && false) { }
+
+    while let Range { start: _, end: _ } = (true..true || false) { }
+
+    while let Range { start: _, end: _ } = (true..true && false) { }
+}
+
+fn outside_if_and_while_expr() {
+    &let 0 = 0;
+    //~^ ERROR expected expression, found `let` statement
+
+    !let 0 = 0;
+    //~^ ERROR expected expression, found `let` statement
+    *let 0 = 0;
+    //~^ ERROR expected expression, found `let` statement
+    -let 0 = 0;
+    //~^ ERROR expected expression, found `let` statement
+    let _ = let _ = 3;
+    //~^ ERROR expected expression, found `let` statement
+
+    fn _check_try_binds_tighter() -> Result<(), ()> {
+        let 0 = 0?;
+        //~^ ERROR the `?` operator can only be applied to values that implement `Try`
+        Ok(())
+    }
+    (let 0 = 0)?;
+    //~^ ERROR expected expression, found `let` statement
+
+    true || let 0 = 0;
+    //~^ ERROR expected expression, found `let` statement
+    (true || let 0 = 0);
+    //~^ ERROR expected expression, found `let` statement
+    true && (true || let 0 = 0);
+    //~^ ERROR expected expression, found `let` statement
+
+    let mut x = true;
+    x = let 0 = 0;
+    //~^ ERROR expected expression, found `let` statement
+
+    true..(let 0 = 0);
+    //~^ ERROR expected expression, found `let` statement
+    ..(let 0 = 0);
+    //~^ ERROR expected expression, found `let` statement
+    (let 0 = 0)..;
+    //~^ ERROR expected expression, found `let` statement
+
+    (let Range { start: _, end: _ } = true..true || false);
+    //~^ ERROR mismatched types
+    //~| ERROR expected expression, found `let` statement
+
+    (let true = let true = true);
+    //~^ ERROR expected expression, found `let` statement
+    //~| ERROR expected expression, found `let` statement
+
+    {
+        #[cfg(FALSE)]
+        let x = true && let y = 1;
+        //~^ ERROR expected expression, found `let` statement
+    }
+
+    #[cfg(FALSE)]
+    {
+        [1, 2, 3][let _ = ()]
+        //~^ ERROR expected expression, found `let` statement
+    }
+
+    // Check function tail position.
+    &let 0 = 0
+    //~^ ERROR expected expression, found `let` statement
+}
+
+// Let's make sure that `let` inside const generic arguments are considered.
+fn inside_const_generic_arguments() {
+    struct A<const B: bool>;
+    impl<const B: bool> A<{B}> { const O: u32 = 5; }
+
+    if let A::<{
+        true && let 1 = 1
+        //~^ ERROR expected expression, found `let` statement
+    }>::O = 5 {}
+
+    while let A::<{
+        true && let 1 = 1
+        //~^ ERROR expected expression, found `let` statement
+    }>::O = 5 {}
+
+    if A::<{
+        true && let 1 = 1
+        //~^ ERROR expected expression, found `let` statement
+    }>::O == 5 {}
+
+    // In the cases above we have `ExprKind::Block` to help us out.
+    // Below however, we would not have a block and so an implementation might go
+    // from visiting expressions to types without banning `let` expressions down the tree.
+    // This tests ensures that we are not caught by surprise should the parser
+    // admit non-IDENT expressions in const generic arguments.
+
+    if A::<
+        true && let 1 = 1
+        //~^ ERROR expressions must be enclosed in braces
+        //~| ERROR expected expression, found `let` statement
+    >::O == 5 {}
+}
+
+fn with_parenthesis() {
+    let opt = Some(Some(1i32));
+
+    if (let Some(a) = opt && true) {
+    //~^ ERROR expected expression, found `let` statement
+    }
+
+    if (let Some(a) = opt) && true {
+    //~^ ERROR expected expression, found `let` statement
+    }
+    if (let Some(a) = opt) && (let Some(b) = a) {
+    //~^ ERROR expected expression, found `let` statement
+    //~| ERROR expected expression, found `let` statement
+    }
+
+    if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
+    //~^ ERROR expected expression, found `let` statement
+    //~| ERROR expected expression, found `let` statement
+    }
+    if (let Some(a) = opt && (let Some(b) = a)) && true {
+    //~^ ERROR expected expression, found `let` statement
+    //~| ERROR expected expression, found `let` statement
+    }
+    if (let Some(a) = opt && (true)) && true {
+    //~^ ERROR expected expression, found `let` statement
+    }
+
+    #[cfg(FALSE)]
+    let x = (true && let y = 1);
+    //~^ ERROR expected expression, found `let` statement
+
+    #[cfg(FALSE)]
+    {
+        ([1, 2, 3][let _ = ()])
+        //~^ ERROR expected expression, found `let` statement
+    }
+}
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions-without-feature-gate.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions-without-feature-gate.stderr
new file mode 100644
index 00000000000..31f389512ed
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions-without-feature-gate.stderr
@@ -0,0 +1,1021 @@
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:11:9
+   |
+LL |     if (let 0 = 1) {}
+   |         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:11:9
+   |
+LL |     if (let 0 = 1) {}
+   |         ^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:14:11
+   |
+LL |     if (((let 0 = 1))) {}
+   |           ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:14:11
+   |
+LL |     if (((let 0 = 1))) {}
+   |           ^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:17:9
+   |
+LL |     if (let 0 = 1) && true {}
+   |         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:17:9
+   |
+LL |     if (let 0 = 1) && true {}
+   |         ^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:20:17
+   |
+LL |     if true && (let 0 = 1) {}
+   |                 ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:20:17
+   |
+LL |     if true && (let 0 = 1) {}
+   |                 ^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:23:9
+   |
+LL |     if (let 0 = 1) && (let 0 = 1) {}
+   |         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:23:9
+   |
+LL |     if (let 0 = 1) && (let 0 = 1) {}
+   |         ^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:23:24
+   |
+LL |     if (let 0 = 1) && (let 0 = 1) {}
+   |                        ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:23:24
+   |
+LL |     if (let 0 = 1) && (let 0 = 1) {}
+   |                        ^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:27:9
+   |
+LL |     if (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:27:9
+   |
+LL |     if (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:27:22
+   |
+LL |     if (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                      ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:27:9
+   |
+LL |     if (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:27:35
+   |
+LL |     if (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                                   ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:27:9
+   |
+LL |     if (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:34:12
+   |
+LL |     while (let 0 = 1) {}
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:34:12
+   |
+LL |     while (let 0 = 1) {}
+   |            ^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:37:14
+   |
+LL |     while (((let 0 = 1))) {}
+   |              ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:37:14
+   |
+LL |     while (((let 0 = 1))) {}
+   |              ^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:40:12
+   |
+LL |     while (let 0 = 1) && true {}
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:40:12
+   |
+LL |     while (let 0 = 1) && true {}
+   |            ^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:43:20
+   |
+LL |     while true && (let 0 = 1) {}
+   |                    ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:43:20
+   |
+LL |     while true && (let 0 = 1) {}
+   |                    ^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:46:12
+   |
+LL |     while (let 0 = 1) && (let 0 = 1) {}
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:46:12
+   |
+LL |     while (let 0 = 1) && (let 0 = 1) {}
+   |            ^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:46:27
+   |
+LL |     while (let 0 = 1) && (let 0 = 1) {}
+   |                           ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:46:27
+   |
+LL |     while (let 0 = 1) && (let 0 = 1) {}
+   |                           ^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:50:12
+   |
+LL |     while (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:50:12
+   |
+LL |     while (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:50:25
+   |
+LL |     while (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:50:12
+   |
+LL |     while (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:50:38
+   |
+LL |     while (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                                      ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:50:12
+   |
+LL |     while (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:70:9
+   |
+LL |     if &let 0 = 0 {}
+   |         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:73:9
+   |
+LL |     if !let 0 = 0 {}
+   |         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:75:9
+   |
+LL |     if *let 0 = 0 {}
+   |         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:77:9
+   |
+LL |     if -let 0 = 0 {}
+   |         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:85:9
+   |
+LL |     if (let 0 = 0)? {}
+   |         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:88:16
+   |
+LL |     if true || let 0 = 0 {}
+   |                ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `||` operators are not supported in let chain expressions
+  --> $DIR/disallowed-positions-without-feature-gate.rs:88:13
+   |
+LL |     if true || let 0 = 0 {}
+   |             ^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:90:17
+   |
+LL |     if (true || let 0 = 0) {}
+   |                 ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:92:25
+   |
+LL |     if true && (true || let 0 = 0) {}
+   |                         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:94:25
+   |
+LL |     if true || (true && let 0 = 0) {}
+   |                         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:98:12
+   |
+LL |     if x = let 0 = 0 {}
+   |            ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:101:15
+   |
+LL |     if true..(let 0 = 0) {}
+   |               ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:104:11
+   |
+LL |     if ..(let 0 = 0) {}
+   |           ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:106:9
+   |
+LL |     if (let 0 = 0).. {}
+   |         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:110:8
+   |
+LL |     if let Range { start: _, end: _ } = true..true && false {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:113:8
+   |
+LL |     if let Range { start: _, end: _ } = true..true || false {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:119:8
+   |
+LL |     if let Range { start: F, end } = F..|| true {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:125:8
+   |
+LL |     if let Range { start: true, end } = t..&&false {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:129:19
+   |
+LL |     if let true = let true = true {}
+   |                   ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:134:12
+   |
+LL |     while &let 0 = 0 {}
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:137:12
+   |
+LL |     while !let 0 = 0 {}
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:139:12
+   |
+LL |     while *let 0 = 0 {}
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:141:12
+   |
+LL |     while -let 0 = 0 {}
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:149:12
+   |
+LL |     while (let 0 = 0)? {}
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:152:19
+   |
+LL |     while true || let 0 = 0 {}
+   |                   ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `||` operators are not supported in let chain expressions
+  --> $DIR/disallowed-positions-without-feature-gate.rs:152:16
+   |
+LL |     while true || let 0 = 0 {}
+   |                ^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:154:20
+   |
+LL |     while (true || let 0 = 0) {}
+   |                    ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:156:28
+   |
+LL |     while true && (true || let 0 = 0) {}
+   |                            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:158:28
+   |
+LL |     while true || (true && let 0 = 0) {}
+   |                            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:162:15
+   |
+LL |     while x = let 0 = 0 {}
+   |               ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:165:18
+   |
+LL |     while true..(let 0 = 0) {}
+   |                  ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:168:14
+   |
+LL |     while ..(let 0 = 0) {}
+   |              ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:170:12
+   |
+LL |     while (let 0 = 0).. {}
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:174:11
+   |
+LL |     while let Range { start: _, end: _ } = true..true && false {}
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:177:11
+   |
+LL |     while let Range { start: _, end: _ } = true..true || false {}
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:183:11
+   |
+LL |     while let Range { start: F, end } = F..|| true {}
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:189:11
+   |
+LL |     while let Range { start: true, end } = t..&&false {}
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:193:22
+   |
+LL |     while let true = let true = true {}
+   |                      ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:208:6
+   |
+LL |     &let 0 = 0;
+   |      ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:211:6
+   |
+LL |     !let 0 = 0;
+   |      ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:213:6
+   |
+LL |     *let 0 = 0;
+   |      ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:215:6
+   |
+LL |     -let 0 = 0;
+   |      ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:217:13
+   |
+LL |     let _ = let _ = 3;
+   |             ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:225:6
+   |
+LL |     (let 0 = 0)?;
+   |      ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:228:13
+   |
+LL |     true || let 0 = 0;
+   |             ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:230:14
+   |
+LL |     (true || let 0 = 0);
+   |              ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:232:22
+   |
+LL |     true && (true || let 0 = 0);
+   |                      ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:236:9
+   |
+LL |     x = let 0 = 0;
+   |         ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:239:12
+   |
+LL |     true..(let 0 = 0);
+   |            ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:241:8
+   |
+LL |     ..(let 0 = 0);
+   |        ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:243:6
+   |
+LL |     (let 0 = 0)..;
+   |      ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:246:6
+   |
+LL |     (let Range { start: _, end: _ } = true..true || false);
+   |      ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:250:6
+   |
+LL |     (let true = let true = true);
+   |      ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:250:17
+   |
+LL |     (let true = let true = true);
+   |                 ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:256:25
+   |
+LL |         let x = true && let y = 1;
+   |                         ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:262:19
+   |
+LL |         [1, 2, 3][let _ = ()]
+   |                   ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:267:6
+   |
+LL |     &let 0 = 0
+   |      ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:277:17
+   |
+LL |         true && let 1 = 1
+   |                 ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:282:17
+   |
+LL |         true && let 1 = 1
+   |                 ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:287:17
+   |
+LL |         true && let 1 = 1
+   |                 ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:298:17
+   |
+LL |         true && let 1 = 1
+   |                 ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expressions must be enclosed in braces to be used as const generic arguments
+  --> $DIR/disallowed-positions-without-feature-gate.rs:298:9
+   |
+LL |         true && let 1 = 1
+   |         ^^^^^^^^^^^^^^^^^
+   |
+help: enclose the `const` expression in braces
+   |
+LL |         { true && let 1 = 1 }
+   |         +                   +
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:307:9
+   |
+LL |     if (let Some(a) = opt && true) {
+   |         ^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:307:9
+   |
+LL |     if (let Some(a) = opt && true) {
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:311:9
+   |
+LL |     if (let Some(a) = opt) && true {
+   |         ^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:311:9
+   |
+LL |     if (let Some(a) = opt) && true {
+   |         ^^^^^^^^^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:314:9
+   |
+LL |     if (let Some(a) = opt) && (let Some(b) = a) {
+   |         ^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:314:9
+   |
+LL |     if (let Some(a) = opt) && (let Some(b) = a) {
+   |         ^^^^^^^^^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:314:32
+   |
+LL |     if (let Some(a) = opt) && (let Some(b) = a) {
+   |                                ^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:314:32
+   |
+LL |     if (let Some(a) = opt) && (let Some(b) = a) {
+   |                                ^^^^^^^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:319:9
+   |
+LL |     if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
+   |         ^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:319:9
+   |
+LL |     if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:319:31
+   |
+LL |     if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
+   |                               ^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:319:31
+   |
+LL |     if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
+   |                               ^^^^^^^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:323:9
+   |
+LL |     if (let Some(a) = opt && (let Some(b) = a)) && true {
+   |         ^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:323:9
+   |
+LL |     if (let Some(a) = opt && (let Some(b) = a)) && true {
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:323:31
+   |
+LL |     if (let Some(a) = opt && (let Some(b) = a)) && true {
+   |                               ^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:323:31
+   |
+LL |     if (let Some(a) = opt && (let Some(b) = a)) && true {
+   |                               ^^^^^^^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:327:9
+   |
+LL |     if (let Some(a) = opt && (true)) && true {
+   |         ^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+note: `let`s wrapped in parentheses are not supported in a context with let chains
+  --> $DIR/disallowed-positions-without-feature-gate.rs:327:9
+   |
+LL |     if (let Some(a) = opt && (true)) && true {
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:332:22
+   |
+LL |     let x = (true && let y = 1);
+   |                      ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:337:20
+   |
+LL |         ([1, 2, 3][let _ = ()])
+   |                    ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:63:16
+   |
+LL |     use_expr!((let 0 = 1 && 0 == 0));
+   |                ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions-without-feature-gate.rs:65:16
+   |
+LL |     use_expr!((let 0 = 1));
+   |                ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions-without-feature-gate.rs:101:8
+   |
+LL |     if true..(let 0 = 0) {}
+   |        ^^^^^^^^^^^^^^^^^ expected `bool`, found `Range<bool>`
+   |
+   = note: expected type `bool`
+            found struct `std::ops::Range<bool>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions-without-feature-gate.rs:110:12
+   |
+LL |     if let Range { start: _, end: _ } = true..true && false {}
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this expression has type `bool`
+   |            |
+   |            expected `bool`, found `Range<_>`
+   |
+   = note: expected type `bool`
+            found struct `std::ops::Range<_>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions-without-feature-gate.rs:113:12
+   |
+LL |     if let Range { start: _, end: _ } = true..true || false {}
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this expression has type `bool`
+   |            |
+   |            expected `bool`, found `Range<_>`
+   |
+   = note: expected type `bool`
+            found struct `std::ops::Range<_>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions-without-feature-gate.rs:119:12
+   |
+LL |     if let Range { start: F, end } = F..|| true {}
+   |            ^^^^^^^^^^^^^^^^^^^^^^^   - this expression has type `fn() -> bool`
+   |            |
+   |            expected fn pointer, found `Range<_>`
+   |
+   = note: expected fn pointer `fn() -> bool`
+                  found struct `std::ops::Range<_>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions-without-feature-gate.rs:125:12
+   |
+LL |     if let Range { start: true, end } = t..&&false {}
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^   - this expression has type `&&bool`
+   |            |
+   |            expected `bool`, found `Range<_>`
+   |
+   = note: expected type `bool`
+            found struct `std::ops::Range<_>`
+
+error[E0277]: the `?` operator can only be applied to values that implement `Try`
+  --> $DIR/disallowed-positions-without-feature-gate.rs:81:20
+   |
+LL |         if let 0 = 0? {}
+   |                    ^^ the `?` operator cannot be applied to type `{integer}`
+   |
+   = help: the trait `Try` is not implemented for `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions-without-feature-gate.rs:165:11
+   |
+LL |     while true..(let 0 = 0) {}
+   |           ^^^^^^^^^^^^^^^^^ expected `bool`, found `Range<bool>`
+   |
+   = note: expected type `bool`
+            found struct `std::ops::Range<bool>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions-without-feature-gate.rs:174:15
+   |
+LL |     while let Range { start: _, end: _ } = true..true && false {}
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this expression has type `bool`
+   |               |
+   |               expected `bool`, found `Range<_>`
+   |
+   = note: expected type `bool`
+            found struct `std::ops::Range<_>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions-without-feature-gate.rs:177:15
+   |
+LL |     while let Range { start: _, end: _ } = true..true || false {}
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this expression has type `bool`
+   |               |
+   |               expected `bool`, found `Range<_>`
+   |
+   = note: expected type `bool`
+            found struct `std::ops::Range<_>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions-without-feature-gate.rs:183:15
+   |
+LL |     while let Range { start: F, end } = F..|| true {}
+   |               ^^^^^^^^^^^^^^^^^^^^^^^   - this expression has type `fn() -> bool`
+   |               |
+   |               expected fn pointer, found `Range<_>`
+   |
+   = note: expected fn pointer `fn() -> bool`
+                  found struct `std::ops::Range<_>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions-without-feature-gate.rs:189:15
+   |
+LL |     while let Range { start: true, end } = t..&&false {}
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^   - this expression has type `&&bool`
+   |               |
+   |               expected `bool`, found `Range<_>`
+   |
+   = note: expected type `bool`
+            found struct `std::ops::Range<_>`
+
+error[E0277]: the `?` operator can only be applied to values that implement `Try`
+  --> $DIR/disallowed-positions-without-feature-gate.rs:145:23
+   |
+LL |         while let 0 = 0? {}
+   |                       ^^ the `?` operator cannot be applied to type `{integer}`
+   |
+   = help: the trait `Try` is not implemented for `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions-without-feature-gate.rs:246:10
+   |
+LL |     (let Range { start: _, end: _ } = true..true || false);
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this expression has type `bool`
+   |          |
+   |          expected `bool`, found `Range<_>`
+   |
+   = note: expected type `bool`
+            found struct `std::ops::Range<_>`
+
+error[E0277]: the `?` operator can only be applied to values that implement `Try`
+  --> $DIR/disallowed-positions-without-feature-gate.rs:221:17
+   |
+LL |         let 0 = 0?;
+   |                 ^^ the `?` operator cannot be applied to type `{integer}`
+   |
+   = help: the trait `Try` is not implemented for `{integer}`
+
+error: aborting due to 105 previous errors
+
+Some errors have detailed explanations: E0277, E0308.
+For more information about an error, try `rustc --explain E0277`.
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs
index 2a9a5472b2e..4ac3ea53a08 100644
--- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs
@@ -27,64 +27,46 @@ fn main() {}
 
 fn _if() {
     if (let 0 = 1) {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     if (((let 0 = 1))) {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     if (let 0 = 1) && true {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     if true && (let 0 = 1) {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     if (let 0 = 1) && (let 0 = 1) {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     //~| ERROR expected expression, found `let` statement
 
     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR `let` expressions are not supported here
-    //~| ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     //~| ERROR expected expression, found `let` statement
     //~| ERROR expected expression, found `let` statement
 }
 
 fn _while() {
     while (let 0 = 1) {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     while (((let 0 = 1))) {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     while (let 0 = 1) && true {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     while true && (let 0 = 1) {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     while (let 0 = 1) && (let 0 = 1) {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     //~| ERROR expected expression, found `let` statement
 
     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR `let` expressions are not supported here
-    //~| ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     //~| ERROR expected expression, found `let` statement
     //~| ERROR expected expression, found `let` statement
 }
@@ -97,32 +79,21 @@ fn _macros() {
         }
     }
     use_expr!((let 0 = 1 && 0 == 0));
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     use_expr!((let 0 = 1));
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 }
 
 fn nested_within_if_expr() {
     if &let 0 = 0 {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR mismatched types
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     if !let 0 = 0 {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     if *let 0 = 0 {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR type `bool` cannot be dereferenced
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     if -let 0 = 0 {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR cannot apply unary operator `-` to type `bool`
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     fn _check_try_binds_tighter() -> Result<(), ()> {
         if let 0 = 0? {}
@@ -130,91 +101,63 @@ fn nested_within_if_expr() {
         Ok(())
     }
     if (let 0 = 0)? {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR the `?` operator can only be applied to values that implement `Try`
-    //~| ERROR the `?` operator can only be used in a function that returns `Result`
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     if true || let 0 = 0 {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     if (true || let 0 = 0) {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     if true && (true || let 0 = 0) {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     if true || (true && let 0 = 0) {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     let mut x = true;
     if x = let 0 = 0 {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR mismatched types
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     if true..(let 0 = 0) {}
-    //~^ ERROR `let` expressions are not supported here
+    //~^ ERROR expected expression, found `let` statement
     //~| ERROR mismatched types
-    //~| ERROR expected expression, found `let` statement
     if ..(let 0 = 0) {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR mismatched types
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     if (let 0 = 0).. {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR mismatched types
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     // Binds as `(let ... = true)..true &&/|| false`.
     if let Range { start: _, end: _ } = true..true && false {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR mismatched types
+    //~^ ERROR expected expression, found `let` statement
     //~| ERROR mismatched types
     if let Range { start: _, end: _ } = true..true || false {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR mismatched types
+    //~^ ERROR expected expression, found `let` statement
     //~| ERROR mismatched types
 
     // Binds as `(let Range { start: F, end } = F)..(|| true)`.
     const F: fn() -> bool = || true;
     if let Range { start: F, end } = F..|| true {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR mismatched types
-    //~| ERROR mismatched types
+    //~^ ERROR expected expression, found `let` statement
     //~| ERROR mismatched types
 
     // Binds as `(let Range { start: true, end } = t)..(&&false)`.
     let t = &&true;
     if let Range { start: true, end } = t..&&false {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR mismatched types
-    //~| ERROR mismatched types
+    //~^ ERROR expected expression, found `let` statement
     //~| ERROR mismatched types
 
     if let true = let true = true {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 }
 
 fn nested_within_while_expr() {
     while &let 0 = 0 {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR mismatched types
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     while !let 0 = 0 {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     while *let 0 = 0 {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR type `bool` cannot be dereferenced
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     while -let 0 = 0 {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR cannot apply unary operator `-` to type `bool`
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     fn _check_try_binds_tighter() -> Result<(), ()> {
         while let 0 = 0? {}
@@ -222,72 +165,51 @@ fn nested_within_while_expr() {
         Ok(())
     }
     while (let 0 = 0)? {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR the `?` operator can only be applied to values that implement `Try`
-    //~| ERROR the `?` operator can only be used in a function that returns `Result`
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     while true || let 0 = 0 {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     while (true || let 0 = 0) {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     while true && (true || let 0 = 0) {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     while true || (true && let 0 = 0) {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     let mut x = true;
     while x = let 0 = 0 {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR mismatched types
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     while true..(let 0 = 0) {}
-    //~^ ERROR `let` expressions are not supported here
+    //~^ ERROR expected expression, found `let` statement
     //~| ERROR mismatched types
-    //~| ERROR expected expression, found `let` statement
     while ..(let 0 = 0) {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR mismatched types
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     while (let 0 = 0).. {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR mismatched types
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     // Binds as `(let ... = true)..true &&/|| false`.
     while let Range { start: _, end: _ } = true..true && false {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR mismatched types
+    //~^ ERROR expected expression, found `let` statement
     //~| ERROR mismatched types
     while let Range { start: _, end: _ } = true..true || false {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR mismatched types
+    //~^ ERROR expected expression, found `let` statement
     //~| ERROR mismatched types
 
     // Binds as `(let Range { start: F, end } = F)..(|| true)`.
     const F: fn() -> bool = || true;
     while let Range { start: F, end } = F..|| true {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR mismatched types
-    //~| ERROR mismatched types
+    //~^ ERROR expected expression, found `let` statement
     //~| ERROR mismatched types
 
     // Binds as `(let Range { start: true, end } = t)..(&&false)`.
     let t = &&true;
     while let Range { start: true, end } = t..&&false {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR mismatched types
-    //~| ERROR mismatched types
+    //~^ ERROR expected expression, found `let` statement
     //~| ERROR mismatched types
 
     while let true = let true = true {}
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 }
 
 fn not_error_because_clarified_intent() {
@@ -302,20 +224,14 @@ fn not_error_because_clarified_intent() {
 
 fn outside_if_and_while_expr() {
     &let 0 = 0;
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     !let 0 = 0;
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     *let 0 = 0;
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR type `bool` cannot be dereferenced
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     -let 0 = 0;
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR cannot apply unary operator `-` to type `bool`
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     fn _check_try_binds_tighter() -> Result<(), ()> {
         let 0 = 0?;
@@ -323,44 +239,32 @@ fn outside_if_and_while_expr() {
         Ok(())
     }
     (let 0 = 0)?;
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR the `?` operator can only be used in a function that returns `Result`
-    //~| ERROR the `?` operator can only be applied to values that implement `Try`
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     true || let 0 = 0;
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     (true || let 0 = 0);
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     true && (true || let 0 = 0);
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     let mut x = true;
     x = let 0 = 0;
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     true..(let 0 = 0);
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     ..(let 0 = 0);
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     (let 0 = 0)..;
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     (let Range { start: _, end: _ } = true..true || false);
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR mismatched types
+    //~^ ERROR mismatched types
     //~| ERROR expected expression, found `let` statement
 
     (let true = let true = true);
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     //~| ERROR expected expression, found `let` statement
 
     {
@@ -377,9 +281,7 @@ fn outside_if_and_while_expr() {
 
     // Check function tail position.
     &let 0 = 0
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR mismatched types
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 }
 
 // Let's make sure that `let` inside const generic arguments are considered.
@@ -389,20 +291,17 @@ fn inside_const_generic_arguments() {
 
     if let A::<{
         true && let 1 = 1
-        //~^ ERROR `let` expressions are not supported here
-        //~| ERROR expected expression, found `let` statement
+        //~^ ERROR expected expression, found `let` statement
     }>::O = 5 {}
 
     while let A::<{
         true && let 1 = 1
-        //~^ ERROR `let` expressions are not supported here
-        //~| ERROR expected expression, found `let` statement
+        //~^ ERROR expected expression, found `let` statement
     }>::O = 5 {}
 
     if A::<{
         true && let 1 = 1
-        //~^ ERROR `let` expressions are not supported here
-        //~| ERROR expected expression, found `let` statement
+        //~^ ERROR expected expression, found `let` statement
     }>::O == 5 {}
 
     // In the cases above we have `ExprKind::Block` to help us out.
@@ -413,8 +312,7 @@ fn inside_const_generic_arguments() {
 
     if A::<
         true && let 1 = 1
-        //~^ ERROR `let` expressions are not supported here
-        //~| ERROR expressions must be enclosed in braces
+        //~^ ERROR expressions must be enclosed in braces
         //~| ERROR expected expression, found `let` statement
     >::O == 5 {}
 }
@@ -423,38 +321,29 @@ fn with_parenthesis() {
     let opt = Some(Some(1i32));
 
     if (let Some(a) = opt && true) {
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     }
 
     if (let Some(a) = opt) && true {
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     }
     if (let Some(a) = opt) && (let Some(b) = a) {
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     //~| ERROR expected expression, found `let` statement
     }
     if let Some(a) = opt && (true && true) {
     }
 
     if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     //~| ERROR expected expression, found `let` statement
     }
     if (let Some(a) = opt && (let Some(b) = a)) && true {
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     //~| ERROR expected expression, found `let` statement
     }
     if (let Some(a) = opt && (true)) && true {
-    //~^ ERROR `let` expressions are not supported here
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     }
 
     if (true && (true)) && let Some(a) = opt {
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.stderr
index 81933173c25..ab58abf4d46 100644
--- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.stderr
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.stderr
@@ -2,503 +2,6 @@ error: expected expression, found `let` statement
   --> $DIR/disallowed-positions.rs:29:9
    |
 LL |     if (let 0 = 1) {}
-   |         ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:33:11
-   |
-LL |     if (((let 0 = 1))) {}
-   |           ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:37:9
-   |
-LL |     if (let 0 = 1) && true {}
-   |         ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:41:17
-   |
-LL |     if true && (let 0 = 1) {}
-   |                 ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:45:9
-   |
-LL |     if (let 0 = 1) && (let 0 = 1) {}
-   |         ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:45:24
-   |
-LL |     if (let 0 = 1) && (let 0 = 1) {}
-   |                        ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:51:35
-   |
-LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
-   |                                   ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:51:48
-   |
-LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
-   |                                                ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:51:61
-   |
-LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
-   |                                                             ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:61:12
-   |
-LL |     while (let 0 = 1) {}
-   |            ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:65:14
-   |
-LL |     while (((let 0 = 1))) {}
-   |              ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:69:12
-   |
-LL |     while (let 0 = 1) && true {}
-   |            ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:73:20
-   |
-LL |     while true && (let 0 = 1) {}
-   |                    ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:77:12
-   |
-LL |     while (let 0 = 1) && (let 0 = 1) {}
-   |            ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:77:27
-   |
-LL |     while (let 0 = 1) && (let 0 = 1) {}
-   |                           ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:83:38
-   |
-LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
-   |                                      ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:83:51
-   |
-LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
-   |                                                   ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:83:64
-   |
-LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
-   |                                                                ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:110:9
-   |
-LL |     if &let 0 = 0 {}
-   |         ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:115:9
-   |
-LL |     if !let 0 = 0 {}
-   |         ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:118:9
-   |
-LL |     if *let 0 = 0 {}
-   |         ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:122:9
-   |
-LL |     if -let 0 = 0 {}
-   |         ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:132:9
-   |
-LL |     if (let 0 = 0)? {}
-   |         ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:138:16
-   |
-LL |     if true || let 0 = 0 {}
-   |                ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:141:17
-   |
-LL |     if (true || let 0 = 0) {}
-   |                 ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:144:25
-   |
-LL |     if true && (true || let 0 = 0) {}
-   |                         ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:147:25
-   |
-LL |     if true || (true && let 0 = 0) {}
-   |                         ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:152:12
-   |
-LL |     if x = let 0 = 0 {}
-   |            ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:157:15
-   |
-LL |     if true..(let 0 = 0) {}
-   |               ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:161:11
-   |
-LL |     if ..(let 0 = 0) {}
-   |           ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:165:9
-   |
-LL |     if (let 0 = 0).. {}
-   |         ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:196:19
-   |
-LL |     if let true = let true = true {}
-   |                   ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:202:12
-   |
-LL |     while &let 0 = 0 {}
-   |            ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:207:12
-   |
-LL |     while !let 0 = 0 {}
-   |            ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:210:12
-   |
-LL |     while *let 0 = 0 {}
-   |            ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:214:12
-   |
-LL |     while -let 0 = 0 {}
-   |            ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:224:12
-   |
-LL |     while (let 0 = 0)? {}
-   |            ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:230:19
-   |
-LL |     while true || let 0 = 0 {}
-   |                   ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:233:20
-   |
-LL |     while (true || let 0 = 0) {}
-   |                    ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:236:28
-   |
-LL |     while true && (true || let 0 = 0) {}
-   |                            ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:239:28
-   |
-LL |     while true || (true && let 0 = 0) {}
-   |                            ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:244:15
-   |
-LL |     while x = let 0 = 0 {}
-   |               ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:249:18
-   |
-LL |     while true..(let 0 = 0) {}
-   |                  ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:253:14
-   |
-LL |     while ..(let 0 = 0) {}
-   |              ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:257:12
-   |
-LL |     while (let 0 = 0).. {}
-   |            ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:288:22
-   |
-LL |     while let true = let true = true {}
-   |                      ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:304:6
-   |
-LL |     &let 0 = 0;
-   |      ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:308:6
-   |
-LL |     !let 0 = 0;
-   |      ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:311:6
-   |
-LL |     *let 0 = 0;
-   |      ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:315:6
-   |
-LL |     -let 0 = 0;
-   |      ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:325:6
-   |
-LL |     (let 0 = 0)?;
-   |      ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:331:13
-   |
-LL |     true || let 0 = 0;
-   |             ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:334:14
-   |
-LL |     (true || let 0 = 0);
-   |              ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:337:22
-   |
-LL |     true && (true || let 0 = 0);
-   |                      ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:342:9
-   |
-LL |     x = let 0 = 0;
-   |         ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:346:12
-   |
-LL |     true..(let 0 = 0);
-   |            ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:349:8
-   |
-LL |     ..(let 0 = 0);
-   |        ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:352:6
-   |
-LL |     (let 0 = 0)..;
-   |      ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:356:6
-   |
-LL |     (let Range { start: _, end: _ } = true..true || false);
-   |      ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:361:6
-   |
-LL |     (let true = let true = true);
-   |      ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:361:17
-   |
-LL |     (let true = let true = true);
-   |                 ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:368:25
-   |
-LL |         let x = true && let y = 1;
-   |                         ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:374:19
-   |
-LL |         [1, 2, 3][let _ = ()]
-   |                   ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:379:6
-   |
-LL |     &let 0 = 0
-   |      ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:391:17
-   |
-LL |         true && let 1 = 1
-   |                 ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:397:17
-   |
-LL |         true && let 1 = 1
-   |                 ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:403:17
-   |
-LL |         true && let 1 = 1
-   |                 ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:415:17
-   |
-LL |         true && let 1 = 1
-   |                 ^^^
-
-error: expressions must be enclosed in braces to be used as const generic arguments
-  --> $DIR/disallowed-positions.rs:415:9
-   |
-LL |         true && let 1 = 1
-   |         ^^^^^^^^^^^^^^^^^
-   |
-help: enclose the `const` expression in braces
-   |
-LL |         { true && let 1 = 1 }
-   |         +                   +
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:425:9
-   |
-LL |     if (let Some(a) = opt && true) {
-   |         ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:430:9
-   |
-LL |     if (let Some(a) = opt) && true {
-   |         ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:434:9
-   |
-LL |     if (let Some(a) = opt) && (let Some(b) = a) {
-   |         ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:434:32
-   |
-LL |     if (let Some(a) = opt) && (let Some(b) = a) {
-   |                                ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:443:9
-   |
-LL |     if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
-   |         ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:443:31
-   |
-LL |     if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
-   |                               ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:449:9
-   |
-LL |     if (let Some(a) = opt && (let Some(b) = a)) && true {
-   |         ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:449:31
-   |
-LL |     if (let Some(a) = opt && (let Some(b) = a)) && true {
-   |                               ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:455:9
-   |
-LL |     if (let Some(a) = opt && (true)) && true {
-   |         ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:472:22
-   |
-LL |     let x = (true && let y = 1);
-   |                      ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:477:20
-   |
-LL |         ([1, 2, 3][let _ = ()])
-   |                    ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:99:16
-   |
-LL |     use_expr!((let 0 = 1 && 0 == 0));
-   |                ^^^
-
-error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:103:16
-   |
-LL |     use_expr!((let 0 = 1));
-   |                ^^^
-
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:29:9
-   |
-LL |     if (let 0 = 1) {}
    |         ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
@@ -508,1012 +11,863 @@ note: `let`s wrapped in parentheses are not supported in a context with let chai
 LL |     if (let 0 = 1) {}
    |         ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:33:11
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:32:11
    |
 LL |     if (((let 0 = 1))) {}
    |           ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:33:11
+  --> $DIR/disallowed-positions.rs:32:11
    |
 LL |     if (((let 0 = 1))) {}
    |           ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:37:9
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:35:9
    |
 LL |     if (let 0 = 1) && true {}
    |         ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:37:9
+  --> $DIR/disallowed-positions.rs:35:9
    |
 LL |     if (let 0 = 1) && true {}
    |         ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:41:17
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:38:17
    |
 LL |     if true && (let 0 = 1) {}
    |                 ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:41:17
+  --> $DIR/disallowed-positions.rs:38:17
    |
 LL |     if true && (let 0 = 1) {}
    |                 ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:45:9
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:41:9
    |
 LL |     if (let 0 = 1) && (let 0 = 1) {}
    |         ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:45:9
+  --> $DIR/disallowed-positions.rs:41:9
    |
 LL |     if (let 0 = 1) && (let 0 = 1) {}
    |         ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:45:24
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:41:24
    |
 LL |     if (let 0 = 1) && (let 0 = 1) {}
    |                        ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:45:24
+  --> $DIR/disallowed-positions.rs:41:24
    |
 LL |     if (let 0 = 1) && (let 0 = 1) {}
    |                        ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:51:35
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:45:35
    |
 LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                   ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:51:35
+  --> $DIR/disallowed-positions.rs:45:35
    |
 LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:51:48
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:45:48
    |
 LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                                ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:51:35
+  --> $DIR/disallowed-positions.rs:45:35
    |
 LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:51:61
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:45:61
    |
 LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                                             ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:51:35
+  --> $DIR/disallowed-positions.rs:45:35
    |
 LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:61:12
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:52:12
    |
 LL |     while (let 0 = 1) {}
    |            ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:61:12
+  --> $DIR/disallowed-positions.rs:52:12
    |
 LL |     while (let 0 = 1) {}
    |            ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:65:14
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:55:14
    |
 LL |     while (((let 0 = 1))) {}
    |              ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:65:14
+  --> $DIR/disallowed-positions.rs:55:14
    |
 LL |     while (((let 0 = 1))) {}
    |              ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:69:12
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:58:12
    |
 LL |     while (let 0 = 1) && true {}
    |            ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:69:12
+  --> $DIR/disallowed-positions.rs:58:12
    |
 LL |     while (let 0 = 1) && true {}
    |            ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:73:20
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:61:20
    |
 LL |     while true && (let 0 = 1) {}
    |                    ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:73:20
+  --> $DIR/disallowed-positions.rs:61:20
    |
 LL |     while true && (let 0 = 1) {}
    |                    ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:77:12
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:64:12
    |
 LL |     while (let 0 = 1) && (let 0 = 1) {}
    |            ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:77:12
+  --> $DIR/disallowed-positions.rs:64:12
    |
 LL |     while (let 0 = 1) && (let 0 = 1) {}
    |            ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:77:27
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:64:27
    |
 LL |     while (let 0 = 1) && (let 0 = 1) {}
    |                           ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:77:27
+  --> $DIR/disallowed-positions.rs:64:27
    |
 LL |     while (let 0 = 1) && (let 0 = 1) {}
    |                           ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:83:38
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:68:38
    |
 LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                      ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:83:38
+  --> $DIR/disallowed-positions.rs:68:38
    |
 LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:83:51
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:68:51
    |
 LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                                   ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:83:38
+  --> $DIR/disallowed-positions.rs:68:38
    |
 LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:83:64
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:68:64
    |
 LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                                                ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:83:38
+  --> $DIR/disallowed-positions.rs:68:38
    |
 LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:99:16
-   |
-LL |     use_expr!((let 0 = 1 && 0 == 0));
-   |                ^^^^^^^^^
-   |
-   = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:99:16
-   |
-LL |     use_expr!((let 0 = 1 && 0 == 0));
-   |                ^^^^^^^^^^^^^^^^^^^
-
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:99:16
-   |
-LL |     use_expr!((let 0 = 1 && 0 == 0));
-   |                ^^^^^^^^^
-   |
-   = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:99:16
-   |
-LL |     use_expr!((let 0 = 1 && 0 == 0));
-   |                ^^^^^^^^^^^^^^^^^^^
-
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:103:16
-   |
-LL |     use_expr!((let 0 = 1));
-   |                ^^^^^^^^^
-   |
-   = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:103:16
-   |
-LL |     use_expr!((let 0 = 1));
-   |                ^^^^^^^^^
-
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:103:16
-   |
-LL |     use_expr!((let 0 = 1));
-   |                ^^^^^^^^^
-   |
-   = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:103:16
-   |
-LL |     use_expr!((let 0 = 1));
-   |                ^^^^^^^^^
-
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:110:9
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:88:9
    |
 LL |     if &let 0 = 0 {}
    |         ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:115:9
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:91:9
    |
 LL |     if !let 0 = 0 {}
    |         ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:118:9
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:93:9
    |
 LL |     if *let 0 = 0 {}
    |         ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:122:9
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:95:9
    |
 LL |     if -let 0 = 0 {}
    |         ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:132:9
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:103:9
    |
 LL |     if (let 0 = 0)? {}
    |         ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:132:9
-   |
-LL |     if (let 0 = 0)? {}
-   |         ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:138:16
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:106:16
    |
 LL |     if true || let 0 = 0 {}
    |                ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `||` operators are not supported in let chain expressions
-  --> $DIR/disallowed-positions.rs:138:13
+  --> $DIR/disallowed-positions.rs:106:13
    |
 LL |     if true || let 0 = 0 {}
    |             ^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:141:17
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:108:17
    |
 LL |     if (true || let 0 = 0) {}
    |                 ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `||` operators are not supported in let chain expressions
-  --> $DIR/disallowed-positions.rs:141:14
-   |
-LL |     if (true || let 0 = 0) {}
-   |              ^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:144:25
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:110:25
    |
 LL |     if true && (true || let 0 = 0) {}
    |                         ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `||` operators are not supported in let chain expressions
-  --> $DIR/disallowed-positions.rs:144:22
-   |
-LL |     if true && (true || let 0 = 0) {}
-   |                      ^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:147:25
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:112:25
    |
 LL |     if true || (true && let 0 = 0) {}
    |                         ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:147:17
-   |
-LL |     if true || (true && let 0 = 0) {}
-   |                 ^^^^^^^^^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:152:12
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:116:12
    |
 LL |     if x = let 0 = 0 {}
-   |            ^^^^^^^^^
+   |            ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:157:15
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:119:15
    |
 LL |     if true..(let 0 = 0) {}
    |               ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:157:15
-   |
-LL |     if true..(let 0 = 0) {}
-   |               ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:161:11
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:122:11
    |
 LL |     if ..(let 0 = 0) {}
    |           ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:161:11
-   |
-LL |     if ..(let 0 = 0) {}
-   |           ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:165:9
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:124:9
    |
 LL |     if (let 0 = 0).. {}
    |         ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:165:9
-   |
-LL |     if (let 0 = 0).. {}
-   |         ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:171:8
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:128:8
    |
 LL |     if let Range { start: _, end: _ } = true..true && false {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:175:8
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:131:8
    |
 LL |     if let Range { start: _, end: _ } = true..true || false {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:182:8
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:137:8
    |
 LL |     if let Range { start: F, end } = F..|| true {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:190:8
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:143:8
    |
 LL |     if let Range { start: true, end } = t..&&false {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:196:19
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:147:19
    |
 LL |     if let true = let true = true {}
-   |                   ^^^^^^^^^^^^^^^
+   |                   ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:202:12
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:152:12
    |
 LL |     while &let 0 = 0 {}
    |            ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:207:12
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:155:12
    |
 LL |     while !let 0 = 0 {}
    |            ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:210:12
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:157:12
    |
 LL |     while *let 0 = 0 {}
    |            ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:214:12
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:159:12
    |
 LL |     while -let 0 = 0 {}
    |            ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:224:12
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:167:12
    |
 LL |     while (let 0 = 0)? {}
    |            ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:224:12
-   |
-LL |     while (let 0 = 0)? {}
-   |            ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:230:19
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:170:19
    |
 LL |     while true || let 0 = 0 {}
    |                   ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `||` operators are not supported in let chain expressions
-  --> $DIR/disallowed-positions.rs:230:16
+  --> $DIR/disallowed-positions.rs:170:16
    |
 LL |     while true || let 0 = 0 {}
    |                ^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:233:20
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:172:20
    |
 LL |     while (true || let 0 = 0) {}
    |                    ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `||` operators are not supported in let chain expressions
-  --> $DIR/disallowed-positions.rs:233:17
-   |
-LL |     while (true || let 0 = 0) {}
-   |                 ^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:236:28
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:174:28
    |
 LL |     while true && (true || let 0 = 0) {}
    |                            ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `||` operators are not supported in let chain expressions
-  --> $DIR/disallowed-positions.rs:236:25
-   |
-LL |     while true && (true || let 0 = 0) {}
-   |                         ^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:239:28
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:176:28
    |
 LL |     while true || (true && let 0 = 0) {}
    |                            ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:239:20
-   |
-LL |     while true || (true && let 0 = 0) {}
-   |                    ^^^^^^^^^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:244:15
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:180:15
    |
 LL |     while x = let 0 = 0 {}
-   |               ^^^^^^^^^
+   |               ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:249:18
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:183:18
    |
 LL |     while true..(let 0 = 0) {}
    |                  ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:249:18
-   |
-LL |     while true..(let 0 = 0) {}
-   |                  ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:253:14
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:186:14
    |
 LL |     while ..(let 0 = 0) {}
    |              ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:253:14
-   |
-LL |     while ..(let 0 = 0) {}
-   |              ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:257:12
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:188:12
    |
 LL |     while (let 0 = 0).. {}
    |            ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:257:12
-   |
-LL |     while (let 0 = 0).. {}
-   |            ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:263:11
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:192:11
    |
 LL |     while let Range { start: _, end: _ } = true..true && false {}
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:267:11
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:195:11
    |
 LL |     while let Range { start: _, end: _ } = true..true || false {}
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:274:11
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:201:11
    |
 LL |     while let Range { start: F, end } = F..|| true {}
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:282:11
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:207:11
    |
 LL |     while let Range { start: true, end } = t..&&false {}
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:288:22
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:211:22
    |
 LL |     while let true = let true = true {}
-   |                      ^^^^^^^^^^^^^^^
+   |                      ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:304:6
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:226:6
    |
 LL |     &let 0 = 0;
-   |      ^^^^^^^^^
+   |      ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:308:6
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:229:6
    |
 LL |     !let 0 = 0;
-   |      ^^^^^^^^^
+   |      ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:311:6
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:231:6
    |
 LL |     *let 0 = 0;
-   |      ^^^^^^^^^
+   |      ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:315:6
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:233:6
    |
 LL |     -let 0 = 0;
-   |      ^^^^^^^^^
+   |      ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:325:6
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:241:6
    |
 LL |     (let 0 = 0)?;
-   |      ^^^^^^^^^
+   |      ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:325:6
-   |
-LL |     (let 0 = 0)?;
-   |      ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:331:13
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:244:13
    |
 LL |     true || let 0 = 0;
-   |             ^^^^^^^^^
+   |             ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `||` operators are not supported in let chain expressions
-  --> $DIR/disallowed-positions.rs:331:10
-   |
-LL |     true || let 0 = 0;
-   |          ^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:334:14
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:246:14
    |
 LL |     (true || let 0 = 0);
-   |              ^^^^^^^^^
+   |              ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `||` operators are not supported in let chain expressions
-  --> $DIR/disallowed-positions.rs:334:11
-   |
-LL |     (true || let 0 = 0);
-   |           ^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:337:22
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:248:22
    |
 LL |     true && (true || let 0 = 0);
-   |                      ^^^^^^^^^
+   |                      ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `||` operators are not supported in let chain expressions
-  --> $DIR/disallowed-positions.rs:337:19
-   |
-LL |     true && (true || let 0 = 0);
-   |                   ^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:342:9
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:252:9
    |
 LL |     x = let 0 = 0;
-   |         ^^^^^^^^^
+   |         ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:346:12
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:255:12
    |
 LL |     true..(let 0 = 0);
-   |            ^^^^^^^^^
+   |            ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:346:12
-   |
-LL |     true..(let 0 = 0);
-   |            ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:349:8
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:257:8
    |
 LL |     ..(let 0 = 0);
-   |        ^^^^^^^^^
+   |        ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:349:8
-   |
-LL |     ..(let 0 = 0);
-   |        ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:352:6
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:259:6
    |
 LL |     (let 0 = 0)..;
-   |      ^^^^^^^^^
+   |      ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:352:6
-   |
-LL |     (let 0 = 0)..;
-   |      ^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:356:6
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:262:6
    |
 LL |     (let Range { start: _, end: _ } = true..true || false);
-   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |      ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:361:6
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:266:6
    |
 LL |     (let true = let true = true);
-   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |      ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
-note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:361:6
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:266:17
    |
 LL |     (let true = let true = true);
-   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                 ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:272:25
+   |
+LL |         let x = true && let y = 1;
+   |                         ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:278:19
+   |
+LL |         [1, 2, 3][let _ = ()]
+   |                   ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:379:6
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:283:6
    |
 LL |     &let 0 = 0
-   |      ^^^^^^^^^
+   |      ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:391:17
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:293:17
    |
 LL |         true && let 1 = 1
-   |                 ^^^^^^^^^
+   |                 ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:397:17
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:298:17
    |
 LL |         true && let 1 = 1
-   |                 ^^^^^^^^^
+   |                 ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:403:17
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:303:17
    |
 LL |         true && let 1 = 1
-   |                 ^^^^^^^^^
+   |                 ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:415:17
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:314:17
    |
 LL |         true && let 1 = 1
-   |                 ^^^^^^^^^
+   |                 ^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:425:9
+error: expressions must be enclosed in braces to be used as const generic arguments
+  --> $DIR/disallowed-positions.rs:314:9
+   |
+LL |         true && let 1 = 1
+   |         ^^^^^^^^^^^^^^^^^
+   |
+help: enclose the `const` expression in braces
+   |
+LL |         { true && let 1 = 1 }
+   |         +                   +
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:323:9
    |
 LL |     if (let Some(a) = opt && true) {
    |         ^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:425:9
+  --> $DIR/disallowed-positions.rs:323:9
    |
 LL |     if (let Some(a) = opt && true) {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:430:9
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:327:9
    |
 LL |     if (let Some(a) = opt) && true {
    |         ^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:430:9
+  --> $DIR/disallowed-positions.rs:327:9
    |
 LL |     if (let Some(a) = opt) && true {
    |         ^^^^^^^^^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:434:9
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:330:9
    |
 LL |     if (let Some(a) = opt) && (let Some(b) = a) {
    |         ^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:434:9
+  --> $DIR/disallowed-positions.rs:330:9
    |
 LL |     if (let Some(a) = opt) && (let Some(b) = a) {
    |         ^^^^^^^^^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:434:32
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:330:32
    |
 LL |     if (let Some(a) = opt) && (let Some(b) = a) {
    |                                ^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:434:32
+  --> $DIR/disallowed-positions.rs:330:32
    |
 LL |     if (let Some(a) = opt) && (let Some(b) = a) {
    |                                ^^^^^^^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:443:9
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:337:9
    |
 LL |     if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
    |         ^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:443:9
+  --> $DIR/disallowed-positions.rs:337:9
    |
 LL |     if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:443:31
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:337:31
    |
 LL |     if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
    |                               ^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:443:31
+  --> $DIR/disallowed-positions.rs:337:31
    |
 LL |     if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
    |                               ^^^^^^^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:449:9
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:341:9
    |
 LL |     if (let Some(a) = opt && (let Some(b) = a)) && true {
    |         ^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:449:9
+  --> $DIR/disallowed-positions.rs:341:9
    |
 LL |     if (let Some(a) = opt && (let Some(b) = a)) && true {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:449:31
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:341:31
    |
 LL |     if (let Some(a) = opt && (let Some(b) = a)) && true {
    |                               ^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:449:31
+  --> $DIR/disallowed-positions.rs:341:31
    |
 LL |     if (let Some(a) = opt && (let Some(b) = a)) && true {
    |                               ^^^^^^^^^^^^^^^
 
-error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:455:9
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:345:9
    |
 LL |     if (let Some(a) = opt && (true)) && true {
    |         ^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:455:9
+  --> $DIR/disallowed-positions.rs:345:9
    |
 LL |     if (let Some(a) = opt && (true)) && true {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:110:8
-   |
-LL |     if &let 0 = 0 {}
-   |        ^^^^^^^^^^ expected `bool`, found `&bool`
-   |
-help: consider removing the borrow
-   |
-LL -     if &let 0 = 0 {}
-LL +     if let 0 = 0 {}
-   |
-
-error[E0614]: type `bool` cannot be dereferenced
-  --> $DIR/disallowed-positions.rs:118:8
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:361:22
    |
-LL |     if *let 0 = 0 {}
-   |        ^^^^^^^^^^
-
-error[E0600]: cannot apply unary operator `-` to type `bool`
-  --> $DIR/disallowed-positions.rs:122:8
+LL |     let x = (true && let y = 1);
+   |                      ^^^
    |
-LL |     if -let 0 = 0 {}
-   |        ^^^^^^^^^^ cannot apply unary operator `-`
+   = note: only supported directly in conditions of `if` and `while` expressions
 
-error[E0277]: the `?` operator can only be applied to values that implement `Try`
-  --> $DIR/disallowed-positions.rs:132:8
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:366:20
    |
-LL |     if (let 0 = 0)? {}
-   |        ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool`
+LL |         ([1, 2, 3][let _ = ()])
+   |                    ^^^
    |
-   = help: the trait `Try` is not implemented for `bool`
+   = note: only supported directly in conditions of `if` and `while` expressions
 
-error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
-  --> $DIR/disallowed-positions.rs:132:19
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:81:16
    |
-LL | fn nested_within_if_expr() {
-   | -------------------------- this function should return `Result` or `Option` to accept `?`
-...
-LL |     if (let 0 = 0)? {}
-   |                   ^ cannot use the `?` operator in a function that returns `()`
+LL |     use_expr!((let 0 = 1 && 0 == 0));
+   |                ^^^
    |
-   = help: the trait `FromResidual<_>` is not implemented for `()`
+   = note: only supported directly in conditions of `if` and `while` expressions
 
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:152:8
-   |
-LL |     if x = let 0 = 0 {}
-   |        ^^^^^^^^^^^^^ expected `bool`, found `()`
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:83:16
    |
-help: you might have meant to compare for equality
+LL |     use_expr!((let 0 = 1));
+   |                ^^^
    |
-LL |     if x == let 0 = 0 {}
-   |           +
+   = note: only supported directly in conditions of `if` and `while` expressions
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:157:8
+  --> $DIR/disallowed-positions.rs:119:8
    |
 LL |     if true..(let 0 = 0) {}
    |        ^^^^^^^^^^^^^^^^^ expected `bool`, found `Range<bool>`
@@ -1522,25 +876,7 @@ LL |     if true..(let 0 = 0) {}
             found struct `std::ops::Range<bool>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:161:8
-   |
-LL |     if ..(let 0 = 0) {}
-   |        ^^^^^^^^^^^^^ expected `bool`, found `RangeTo<bool>`
-   |
-   = note: expected type `bool`
-            found struct `RangeTo<bool>`
-
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:165:8
-   |
-LL |     if (let 0 = 0).. {}
-   |        ^^^^^^^^^^^^^ expected `bool`, found `RangeFrom<bool>`
-   |
-   = note: expected type `bool`
-            found struct `RangeFrom<bool>`
-
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:171:12
+  --> $DIR/disallowed-positions.rs:128:12
    |
 LL |     if let Range { start: _, end: _ } = true..true && false {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this expression has type `bool`
@@ -1551,16 +887,7 @@ LL |     if let Range { start: _, end: _ } = true..true && false {}
             found struct `std::ops::Range<_>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:171:8
-   |
-LL |     if let Range { start: _, end: _ } = true..true && false {}
-   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `Range<bool>`
-   |
-   = note: expected type `bool`
-            found struct `std::ops::Range<bool>`
-
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:175:12
+  --> $DIR/disallowed-positions.rs:131:12
    |
 LL |     if let Range { start: _, end: _ } = true..true || false {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this expression has type `bool`
@@ -1571,16 +898,7 @@ LL |     if let Range { start: _, end: _ } = true..true || false {}
             found struct `std::ops::Range<_>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:175:8
-   |
-LL |     if let Range { start: _, end: _ } = true..true || false {}
-   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `Range<bool>`
-   |
-   = note: expected type `bool`
-            found struct `std::ops::Range<bool>`
-
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:182:12
+  --> $DIR/disallowed-positions.rs:137:12
    |
 LL |     if let Range { start: F, end } = F..|| true {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^   - this expression has type `fn() -> bool`
@@ -1591,29 +909,7 @@ LL |     if let Range { start: F, end } = F..|| true {}
                   found struct `std::ops::Range<_>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:182:41
-   |
-LL |     if let Range { start: F, end } = F..|| true {}
-   |                                         ^^^^^^^ expected `bool`, found closure
-   |
-   = note: expected type `bool`
-           found closure `[closure@$DIR/disallowed-positions.rs:182:41: 182:43]`
-help: use parentheses to call this closure
-   |
-LL |     if let Range { start: F, end } = F..(|| true)() {}
-   |                                         +       +++
-
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:182:8
-   |
-LL |     if let Range { start: F, end } = F..|| true {}
-   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `Range<bool>`
-   |
-   = note: expected type `bool`
-            found struct `std::ops::Range<bool>`
-
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:190:12
+  --> $DIR/disallowed-positions.rs:143:12
    |
 LL |     if let Range { start: true, end } = t..&&false {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^   - this expression has type `&&bool`
@@ -1623,29 +919,8 @@ LL |     if let Range { start: true, end } = t..&&false {}
    = note: expected type `bool`
             found struct `std::ops::Range<_>`
 
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:190:44
-   |
-LL |     if let Range { start: true, end } = t..&&false {}
-   |                                            ^^^^^^^ expected `bool`, found `&&bool`
-   |
-help: consider removing the `&&`
-   |
-LL -     if let Range { start: true, end } = t..&&false {}
-LL +     if let Range { start: true, end } = t..false {}
-   |
-
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:190:8
-   |
-LL |     if let Range { start: true, end } = t..&&false {}
-   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `Range<bool>`
-   |
-   = note: expected type `bool`
-            found struct `std::ops::Range<bool>`
-
 error[E0277]: the `?` operator can only be applied to values that implement `Try`
-  --> $DIR/disallowed-positions.rs:128:20
+  --> $DIR/disallowed-positions.rs:99:20
    |
 LL |         if let 0 = 0? {}
    |                    ^^ the `?` operator cannot be applied to type `{integer}`
@@ -1653,61 +928,7 @@ LL |         if let 0 = 0? {}
    = help: the trait `Try` is not implemented for `{integer}`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:202:11
-   |
-LL |     while &let 0 = 0 {}
-   |           ^^^^^^^^^^ expected `bool`, found `&bool`
-   |
-help: consider removing the borrow
-   |
-LL -     while &let 0 = 0 {}
-LL +     while let 0 = 0 {}
-   |
-
-error[E0614]: type `bool` cannot be dereferenced
-  --> $DIR/disallowed-positions.rs:210:11
-   |
-LL |     while *let 0 = 0 {}
-   |           ^^^^^^^^^^
-
-error[E0600]: cannot apply unary operator `-` to type `bool`
-  --> $DIR/disallowed-positions.rs:214:11
-   |
-LL |     while -let 0 = 0 {}
-   |           ^^^^^^^^^^ cannot apply unary operator `-`
-
-error[E0277]: the `?` operator can only be applied to values that implement `Try`
-  --> $DIR/disallowed-positions.rs:224:11
-   |
-LL |     while (let 0 = 0)? {}
-   |           ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool`
-   |
-   = help: the trait `Try` is not implemented for `bool`
-
-error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
-  --> $DIR/disallowed-positions.rs:224:22
-   |
-LL | fn nested_within_while_expr() {
-   | ----------------------------- this function should return `Result` or `Option` to accept `?`
-...
-LL |     while (let 0 = 0)? {}
-   |                      ^ cannot use the `?` operator in a function that returns `()`
-   |
-   = help: the trait `FromResidual<_>` is not implemented for `()`
-
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:244:11
-   |
-LL |     while x = let 0 = 0 {}
-   |           ^^^^^^^^^^^^^ expected `bool`, found `()`
-   |
-help: you might have meant to compare for equality
-   |
-LL |     while x == let 0 = 0 {}
-   |              +
-
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:249:11
+  --> $DIR/disallowed-positions.rs:183:11
    |
 LL |     while true..(let 0 = 0) {}
    |           ^^^^^^^^^^^^^^^^^ expected `bool`, found `Range<bool>`
@@ -1716,25 +937,7 @@ LL |     while true..(let 0 = 0) {}
             found struct `std::ops::Range<bool>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:253:11
-   |
-LL |     while ..(let 0 = 0) {}
-   |           ^^^^^^^^^^^^^ expected `bool`, found `RangeTo<bool>`
-   |
-   = note: expected type `bool`
-            found struct `RangeTo<bool>`
-
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:257:11
-   |
-LL |     while (let 0 = 0).. {}
-   |           ^^^^^^^^^^^^^ expected `bool`, found `RangeFrom<bool>`
-   |
-   = note: expected type `bool`
-            found struct `RangeFrom<bool>`
-
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:263:15
+  --> $DIR/disallowed-positions.rs:192:15
    |
 LL |     while let Range { start: _, end: _ } = true..true && false {}
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this expression has type `bool`
@@ -1745,16 +948,7 @@ LL |     while let Range { start: _, end: _ } = true..true && false {}
             found struct `std::ops::Range<_>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:263:11
-   |
-LL |     while let Range { start: _, end: _ } = true..true && false {}
-   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `Range<bool>`
-   |
-   = note: expected type `bool`
-            found struct `std::ops::Range<bool>`
-
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:267:15
+  --> $DIR/disallowed-positions.rs:195:15
    |
 LL |     while let Range { start: _, end: _ } = true..true || false {}
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this expression has type `bool`
@@ -1765,16 +959,7 @@ LL |     while let Range { start: _, end: _ } = true..true || false {}
             found struct `std::ops::Range<_>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:267:11
-   |
-LL |     while let Range { start: _, end: _ } = true..true || false {}
-   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `Range<bool>`
-   |
-   = note: expected type `bool`
-            found struct `std::ops::Range<bool>`
-
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:274:15
+  --> $DIR/disallowed-positions.rs:201:15
    |
 LL |     while let Range { start: F, end } = F..|| true {}
    |               ^^^^^^^^^^^^^^^^^^^^^^^   - this expression has type `fn() -> bool`
@@ -1785,29 +970,7 @@ LL |     while let Range { start: F, end } = F..|| true {}
                   found struct `std::ops::Range<_>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:274:44
-   |
-LL |     while let Range { start: F, end } = F..|| true {}
-   |                                            ^^^^^^^ expected `bool`, found closure
-   |
-   = note: expected type `bool`
-           found closure `[closure@$DIR/disallowed-positions.rs:274:44: 274:46]`
-help: use parentheses to call this closure
-   |
-LL |     while let Range { start: F, end } = F..(|| true)() {}
-   |                                            +       +++
-
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:274:11
-   |
-LL |     while let Range { start: F, end } = F..|| true {}
-   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `Range<bool>`
-   |
-   = note: expected type `bool`
-            found struct `std::ops::Range<bool>`
-
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:282:15
+  --> $DIR/disallowed-positions.rs:207:15
    |
 LL |     while let Range { start: true, end } = t..&&false {}
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^   - this expression has type `&&bool`
@@ -1817,68 +980,16 @@ LL |     while let Range { start: true, end } = t..&&false {}
    = note: expected type `bool`
             found struct `std::ops::Range<_>`
 
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:282:47
-   |
-LL |     while let Range { start: true, end } = t..&&false {}
-   |                                               ^^^^^^^ expected `bool`, found `&&bool`
-   |
-help: consider removing the `&&`
-   |
-LL -     while let Range { start: true, end } = t..&&false {}
-LL +     while let Range { start: true, end } = t..false {}
-   |
-
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:282:11
-   |
-LL |     while let Range { start: true, end } = t..&&false {}
-   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `Range<bool>`
-   |
-   = note: expected type `bool`
-            found struct `std::ops::Range<bool>`
-
 error[E0277]: the `?` operator can only be applied to values that implement `Try`
-  --> $DIR/disallowed-positions.rs:220:23
+  --> $DIR/disallowed-positions.rs:163:23
    |
 LL |         while let 0 = 0? {}
    |                       ^^ the `?` operator cannot be applied to type `{integer}`
    |
    = help: the trait `Try` is not implemented for `{integer}`
 
-error[E0614]: type `bool` cannot be dereferenced
-  --> $DIR/disallowed-positions.rs:311:5
-   |
-LL |     *let 0 = 0;
-   |     ^^^^^^^^^^
-
-error[E0600]: cannot apply unary operator `-` to type `bool`
-  --> $DIR/disallowed-positions.rs:315:5
-   |
-LL |     -let 0 = 0;
-   |     ^^^^^^^^^^ cannot apply unary operator `-`
-
-error[E0277]: the `?` operator can only be applied to values that implement `Try`
-  --> $DIR/disallowed-positions.rs:325:5
-   |
-LL |     (let 0 = 0)?;
-   |     ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool`
-   |
-   = help: the trait `Try` is not implemented for `bool`
-
-error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
-  --> $DIR/disallowed-positions.rs:325:16
-   |
-LL | fn outside_if_and_while_expr() {
-   | ------------------------------ this function should return `Result` or `Option` to accept `?`
-...
-LL |     (let 0 = 0)?;
-   |                ^ cannot use the `?` operator in a function that returns `()`
-   |
-   = help: the trait `FromResidual<_>` is not implemented for `()`
-
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:356:10
+  --> $DIR/disallowed-positions.rs:262:10
    |
 LL |     (let Range { start: _, end: _ } = true..true || false);
    |          ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this expression has type `bool`
@@ -1888,24 +999,15 @@ LL |     (let Range { start: _, end: _ } = true..true || false);
    = note: expected type `bool`
             found struct `std::ops::Range<_>`
 
-error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:379:5
-   |
-LL | fn outside_if_and_while_expr() {
-   |                                - help: try adding a return type: `-> &bool`
-...
-LL |     &let 0 = 0
-   |     ^^^^^^^^^^ expected `()`, found `&bool`
-
 error[E0277]: the `?` operator can only be applied to values that implement `Try`
-  --> $DIR/disallowed-positions.rs:321:17
+  --> $DIR/disallowed-positions.rs:237:17
    |
 LL |         let 0 = 0?;
    |                 ^^ the `?` operator cannot be applied to type `{integer}`
    |
    = help: the trait `Try` is not implemented for `{integer}`
 
-error: aborting due to 215 previous errors
+error: aborting due to 104 previous errors
 
-Some errors have detailed explanations: E0277, E0308, E0600, E0614.
+Some errors have detailed explanations: E0277, E0308.
 For more information about an error, try `rustc --explain E0277`.
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs
index 2a6c144350a..3c572054e3f 100644
--- a/tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs
@@ -14,7 +14,6 @@ fn main() {
     };
     let Some(n) = opt && let another = n else {
     //~^ ERROR a `&&` expression cannot be directly assigned in `let...else`
-    //~| ERROR `let` expressions are not supported here
     //~| ERROR mismatched types
     //~| ERROR mismatched types
     //~| ERROR expected expression, found `let` statement
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr
index 9bc3e754126..0442f1218b1 100644
--- a/tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr
@@ -14,6 +14,8 @@ error: expected expression, found `let` statement
    |
 LL |     let Some(n) = opt && let another = n else {
    |                          ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
 
 error: a `&&` expression cannot be directly assigned in `let...else`
   --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:15:19
@@ -27,13 +29,13 @@ LL |     let Some(n) = (opt && let another = n) else {
    |                   +                      +
 
 error: this `if` expression is missing a block after the condition
-  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:24:5
+  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:23:5
    |
 LL |     if let Some(n) = opt else {
    |     ^^
    |
 help: add a block here
-  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:24:25
+  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:23:25
    |
 LL |     if let Some(n) = opt else {
    |                         ^
@@ -44,31 +46,31 @@ LL +     let Some(n) = opt else {
    |
 
 error: this `if` expression is missing a block after the condition
-  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:28:5
+  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:27:5
    |
 LL |     if let Some(n) = opt && n == 1 else {
    |     ^^
    |
 help: add a block here
-  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:28:35
+  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:27:35
    |
 LL |     if let Some(n) = opt && n == 1 else {
    |                                   ^
 
 error: this `if` expression is missing a block after the condition
-  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:32:5
+  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:31:5
    |
 LL |     if let Some(n) = opt && let another = n else {
    |     ^^
    |
 help: add a block here
-  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:32:44
+  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:31:44
    |
 LL |     if let Some(n) = opt && let another = n else {
    |                                            ^
 
 error: expected `{`, found keyword `else`
-  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:38:33
+  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:37:33
    |
 LL |         while let Some(n) = opt else {
    |         ----- ----------------- ^^^^ expected `{`
@@ -77,7 +79,7 @@ LL |         while let Some(n) = opt else {
    |         while parsing the body of this `while` expression
 
 error: expected `{`, found keyword `else`
-  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:44:43
+  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:43:43
    |
 LL |         while let Some(n) = opt && n == 1 else {
    |         ----- --------------------------- ^^^^ expected `{`
@@ -86,7 +88,7 @@ LL |         while let Some(n) = opt && n == 1 else {
    |         while parsing the body of this `while` expression
 
 error: expected `{`, found keyword `else`
-  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:50:52
+  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:49:52
    |
 LL |         while let Some(n) = opt && let another = n else {
    |         ----- ------------------------------------ ^^^^ expected `{`
@@ -94,14 +96,6 @@ LL |         while let Some(n) = opt && let another = n else {
    |         |     this `while` condition successfully parsed
    |         while parsing the body of this `while` expression
 
-error: `let` expressions are not supported here
-  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:15:26
-   |
-LL |     let Some(n) = opt && let another = n else {
-   |                          ^^^^^^^^^^^^^^^
-   |
-   = note: only supported directly in conditions of `if` and `while` expressions
-
 error[E0308]: mismatched types
   --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:9:19
    |
@@ -142,6 +136,6 @@ LL |     let Some(n) = opt && let another = n else {
    = note: expected type `bool`
               found enum `Option<_>`
 
-error: aborting due to 14 previous errors
+error: aborting due to 13 previous errors
 
 For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.rs
index 2b407ef510c..bca7564efd8 100644
--- a/tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.rs
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.rs
@@ -43,8 +43,7 @@ fn _macros() {
     macro_rules! noop_expr { ($e:expr) => {}; }
 
     noop_expr!((let 0 = 1));
-    //~^ ERROR `let` expressions in this position are unstable [E0658]
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 
     macro_rules! use_expr {
         ($e:expr) => {
@@ -53,8 +52,7 @@ fn _macros() {
         }
     }
     #[cfg(FALSE)] (let 0 = 1);
-    //~^ ERROR `let` expressions in this position are unstable [E0658]
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
     use_expr!(let 0 = 1);
     //~^ ERROR no rules expected the token `let`
 }
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.stderr
index 7a43b71fc8b..6f74736755e 100644
--- a/tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.stderr
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.stderr
@@ -1,17 +1,21 @@
 error: expected expression, found `let` statement
-  --> $DIR/feature-gate.rs:55:20
+  --> $DIR/feature-gate.rs:54:20
    |
 LL |     #[cfg(FALSE)] (let 0 = 1);
    |                    ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
 
 error: expected expression, found `let` statement
   --> $DIR/feature-gate.rs:45:17
    |
 LL |     noop_expr!((let 0 = 1));
    |                 ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
 
 error: no rules expected the token `let`
-  --> $DIR/feature-gate.rs:58:15
+  --> $DIR/feature-gate.rs:56:15
    |
 LL |     macro_rules! use_expr {
    |     --------------------- when calling this macro
@@ -20,7 +24,7 @@ LL |     use_expr!(let 0 = 1);
    |               ^^^ no rules expected this token in macro call
    |
 note: while trying to match meta-variable `$e:expr`
-  --> $DIR/feature-gate.rs:50:10
+  --> $DIR/feature-gate.rs:49:10
    |
 LL |         ($e:expr) => {
    |          ^^^^^^^
@@ -97,24 +101,6 @@ LL |     while let Range { start: _, end: _ } = (true..true) && false {}
    = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
-error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:55:20
-   |
-LL |     #[cfg(FALSE)] (let 0 = 1);
-   |                    ^^^^^^^^^
-   |
-   = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
-   = help: add `#![feature(let_chains)]` to the crate attributes to enable
-
-error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:45:17
-   |
-LL |     noop_expr!((let 0 = 1));
-   |                 ^^^^^^^^^
-   |
-   = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
-   = help: add `#![feature(let_chains)]` to the crate attributes to enable
-
-error: aborting due to 13 previous errors
+error: aborting due to 11 previous errors
 
 For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs
index a942d1f4caf..dce1c19ff33 100644
--- a/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs
@@ -13,6 +13,7 @@ fn main() {
         if let Some(elem) = _opt && [1, 2, 3][let _ = &&let Some(x) = Some(42)] = 1 {
         //~^ ERROR expected expression, found `let` statement
         //~| ERROR expected expression, found `let` statement
+        //~| ERROR expected expression, found `let` statement
             true
         }
     }
@@ -31,6 +32,7 @@ fn main() {
     {
         if let Some(elem) = _opt && [1, 2, 3][let _ = ()] = 1 {
         //~^ ERROR expected expression, found `let` statement
+        //~| ERROR expected expression, found `let` statement
             true
         }
     }
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr
index d1ce83c7233..247fad2e992 100644
--- a/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr
@@ -3,36 +3,64 @@ error: expected expression, found `let` statement
    |
 LL |         let _ = &&let Some(x) = Some(42);
    |                   ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
 
 error: expected expression, found `let` statement
   --> $DIR/invalid-let-in-a-valid-let-context.rs:13:47
    |
 LL |         if let Some(elem) = _opt && [1, 2, 3][let _ = &&let Some(x) = Some(42)] = 1 {
    |                                               ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
 
 error: expected expression, found `let` statement
   --> $DIR/invalid-let-in-a-valid-let-context.rs:13:57
    |
 LL |         if let Some(elem) = _opt && [1, 2, 3][let _ = &&let Some(x) = Some(42)] = 1 {
    |                                                         ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/invalid-let-in-a-valid-let-context.rs:13:12
+   |
+LL |         if let Some(elem) = _opt && [1, 2, 3][let _ = &&let Some(x) = Some(42)] = 1 {
+   |            ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
 
 error: expected expression, found `let` statement
-  --> $DIR/invalid-let-in-a-valid-let-context.rs:23:23
+  --> $DIR/invalid-let-in-a-valid-let-context.rs:24:23
    |
 LL |             [1, 2, 3][let _ = ()];
    |                       ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
 
 error: expected expression, found `let` statement
-  --> $DIR/invalid-let-in-a-valid-let-context.rs:32:47
+  --> $DIR/invalid-let-in-a-valid-let-context.rs:33:47
    |
 LL |         if let Some(elem) = _opt && [1, 2, 3][let _ = ()] = 1 {
    |                                               ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+
+error: expected expression, found `let` statement
+  --> $DIR/invalid-let-in-a-valid-let-context.rs:33:12
+   |
+LL |         if let Some(elem) = _opt && [1, 2, 3][let _ = ()] = 1 {
+   |            ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
 
 error: expected expression, found `let` statement
-  --> $DIR/invalid-let-in-a-valid-let-context.rs:40:21
+  --> $DIR/invalid-let-in-a-valid-let-context.rs:42:21
    |
 LL |             let x = let y = 1;
    |                     ^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
 
-error: aborting due to 6 previous errors
+error: aborting due to 8 previous errors
 
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/fallback.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/fallback.rs
new file mode 100644
index 00000000000..da2778f6101
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/fallback.rs
@@ -0,0 +1,16 @@
+// check-pass
+
+#![feature(effects)]
+
+pub const fn owo() {}
+
+fn main() {
+    // make sure falling back ty/int vars doesn't cause const fallback to be skipped...
+    // See issue: 115791.
+    let _ = 1;
+    if false {
+        let x = panic!();
+    }
+
+    let _ = owo;
+}
diff --git a/tests/ui/sanitize/cfg.rs b/tests/ui/sanitize/cfg.rs
index c0f08a6d1e5..523de1ceaee 100644
--- a/tests/ui/sanitize/cfg.rs
+++ b/tests/ui/sanitize/cfg.rs
@@ -2,19 +2,19 @@
 // the `#[cfg(sanitize = "option")]` attribute is configured.
 
 // needs-sanitizer-support
-// needs-sanitizer-address
-// needs-sanitizer-cfi
-// needs-sanitizer-kcfi
-// needs-sanitizer-leak
-// needs-sanitizer-memory
-// needs-sanitizer-thread
 // check-pass
-// revisions: address leak memory thread
+// revisions: address cfi kcfi leak memory thread
+//[address]needs-sanitizer-address
 //[address]compile-flags: -Zsanitizer=address --cfg address
-//[cfi]compile-flags:     -Zsanitizer=cfi     --cfg cfi
+//[cfi]needs-sanitizer-cfi
+//[cfi]compile-flags:     -Zsanitizer=cfi     --cfg cfi -Clto
+//[kcfi]needs-sanitizer-kcfi
 //[kcfi]compile-flags:    -Zsanitizer=kcfi    --cfg kcfi
+//[leak]needs-sanitizer-leak
 //[leak]compile-flags:    -Zsanitizer=leak    --cfg leak
+//[memory]needs-sanitizer-memory
 //[memory]compile-flags:  -Zsanitizer=memory  --cfg memory
+//[thread]needs-sanitizer-thread
 //[thread]compile-flags:  -Zsanitizer=thread  --cfg thread
 
 #![feature(cfg_sanitize)]
diff --git a/tests/ui/span/issue-29595.stderr b/tests/ui/span/issue-29595.stderr
index 92445e40731..7d603cdbfe5 100644
--- a/tests/ui/span/issue-29595.stderr
+++ b/tests/ui/span/issue-29595.stderr
@@ -3,6 +3,12 @@ error[E0277]: the trait bound `u8: Tr` is not satisfied
    |
 LL |     let a: u8 = Tr::C;
    |                 ^^^^^ the trait `Tr` is not implemented for `u8`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-29595.rs:1:1
+   |
+LL | trait Tr {
+   | ^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/tests/ui/span/send-is-not-static-std-sync.rs b/tests/ui/span/send-is-not-static-std-sync.rs
index f8ab5243c22..9c1ee287154 100644
--- a/tests/ui/span/send-is-not-static-std-sync.rs
+++ b/tests/ui/span/send-is-not-static-std-sync.rs
@@ -46,7 +46,7 @@ fn channel() {
         tx.send(&z).unwrap();
     }
     //~^^ ERROR `z` does not live long enough
-    // (channels lack #[may_dangle], thus their dtors are implicit uses of `z`)
+    tx.use_ref(); // (channel drop glue does not use `z` => needs explicit use)
 }
 
 fn main() {}
diff --git a/tests/ui/span/send-is-not-static-std-sync.stderr b/tests/ui/span/send-is-not-static-std-sync.stderr
index eaba415adaa..46534b39168 100644
--- a/tests/ui/span/send-is-not-static-std-sync.stderr
+++ b/tests/ui/span/send-is-not-static-std-sync.stderr
@@ -75,11 +75,9 @@ LL |         tx.send(&z).unwrap();
    |                 ^^ borrowed value does not live long enough
 LL |     }
    |     - `z` dropped here while still borrowed
-...
-LL | }
-   | - borrow might be used here, when `tx` is dropped and runs the `Drop` code for type `Sender`
-   |
-   = note: values in a scope are dropped in the opposite order they are defined
+LL |
+LL |     tx.use_ref(); // (channel drop glue does not use `z` => needs explicit use)
+   |     -- borrow later used here
 
 error: aborting due to 6 previous errors
 
diff --git a/tests/ui/specialization/issue-38091.stderr b/tests/ui/specialization/issue-38091.stderr
index f2210a40719..4d840482b46 100644
--- a/tests/ui/specialization/issue-38091.stderr
+++ b/tests/ui/specialization/issue-38091.stderr
@@ -14,6 +14,11 @@ error[E0277]: the trait bound `(): Valid` is not satisfied
 LL |     default type Ty = ();
    |                       ^^ the trait `Valid` is not implemented for `()`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-38091.rs:20:1
+   |
+LL | trait Valid {}
+   | ^^^^^^^^^^^
 note: required by a bound in `Iterate::Ty`
   --> $DIR/issue-38091.rs:5:14
    |
diff --git a/tests/ui/suggestions/issue-89333.stderr b/tests/ui/suggestions/issue-89333.stderr
index f73f1147d5d..4739f11ddad 100644
--- a/tests/ui/suggestions/issue-89333.stderr
+++ b/tests/ui/suggestions/issue-89333.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `for<'a> &'a _: Trait` is not satisfied
 LL |     test(&|| 0);
    |     ^^^^ the trait `for<'a> Trait` is not implemented for `&'a _`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-89333.rs:9:1
+   |
+LL | trait Trait {}
+   | ^^^^^^^^^^^
 note: required by a bound in `test`
   --> $DIR/issue-89333.rs:11:55
    |
diff --git a/tests/ui/thir-print/thir-flat-const-variant.stdout b/tests/ui/thir-print/thir-flat-const-variant.stdout
index 7bddc925996..af7f2b67152 100644
--- a/tests/ui/thir-print/thir-flat-const-variant.stdout
+++ b/tests/ui/thir-print/thir-flat-const-variant.stdout
@@ -1,7 +1,11 @@
 DefId(0:8 ~ thir_flat_const_variant[1f54]::{impl#0}::BAR1):
 Thir {
     body_type: Const(
-        Foo,
+        Adt(
+            Foo,
+            [
+            ],
+        ),
     ),
     arms: [],
     blocks: [],
@@ -46,7 +50,11 @@ Thir {
                     base: None,
                 },
             ),
-            ty: Foo,
+            ty: Adt(
+                Foo,
+                [
+                ],
+            ),
             temp_lifetime: Some(
                 Node(3),
             ),
@@ -60,7 +68,11 @@ Thir {
                 ),
                 value: e2,
             },
-            ty: Foo,
+            ty: Adt(
+                Foo,
+                [
+                ],
+            ),
             temp_lifetime: Some(
                 Node(3),
             ),
@@ -72,7 +84,11 @@ Thir {
                 lint_level: Inherited,
                 value: e3,
             },
-            ty: Foo,
+            ty: Adt(
+                Foo,
+                [
+                ],
+            ),
             temp_lifetime: Some(
                 Node(3),
             ),
@@ -86,7 +102,11 @@ Thir {
 DefId(0:9 ~ thir_flat_const_variant[1f54]::{impl#0}::BAR2):
 Thir {
     body_type: Const(
-        Foo,
+        Adt(
+            Foo,
+            [
+            ],
+        ),
     ),
     arms: [],
     blocks: [],
@@ -131,7 +151,11 @@ Thir {
                     base: None,
                 },
             ),
-            ty: Foo,
+            ty: Adt(
+                Foo,
+                [
+                ],
+            ),
             temp_lifetime: Some(
                 Node(3),
             ),
@@ -145,7 +169,11 @@ Thir {
                 ),
                 value: e2,
             },
-            ty: Foo,
+            ty: Adt(
+                Foo,
+                [
+                ],
+            ),
             temp_lifetime: Some(
                 Node(3),
             ),
@@ -157,7 +185,11 @@ Thir {
                 lint_level: Inherited,
                 value: e3,
             },
-            ty: Foo,
+            ty: Adt(
+                Foo,
+                [
+                ],
+            ),
             temp_lifetime: Some(
                 Node(3),
             ),
@@ -171,7 +203,11 @@ Thir {
 DefId(0:10 ~ thir_flat_const_variant[1f54]::{impl#0}::BAR3):
 Thir {
     body_type: Const(
-        Foo,
+        Adt(
+            Foo,
+            [
+            ],
+        ),
     ),
     arms: [],
     blocks: [],
@@ -216,7 +252,11 @@ Thir {
                     base: None,
                 },
             ),
-            ty: Foo,
+            ty: Adt(
+                Foo,
+                [
+                ],
+            ),
             temp_lifetime: Some(
                 Node(3),
             ),
@@ -230,7 +270,11 @@ Thir {
                 ),
                 value: e2,
             },
-            ty: Foo,
+            ty: Adt(
+                Foo,
+                [
+                ],
+            ),
             temp_lifetime: Some(
                 Node(3),
             ),
@@ -242,7 +286,11 @@ Thir {
                 lint_level: Inherited,
                 value: e3,
             },
-            ty: Foo,
+            ty: Adt(
+                Foo,
+                [
+                ],
+            ),
             temp_lifetime: Some(
                 Node(3),
             ),
@@ -256,7 +304,11 @@ Thir {
 DefId(0:11 ~ thir_flat_const_variant[1f54]::{impl#0}::BAR4):
 Thir {
     body_type: Const(
-        Foo,
+        Adt(
+            Foo,
+            [
+            ],
+        ),
     ),
     arms: [],
     blocks: [],
@@ -301,7 +353,11 @@ Thir {
                     base: None,
                 },
             ),
-            ty: Foo,
+            ty: Adt(
+                Foo,
+                [
+                ],
+            ),
             temp_lifetime: Some(
                 Node(3),
             ),
@@ -315,7 +371,11 @@ Thir {
                 ),
                 value: e2,
             },
-            ty: Foo,
+            ty: Adt(
+                Foo,
+                [
+                ],
+            ),
             temp_lifetime: Some(
                 Node(3),
             ),
@@ -327,7 +387,11 @@ Thir {
                 lint_level: Inherited,
                 value: e3,
             },
-            ty: Foo,
+            ty: Adt(
+                Foo,
+                [
+                ],
+            ),
             temp_lifetime: Some(
                 Node(3),
             ),
diff --git a/tests/ui/thir-print/thir-tree-match.stdout b/tests/ui/thir-print/thir-tree-match.stdout
index 3fc130f0176..0e21b98307b 100644
--- a/tests/ui/thir-print/thir-tree-match.stdout
+++ b/tests/ui/thir-print/thir-tree-match.stdout
@@ -1,13 +1,13 @@
 DefId(0:16 ~ thir_tree_match[fcf8]::has_match):
 params: [
     Param {
-        ty: Foo
+        ty: Adt(Foo, [])
         ty_span: Some($DIR/thir-tree-match.rs:15:19: 15:22 (#0))
         self_kind: None
         hir_id: Some(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).1))
         param: Some( 
             Pat: {
-                ty: Foo
+                ty: Adt(Foo, [])
                 span: $DIR/thir-tree-match.rs:15:14: 15:17 (#0)
                 kind: PatKind {
                     Binding {
@@ -15,7 +15,7 @@ params: [
                         name: "foo"
                         mode: ByValue
                         var: LocalVarId(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).2))
-                        ty: Foo
+                        ty: Adt(Foo, [])
                         is_primary: true
                         subpattern: None
                     }
@@ -73,7 +73,7 @@ body:
                                                                             Match {
                                                                                 scrutinee:
                                                                                     Expr {
-                                                                                        ty: Foo
+                                                                                        ty: Adt(Foo, [])
                                                                                         temp_lifetime: Some(Node(26))
                                                                                         span: $DIR/thir-tree-match.rs:16:11: 16:14 (#0)
                                                                                         kind: 
@@ -82,7 +82,7 @@ body:
                                                                                                 lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).4))
                                                                                                 value:
                                                                                                     Expr {
-                                                                                                        ty: Foo
+                                                                                                        ty: Adt(Foo, [])
                                                                                                         temp_lifetime: Some(Node(26))
                                                                                                         span: $DIR/thir-tree-match.rs:16:11: 16:14 (#0)
                                                                                                         kind: 
@@ -96,7 +96,7 @@ body:
                                                                                     Arm {
                                                                                         pattern: 
                                                                                             Pat: {
-                                                                                                ty: Foo
+                                                                                                ty: Adt(Foo, [])
                                                                                                 span: $DIR/thir-tree-match.rs:17:9: 17:32 (#0)
                                                                                                 kind: PatKind {
                                                                                                     Variant {
@@ -110,7 +110,7 @@ body:
                                                                                                         variant_index: 0
                                                                                                         subpatterns: [
                                                                                                             Pat: {
-                                                                                                                ty: Bar
+                                                                                                                ty: Adt(Bar, [])
                                                                                                                 span: $DIR/thir-tree-match.rs:17:21: 17:31 (#0)
                                                                                                                 kind: PatKind {
                                                                                                                     Variant {
@@ -169,7 +169,7 @@ body:
                                                                                     Arm {
                                                                                         pattern: 
                                                                                             Pat: {
-                                                                                                ty: Foo
+                                                                                                ty: Adt(Foo, [])
                                                                                                 span: $DIR/thir-tree-match.rs:18:9: 18:23 (#0)
                                                                                                 kind: PatKind {
                                                                                                     Variant {
@@ -183,7 +183,7 @@ body:
                                                                                                         variant_index: 0
                                                                                                         subpatterns: [
                                                                                                             Pat: {
-                                                                                                                ty: Bar
+                                                                                                                ty: Adt(Bar, [])
                                                                                                                 span: $DIR/thir-tree-match.rs:18:21: 18:22 (#0)
                                                                                                                 kind: PatKind {
                                                                                                                     Wild
@@ -232,7 +232,7 @@ body:
                                                                                     Arm {
                                                                                         pattern: 
                                                                                             Pat: {
-                                                                                                ty: Foo
+                                                                                                ty: Adt(Foo, [])
                                                                                                 span: $DIR/thir-tree-match.rs:19:9: 19:20 (#0)
                                                                                                 kind: PatKind {
                                                                                                     Variant {
diff --git a/tests/ui/trait-bounds/issue-82038.rs b/tests/ui/trait-bounds/issue-82038.rs
new file mode 100644
index 00000000000..11de714faf0
--- /dev/null
+++ b/tests/ui/trait-bounds/issue-82038.rs
@@ -0,0 +1,9 @@
+// Failed bound `bool: Foo` must not point at the `Self: Clone` line
+
+trait Foo {
+    fn my_method() where Self: Clone;
+}
+
+fn main() {
+    <bool as Foo>::my_method(); //~ERROR [E0277]
+}
diff --git a/tests/ui/trait-bounds/issue-82038.stderr b/tests/ui/trait-bounds/issue-82038.stderr
new file mode 100644
index 00000000000..30bb4a0a850
--- /dev/null
+++ b/tests/ui/trait-bounds/issue-82038.stderr
@@ -0,0 +1,15 @@
+error[E0277]: the trait bound `bool: Foo` is not satisfied
+  --> $DIR/issue-82038.rs:8:6
+   |
+LL |     <bool as Foo>::my_method();
+   |      ^^^^ the trait `Foo` is not implemented for `bool`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-82038.rs:3:1
+   |
+LL | trait Foo {
+   | ^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/bound/on-structs-and-enums-in-fns.stderr b/tests/ui/traits/bound/on-structs-and-enums-in-fns.stderr
index 61237a63e32..530264b344a 100644
--- a/tests/ui/traits/bound/on-structs-and-enums-in-fns.stderr
+++ b/tests/ui/traits/bound/on-structs-and-enums-in-fns.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `u32: Trait` is not satisfied
 LL | fn explode(x: Foo<u32>) {}
    |               ^^^^^^^^ the trait `Trait` is not implemented for `u32`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/on-structs-and-enums-in-fns.rs:1:1
+   |
+LL | trait Trait {}
+   | ^^^^^^^^^^^
 note: required by a bound in `Foo`
   --> $DIR/on-structs-and-enums-in-fns.rs:3:14
    |
@@ -16,6 +21,11 @@ error[E0277]: the trait bound `f32: Trait` is not satisfied
 LL | fn kaboom(y: Bar<f32>) {}
    |              ^^^^^^^^ the trait `Trait` is not implemented for `f32`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/on-structs-and-enums-in-fns.rs:1:1
+   |
+LL | trait Trait {}
+   | ^^^^^^^^^^^
 note: required by a bound in `Bar`
   --> $DIR/on-structs-and-enums-in-fns.rs:7:12
    |
diff --git a/tests/ui/traits/bound/on-structs-and-enums-in-impls.stderr b/tests/ui/traits/bound/on-structs-and-enums-in-impls.stderr
index 8a43742260b..372bbabbd86 100644
--- a/tests/ui/traits/bound/on-structs-and-enums-in-impls.stderr
+++ b/tests/ui/traits/bound/on-structs-and-enums-in-impls.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `u16: Trait` is not satisfied
 LL | impl PolyTrait<Foo<u16>> for Struct {
    |      ^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `u16`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/on-structs-and-enums-in-impls.rs:1:1
+   |
+LL | trait Trait {}
+   | ^^^^^^^^^^^
 note: required by a bound in `Foo`
   --> $DIR/on-structs-and-enums-in-impls.rs:3:14
    |
diff --git a/tests/ui/traits/bound/on-structs-and-enums-locals.stderr b/tests/ui/traits/bound/on-structs-and-enums-locals.stderr
index 20bbe69c059..01cf76c62d5 100644
--- a/tests/ui/traits/bound/on-structs-and-enums-locals.stderr
+++ b/tests/ui/traits/bound/on-structs-and-enums-locals.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `usize: Trait` is not satisfied
 LL |     let baz: Foo<usize> = loop { };
    |              ^^^^^^^^^^ the trait `Trait` is not implemented for `usize`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/on-structs-and-enums-locals.rs:1:1
+   |
+LL | trait Trait {
+   | ^^^^^^^^^^^
 note: required by a bound in `Foo`
   --> $DIR/on-structs-and-enums-locals.rs:5:14
    |
@@ -16,6 +21,11 @@ error[E0277]: the trait bound `{integer}: Trait` is not satisfied
 LL |         x: 3
    |            ^ the trait `Trait` is not implemented for `{integer}`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/on-structs-and-enums-locals.rs:1:1
+   |
+LL | trait Trait {
+   | ^^^^^^^^^^^
 note: required by a bound in `Foo`
   --> $DIR/on-structs-and-enums-locals.rs:5:14
    |
diff --git a/tests/ui/traits/bound/on-structs-and-enums-static.stderr b/tests/ui/traits/bound/on-structs-and-enums-static.stderr
index fda734e8571..fa14aff684d 100644
--- a/tests/ui/traits/bound/on-structs-and-enums-static.stderr
+++ b/tests/ui/traits/bound/on-structs-and-enums-static.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `usize: Trait` is not satisfied
 LL | static X: Foo<usize> = Foo {
    |           ^^^^^^^^^^ the trait `Trait` is not implemented for `usize`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/on-structs-and-enums-static.rs:1:1
+   |
+LL | trait Trait {
+   | ^^^^^^^^^^^
 note: required by a bound in `Foo`
   --> $DIR/on-structs-and-enums-static.rs:5:14
    |
diff --git a/tests/ui/traits/bound/on-structs-and-enums.stderr b/tests/ui/traits/bound/on-structs-and-enums.stderr
index fe05b86344b..606f764852f 100644
--- a/tests/ui/traits/bound/on-structs-and-enums.stderr
+++ b/tests/ui/traits/bound/on-structs-and-enums.stderr
@@ -20,6 +20,11 @@ error[E0277]: the trait bound `isize: Trait` is not satisfied
 LL |     a: Foo<isize>,
    |        ^^^^^^^^^^ the trait `Trait` is not implemented for `isize`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/on-structs-and-enums.rs:1:1
+   |
+LL | trait Trait {}
+   | ^^^^^^^^^^^
 note: required by a bound in `Foo`
   --> $DIR/on-structs-and-enums.rs:3:14
    |
@@ -32,6 +37,11 @@ error[E0277]: the trait bound `usize: Trait` is not satisfied
 LL |     Quux(Bar<usize>),
    |          ^^^^^^^^^^ the trait `Trait` is not implemented for `usize`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/on-structs-and-enums.rs:1:1
+   |
+LL | trait Trait {}
+   | ^^^^^^^^^^^
 note: required by a bound in `Bar`
   --> $DIR/on-structs-and-enums.rs:7:12
    |
@@ -76,6 +86,11 @@ error[E0277]: the trait bound `i32: Trait` is not satisfied
 LL |     Foo<i32>,
    |     ^^^^^^^^ the trait `Trait` is not implemented for `i32`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/on-structs-and-enums.rs:1:1
+   |
+LL | trait Trait {}
+   | ^^^^^^^^^^^
 note: required by a bound in `Foo`
   --> $DIR/on-structs-and-enums.rs:3:14
    |
@@ -88,6 +103,11 @@ error[E0277]: the trait bound `u8: Trait` is not satisfied
 LL |     DictionaryLike { field: Bar<u8> },
    |                             ^^^^^^^ the trait `Trait` is not implemented for `u8`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/on-structs-and-enums.rs:1:1
+   |
+LL | trait Trait {}
+   | ^^^^^^^^^^^
 note: required by a bound in `Bar`
   --> $DIR/on-structs-and-enums.rs:7:12
    |
diff --git a/tests/ui/traits/deny-builtin-object-impl.current.stderr b/tests/ui/traits/deny-builtin-object-impl.current.stderr
index 5c1987426f7..8ca3d3a057f 100644
--- a/tests/ui/traits/deny-builtin-object-impl.current.stderr
+++ b/tests/ui/traits/deny-builtin-object-impl.current.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `dyn NotObject: NotObject` is not satisfied
 LL |     test_not_object::<dyn NotObject>();
    |                       ^^^^^^^^^^^^^ the trait `NotObject` is not implemented for `dyn NotObject`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/deny-builtin-object-impl.rs:10:1
+   |
+LL | trait NotObject {}
+   | ^^^^^^^^^^^^^^^
 note: required by a bound in `test_not_object`
   --> $DIR/deny-builtin-object-impl.rs:14:23
    |
diff --git a/tests/ui/traits/deny-builtin-object-impl.next.stderr b/tests/ui/traits/deny-builtin-object-impl.next.stderr
index 5c1987426f7..8ca3d3a057f 100644
--- a/tests/ui/traits/deny-builtin-object-impl.next.stderr
+++ b/tests/ui/traits/deny-builtin-object-impl.next.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `dyn NotObject: NotObject` is not satisfied
 LL |     test_not_object::<dyn NotObject>();
    |                       ^^^^^^^^^^^^^ the trait `NotObject` is not implemented for `dyn NotObject`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/deny-builtin-object-impl.rs:10:1
+   |
+LL | trait NotObject {}
+   | ^^^^^^^^^^^^^^^
 note: required by a bound in `test_not_object`
   --> $DIR/deny-builtin-object-impl.rs:14:23
    |
diff --git a/tests/ui/traits/dont-autoderef-ty-with-escaping-var.stderr b/tests/ui/traits/dont-autoderef-ty-with-escaping-var.stderr
index 263c59ee327..a5d0e6ab095 100644
--- a/tests/ui/traits/dont-autoderef-ty-with-escaping-var.stderr
+++ b/tests/ui/traits/dont-autoderef-ty-with-escaping-var.stderr
@@ -10,6 +10,11 @@ error[E0277]: the trait bound `for<'a> &'a mut Vec<&'a u32>: Foo<'static, i32>`
 LL |     <i32 as RefFoo<i32>>::ref_foo(unknown);
    |      ^^^ the trait `for<'a> Foo<'static, i32>` is not implemented for `&'a mut Vec<&'a u32>`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/dont-autoderef-ty-with-escaping-var.rs:3:1
+   |
+LL | trait Foo<'x, T> {}
+   | ^^^^^^^^^^^^^^^^
 note: required for `i32` to implement `RefFoo<i32>`
   --> $DIR/dont-autoderef-ty-with-escaping-var.rs:9:9
    |
diff --git a/tests/ui/traits/impl-bounds-checking.stderr b/tests/ui/traits/impl-bounds-checking.stderr
index 1f969efe114..bfa8213abe7 100644
--- a/tests/ui/traits/impl-bounds-checking.stderr
+++ b/tests/ui/traits/impl-bounds-checking.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `isize: Clone2` is not satisfied
 LL | impl Getter<isize> for isize {
    |                        ^^^^^ the trait `Clone2` is not implemented for `isize`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/impl-bounds-checking.rs:1:1
+   |
+LL | pub trait Clone2 {
+   | ^^^^^^^^^^^^^^^^
 note: required by a bound in `Getter`
   --> $DIR/impl-bounds-checking.rs:6:17
    |
diff --git a/tests/ui/traits/new-solver/canonicalize-effect-var.rs b/tests/ui/traits/new-solver/canonicalize-effect-var.rs
new file mode 100644
index 00000000000..35b69ed1a6b
--- /dev/null
+++ b/tests/ui/traits/new-solver/canonicalize-effect-var.rs
@@ -0,0 +1,22 @@
+// compile-flags: -Ztrait-solver=next
+// check-pass
+
+#![feature(effects)]
+#![feature(const_trait_impl)]
+
+#[const_trait]
+trait Foo {
+    fn foo();
+}
+
+trait Bar {}
+
+impl const Foo for i32 {
+    fn foo() {}
+}
+
+impl<T> const Foo for T where T: Bar {
+    fn foo() {}
+}
+
+fn main() {}
diff --git a/tests/ui/traits/new-solver/projection-discr-kind.stderr b/tests/ui/traits/new-solver/projection-discr-kind.stderr
index 03e28f993e2..e14953f19ff 100644
--- a/tests/ui/traits/new-solver/projection-discr-kind.stderr
+++ b/tests/ui/traits/new-solver/projection-discr-kind.stderr
@@ -6,6 +6,11 @@ LL |     needs_bar(std::mem::discriminant(&x));
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/projection-discr-kind.rs:10:1
+   |
+LL | trait Bar {}
+   | ^^^^^^^^^
 note: required by a bound in `needs_bar`
   --> $DIR/projection-discr-kind.rs:11:22
    |
diff --git a/tests/ui/traits/non_lifetime_binders/fail.stderr b/tests/ui/traits/non_lifetime_binders/fail.stderr
index 7bd02550fb3..240bcef7df5 100644
--- a/tests/ui/traits/non_lifetime_binders/fail.stderr
+++ b/tests/ui/traits/non_lifetime_binders/fail.stderr
@@ -13,6 +13,11 @@ error[E0277]: the trait bound `T: Trait` is not satisfied
 LL |     fail();
    |     ^^^^ the trait `Trait` is not implemented for `T`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/fail.rs:6:1
+   |
+LL | trait Trait {}
+   | ^^^^^^^^^^^
 note: required by a bound in `fail`
   --> $DIR/fail.rs:10:15
    |
diff --git a/tests/ui/traits/object-does-not-impl-trait.stderr b/tests/ui/traits/object-does-not-impl-trait.stderr
index f1dd508a467..81d67255a0b 100644
--- a/tests/ui/traits/object-does-not-impl-trait.stderr
+++ b/tests/ui/traits/object-does-not-impl-trait.stderr
@@ -6,6 +6,11 @@ LL | fn take_object(f: Box<dyn Foo>) { take_foo(f); }
    |                                   |
    |                                   required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/object-does-not-impl-trait.rs:4:1
+   |
+LL | trait Foo {}
+   | ^^^^^^^^^
 note: required by a bound in `take_foo`
   --> $DIR/object-does-not-impl-trait.rs:5:15
    |
diff --git a/tests/ui/traits/object/enforce-supertrait-projection.rs b/tests/ui/traits/object/enforce-supertrait-projection.rs
index 2c9b41eea2a..0ea944ec2df 100644
--- a/tests/ui/traits/object/enforce-supertrait-projection.rs
+++ b/tests/ui/traits/object/enforce-supertrait-projection.rs
@@ -7,7 +7,7 @@ trait Trait: SuperTrait<A = <Self as SuperTrait>::B> {}
 
 fn transmute<A, B>(x: A) -> B {
     foo::<A, B, dyn Trait<A = A, B = B>>(x)
-    //~^ ERROR type mismatch resolving `<dyn Trait<B = B, A = A> as SuperTrait>::A == B`
+    //~^ ERROR type mismatch resolving `<dyn Trait<A = A, B = B> as SuperTrait>::A == B`
 }
 
 fn foo<A, B, T: ?Sized>(x: T::A) -> B
diff --git a/tests/ui/traits/object/enforce-supertrait-projection.stderr b/tests/ui/traits/object/enforce-supertrait-projection.stderr
index 848b4e69a4b..2fb94d34896 100644
--- a/tests/ui/traits/object/enforce-supertrait-projection.stderr
+++ b/tests/ui/traits/object/enforce-supertrait-projection.stderr
@@ -1,4 +1,4 @@
-error[E0271]: type mismatch resolving `<dyn Trait<B = B, A = A> as SuperTrait>::A == B`
+error[E0271]: type mismatch resolving `<dyn Trait<A = A, B = B> as SuperTrait>::A == B`
   --> $DIR/enforce-supertrait-projection.rs:9:17
    |
 LL | fn transmute<A, B>(x: A) -> B {
diff --git a/tests/ui/traits/suggest-dereferences/dont-suggest-unsize-deref.rs b/tests/ui/traits/suggest-dereferences/dont-suggest-unsize-deref.rs
new file mode 100644
index 00000000000..c6f9e345618
--- /dev/null
+++ b/tests/ui/traits/suggest-dereferences/dont-suggest-unsize-deref.rs
@@ -0,0 +1,15 @@
+fn use_iterator<I>(itr: I)
+where
+    I: IntoIterator<Item = i32>,
+{
+}
+
+fn pass_iterator<I>(i: &dyn IntoIterator<Item = i32, IntoIter = I>)
+where
+    I: Iterator<Item = i32>,
+{
+    use_iterator(i);
+    //~^ ERROR `&dyn IntoIterator<IntoIter = I, Item = i32>` is not an iterator
+}
+
+fn main() {}
diff --git a/tests/ui/traits/suggest-dereferences/dont-suggest-unsize-deref.stderr b/tests/ui/traits/suggest-dereferences/dont-suggest-unsize-deref.stderr
new file mode 100644
index 00000000000..bd0e7ca2c02
--- /dev/null
+++ b/tests/ui/traits/suggest-dereferences/dont-suggest-unsize-deref.stderr
@@ -0,0 +1,22 @@
+error[E0277]: `&dyn IntoIterator<IntoIter = I, Item = i32>` is not an iterator
+  --> $DIR/dont-suggest-unsize-deref.rs:11:18
+   |
+LL |     use_iterator(i);
+   |     ------------ ^ `&dyn IntoIterator<IntoIter = I, Item = i32>` is not an iterator
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Iterator` is not implemented for `&dyn IntoIterator<IntoIter = I, Item = i32>`
+   = note: required for `&dyn IntoIterator<IntoIter = I, Item = i32>` to implement `IntoIterator`
+note: required by a bound in `use_iterator`
+  --> $DIR/dont-suggest-unsize-deref.rs:3:8
+   |
+LL | fn use_iterator<I>(itr: I)
+   |    ------------ required by a bound in this function
+LL | where
+LL |     I: IntoIterator<Item = i32>,
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `use_iterator`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/suggest-deferences/issue-39029.fixed b/tests/ui/traits/suggest-dereferences/issue-39029.fixed
index a1abf668b8b..a1abf668b8b 100644
--- a/tests/ui/traits/suggest-deferences/issue-39029.fixed
+++ b/tests/ui/traits/suggest-dereferences/issue-39029.fixed
diff --git a/tests/ui/traits/suggest-deferences/issue-39029.rs b/tests/ui/traits/suggest-dereferences/issue-39029.rs
index 90d097105ed..90d097105ed 100644
--- a/tests/ui/traits/suggest-deferences/issue-39029.rs
+++ b/tests/ui/traits/suggest-dereferences/issue-39029.rs
diff --git a/tests/ui/traits/suggest-deferences/issue-39029.stderr b/tests/ui/traits/suggest-dereferences/issue-39029.stderr
index 49105de3d69..49105de3d69 100644
--- a/tests/ui/traits/suggest-deferences/issue-39029.stderr
+++ b/tests/ui/traits/suggest-dereferences/issue-39029.stderr
diff --git a/tests/ui/traits/suggest-deferences/issue-62530.fixed b/tests/ui/traits/suggest-dereferences/issue-62530.fixed
index 406caaa007f..406caaa007f 100644
--- a/tests/ui/traits/suggest-deferences/issue-62530.fixed
+++ b/tests/ui/traits/suggest-dereferences/issue-62530.fixed
diff --git a/tests/ui/traits/suggest-deferences/issue-62530.rs b/tests/ui/traits/suggest-dereferences/issue-62530.rs
index 53846be7306..53846be7306 100644
--- a/tests/ui/traits/suggest-deferences/issue-62530.rs
+++ b/tests/ui/traits/suggest-dereferences/issue-62530.rs
diff --git a/tests/ui/traits/suggest-deferences/issue-62530.stderr b/tests/ui/traits/suggest-dereferences/issue-62530.stderr
index e47ae0b65af..e47ae0b65af 100644
--- a/tests/ui/traits/suggest-deferences/issue-62530.stderr
+++ b/tests/ui/traits/suggest-dereferences/issue-62530.stderr
diff --git a/tests/ui/traits/suggest-deferences/multiple-0.fixed b/tests/ui/traits/suggest-dereferences/multiple-0.fixed
index b7160b75c60..b7160b75c60 100644
--- a/tests/ui/traits/suggest-deferences/multiple-0.fixed
+++ b/tests/ui/traits/suggest-dereferences/multiple-0.fixed
diff --git a/tests/ui/traits/suggest-deferences/multiple-0.rs b/tests/ui/traits/suggest-dereferences/multiple-0.rs
index 9ac55177ffa..9ac55177ffa 100644
--- a/tests/ui/traits/suggest-deferences/multiple-0.rs
+++ b/tests/ui/traits/suggest-dereferences/multiple-0.rs
diff --git a/tests/ui/traits/suggest-deferences/multiple-0.stderr b/tests/ui/traits/suggest-dereferences/multiple-0.stderr
index 6a4d4b8d521..6a4d4b8d521 100644
--- a/tests/ui/traits/suggest-deferences/multiple-0.stderr
+++ b/tests/ui/traits/suggest-dereferences/multiple-0.stderr
diff --git a/tests/ui/traits/suggest-deferences/multiple-1.rs b/tests/ui/traits/suggest-dereferences/multiple-1.rs
index 91c6c7924a4..91c6c7924a4 100644
--- a/tests/ui/traits/suggest-deferences/multiple-1.rs
+++ b/tests/ui/traits/suggest-dereferences/multiple-1.rs
diff --git a/tests/ui/traits/suggest-deferences/multiple-1.stderr b/tests/ui/traits/suggest-dereferences/multiple-1.stderr
index 6e12321c233..6e12321c233 100644
--- a/tests/ui/traits/suggest-deferences/multiple-1.stderr
+++ b/tests/ui/traits/suggest-dereferences/multiple-1.stderr
diff --git a/tests/ui/traits/suggest-deferences/root-obligation.fixed b/tests/ui/traits/suggest-dereferences/root-obligation.fixed
index 7a8433f9057..7a8433f9057 100644
--- a/tests/ui/traits/suggest-deferences/root-obligation.fixed
+++ b/tests/ui/traits/suggest-dereferences/root-obligation.fixed
diff --git a/tests/ui/traits/suggest-deferences/root-obligation.rs b/tests/ui/traits/suggest-dereferences/root-obligation.rs
index 51bac2107e3..51bac2107e3 100644
--- a/tests/ui/traits/suggest-deferences/root-obligation.rs
+++ b/tests/ui/traits/suggest-dereferences/root-obligation.rs
diff --git a/tests/ui/traits/suggest-deferences/root-obligation.stderr b/tests/ui/traits/suggest-dereferences/root-obligation.stderr
index 1363fb8c47a..1363fb8c47a 100644
--- a/tests/ui/traits/suggest-deferences/root-obligation.stderr
+++ b/tests/ui/traits/suggest-dereferences/root-obligation.stderr
diff --git a/tests/ui/traits/suggest-deferences/suggest-dereferencing-receiver-argument.fixed b/tests/ui/traits/suggest-dereferences/suggest-dereferencing-receiver-argument.fixed
index ea3d1bf853a..ea3d1bf853a 100644
--- a/tests/ui/traits/suggest-deferences/suggest-dereferencing-receiver-argument.fixed
+++ b/tests/ui/traits/suggest-dereferences/suggest-dereferencing-receiver-argument.fixed
diff --git a/tests/ui/traits/suggest-deferences/suggest-dereferencing-receiver-argument.rs b/tests/ui/traits/suggest-dereferences/suggest-dereferencing-receiver-argument.rs
index 9eda68027b2..9eda68027b2 100644
--- a/tests/ui/traits/suggest-deferences/suggest-dereferencing-receiver-argument.rs
+++ b/tests/ui/traits/suggest-dereferences/suggest-dereferencing-receiver-argument.rs
diff --git a/tests/ui/traits/suggest-deferences/suggest-dereferencing-receiver-argument.stderr b/tests/ui/traits/suggest-dereferences/suggest-dereferencing-receiver-argument.stderr
index ede31a2c7bc..ede31a2c7bc 100644
--- a/tests/ui/traits/suggest-deferences/suggest-dereferencing-receiver-argument.stderr
+++ b/tests/ui/traits/suggest-dereferences/suggest-dereferencing-receiver-argument.stderr
diff --git a/tests/ui/traits/vtable-res-trait-param.stderr b/tests/ui/traits/vtable-res-trait-param.stderr
index 2b3e3de9b1a..4cfceefb24c 100644
--- a/tests/ui/traits/vtable-res-trait-param.stderr
+++ b/tests/ui/traits/vtable-res-trait-param.stderr
@@ -6,6 +6,11 @@ LL |     b.gimme_an_a(y)
    |       |
    |       required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/vtable-res-trait-param.rs:1:1
+   |
+LL | trait TraitA {
+   | ^^^^^^^^^^^^
 note: required by a bound in `TraitB::gimme_an_a`
   --> $DIR/vtable-res-trait-param.rs:6:21
    |
diff --git a/tests/ui/trivial-bounds/trivial-bounds-leak.stderr b/tests/ui/trivial-bounds/trivial-bounds-leak.stderr
index 02c5d5d2484..be472a50769 100644
--- a/tests/ui/trivial-bounds/trivial-bounds-leak.stderr
+++ b/tests/ui/trivial-bounds/trivial-bounds-leak.stderr
@@ -27,6 +27,12 @@ LL |     Foo::test(&4i32);
    |     --------- ^^^^^ the trait `Foo` is not implemented for `i32`
    |     |
    |     required by a bound introduced by this call
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/trivial-bounds-leak.rs:4:1
+   |
+LL | pub trait Foo {
+   | ^^^^^^^^^^^^^
 
 error[E0277]: the trait bound `i32: Foo` is not satisfied
   --> $DIR/trivial-bounds-leak.rs:26:22
@@ -36,6 +42,11 @@ LL |     generic_function(5i32);
    |     |
    |     required by a bound introduced by this call
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/trivial-bounds-leak.rs:4:1
+   |
+LL | pub trait Foo {
+   | ^^^^^^^^^^^^^
 note: required by a bound in `generic_function`
   --> $DIR/trivial-bounds-leak.rs:29:24
    |
diff --git a/tests/ui/type-alias-impl-trait/nested-tait-hrtb.rs b/tests/ui/type-alias-impl-trait/nested-tait-hrtb.rs
index 4a9631a7208..ba705d6f85a 100644
--- a/tests/ui/type-alias-impl-trait/nested-tait-hrtb.rs
+++ b/tests/ui/type-alias-impl-trait/nested-tait-hrtb.rs
@@ -8,7 +8,7 @@ fn without_lt() -> impl for<'a> Trait<'a, Assoc = WithoutLt> {}
 //~^ ERROR captures lifetime that does not appear in bounds
 
 type WithLt<'a> = impl Sized + 'a;
-//~^ ERROR concrete type differs from previous defining opaque type use
+
 fn with_lt() -> impl for<'a> Trait<'a, Assoc = WithLt<'a>> {}
 //~^ ERROR expected generic lifetime parameter, found `'a`
 
diff --git a/tests/ui/type-alias-impl-trait/nested-tait-hrtb.stderr b/tests/ui/type-alias-impl-trait/nested-tait-hrtb.stderr
index 9a783a6d92a..f208730552d 100644
--- a/tests/ui/type-alias-impl-trait/nested-tait-hrtb.stderr
+++ b/tests/ui/type-alias-impl-trait/nested-tait-hrtb.stderr
@@ -17,19 +17,7 @@ LL |
 LL | fn with_lt() -> impl for<'a> Trait<'a, Assoc = WithLt<'a>> {}
    |                                                            ^^
 
-error: concrete type differs from previous defining opaque type use
-  --> $DIR/nested-tait-hrtb.rs:10:19
-   |
-LL | type WithLt<'a> = impl Sized + 'a;
-   |                   ^^^^^^^^^^^^^^^ expected `&'a str`, got `{type error}`
-   |
-note: previous use here
-  --> $DIR/nested-tait-hrtb.rs:12:17
-   |
-LL | fn with_lt() -> impl for<'a> Trait<'a, Assoc = WithLt<'a>> {}
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
 Some errors have detailed explanations: E0700, E0792.
 For more information about an error, try `rustc --explain E0700`.
diff --git a/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.rs b/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.rs
new file mode 100644
index 00000000000..eefe333da45
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.rs
@@ -0,0 +1,25 @@
+//! This tries to prove the APIT's bounds in a canonical query,
+//! which doesn't know anything about the defining scope of either
+//! opaque type and thus makes a random choice as to which opaque type
+//! becomes the hidden type of the other. When we leave the canonical
+//! query, we attempt to actually check the defining anchor, but now we
+//! have a situation where the RPIT gets constrained outside its anchor.
+
+// revisions: current next
+//[next] compile-flags: -Ztrait-solver=next
+// check-pass
+
+#![feature(type_alias_impl_trait)]
+
+type Opaque = impl Sized;
+
+fn get_rpit() -> impl Clone {}
+
+fn query(_: impl FnOnce() -> Opaque) {}
+
+fn test() -> Opaque {
+    query(get_rpit);
+    get_rpit()
+}
+
+fn main() {}
diff --git a/tests/ui/type/type-arg-out-of-scope.rs b/tests/ui/type/type-arg-out-of-scope.rs
index 02aad007707..c36f9904e5d 100644
--- a/tests/ui/type/type-arg-out-of-scope.rs
+++ b/tests/ui/type/type-arg-out-of-scope.rs
@@ -1,4 +1,4 @@
-// error-pattern:can't use generic parameters from outer function
+// error-pattern:can't use generic parameters from outer item
 fn foo<T>(x: T) {
     fn bar(f: Box<dyn FnMut(T) -> T>) { }
 }
diff --git a/tests/ui/type/type-arg-out-of-scope.stderr b/tests/ui/type/type-arg-out-of-scope.stderr
index 7f18b4510f4..8665001e243 100644
--- a/tests/ui/type/type-arg-out-of-scope.stderr
+++ b/tests/ui/type/type-arg-out-of-scope.stderr
@@ -1,22 +1,22 @@
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/type-arg-out-of-scope.rs:3:29
    |
 LL | fn foo<T>(x: T) {
-   |        - type parameter from outer function
+   |        - type parameter from outer item
 LL |     fn bar(f: Box<dyn FnMut(T) -> T>) { }
-   |           -                 ^ use of generic parameter from outer function
+   |           -                 ^ use of generic parameter from outer item
    |           |
-   |           help: try using a local generic parameter instead: `<T>`
+   |           help: try introducing a local generic parameter here: `<T>`
 
-error[E0401]: can't use generic parameters from outer function
+error[E0401]: can't use generic parameters from outer item
   --> $DIR/type-arg-out-of-scope.rs:3:35
    |
 LL | fn foo<T>(x: T) {
-   |        - type parameter from outer function
+   |        - type parameter from outer item
 LL |     fn bar(f: Box<dyn FnMut(T) -> T>) { }
-   |           -                       ^ use of generic parameter from outer function
+   |           -                       ^ use of generic parameter from outer item
    |           |
-   |           help: try using a local generic parameter instead: `<T>`
+   |           help: try introducing a local generic parameter here: `<T>`
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/union/projection-as-union-type-error-2.stderr b/tests/ui/union/projection-as-union-type-error-2.stderr
index bab226f271d..21f4ea103ad 100644
--- a/tests/ui/union/projection-as-union-type-error-2.stderr
+++ b/tests/ui/union/projection-as-union-type-error-2.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `u8: NotImplemented` is not satisfied
 LL |     a: <Foo as Identity>::Identity,
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `NotImplemented` is not implemented for `u8`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/projection-as-union-type-error-2.rs:9:1
+   |
+LL | trait NotImplemented {}
+   | ^^^^^^^^^^^^^^^^^^^^
 note: required for `u8` to implement `Identity`
   --> $DIR/projection-as-union-type-error-2.rs:11:25
    |
diff --git a/tests/ui/union/projection-as-union-type-error.stderr b/tests/ui/union/projection-as-union-type-error.stderr
index e4fbe9603ad..2b0241caf98 100644
--- a/tests/ui/union/projection-as-union-type-error.stderr
+++ b/tests/ui/union/projection-as-union-type-error.stderr
@@ -3,6 +3,12 @@ error[E0277]: the trait bound `u8: Identity` is not satisfied
    |
 LL |     a:  <Foo as Identity>::Identity,
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Identity` is not implemented for `u8`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/projection-as-union-type-error.rs:6:1
+   |
+LL | pub trait Identity {
+   | ^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.rs b/tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.rs
new file mode 100644
index 00000000000..a192f3445f7
--- /dev/null
+++ b/tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.rs
@@ -0,0 +1,17 @@
+// edition: 2024
+// compile-flags: -Zunstable-options
+// check-pass
+
+#![crate_type = "lib"]
+
+#![deny(unused_unsafe)]
+
+unsafe fn unsf() {}
+
+unsafe fn foo() {
+    unsf();
+    //~^ WARN call to unsafe function is unsafe and requires unsafe block
+
+    // no unused_unsafe
+    unsafe { unsf(); }
+}
diff --git a/tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.stderr b/tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.stderr
new file mode 100644
index 00000000000..fbc621f4d0e
--- /dev/null
+++ b/tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.stderr
@@ -0,0 +1,16 @@
+warning: call to unsafe function is unsafe and requires unsafe block (error E0133)
+  --> $DIR/edition-2024-unsafe_op_in_unsafe_fn.rs:12:5
+   |
+LL |     unsf();
+   |     ^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+note: an unsafe function restricts its caller, but its body is safe by default
+  --> $DIR/edition-2024-unsafe_op_in_unsafe_fn.rs:11:1
+   |
+LL | unsafe fn foo() {
+   | ^^^^^^^^^^^^^^^
+   = note: `#[warn(unsafe_op_in_unsafe_fn)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/unsafe/issue-115348-false-positive-warning-of-unnecessary-unsafe.rs b/tests/ui/unsafe/issue-115348-false-positive-warning-of-unnecessary-unsafe.rs
new file mode 100644
index 00000000000..68559338d49
--- /dev/null
+++ b/tests/ui/unsafe/issue-115348-false-positive-warning-of-unnecessary-unsafe.rs
@@ -0,0 +1,16 @@
+// Regression test for #115348.
+
+unsafe fn uwu() {}
+
+// Tests that the false-positive warning "unnecessary `unsafe` block"
+// should not be reported, when the error "non-exhaustive patterns"
+// appears.
+
+fn foo(x: Option<u32>) {
+    match x {
+        //~^ ERROR non-exhaustive patterns: `None` not covered
+        Some(_) => unsafe { uwu() },
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/unsafe/issue-115348-false-positive-warning-of-unnecessary-unsafe.stderr b/tests/ui/unsafe/issue-115348-false-positive-warning-of-unnecessary-unsafe.stderr
new file mode 100644
index 00000000000..7384899b978
--- /dev/null
+++ b/tests/ui/unsafe/issue-115348-false-positive-warning-of-unnecessary-unsafe.stderr
@@ -0,0 +1,21 @@
+error[E0004]: non-exhaustive patterns: `None` not covered
+  --> $DIR/issue-115348-false-positive-warning-of-unnecessary-unsafe.rs:10:11
+   |
+LL |     match x {
+   |           ^ pattern `None` not covered
+   |
+note: `Option<u32>` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Option<u32>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         Some(_) => unsafe { uwu() },
+LL ~         None => todo!(),
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/unsized/issue-115203.rs b/tests/ui/unsized/issue-115203.rs
new file mode 100644
index 00000000000..5fe7bd64288
--- /dev/null
+++ b/tests/ui/unsized/issue-115203.rs
@@ -0,0 +1,11 @@
+// compile-flags: --emit link
+
+fn main() {
+    let a: [i32; 0] = [];
+    match [a[..]] {
+        //~^ ERROR cannot move a value of type `[i32]
+        //~| ERROR cannot move out of type `[i32]`, a non-copy slice
+        [[]] => (),
+        _ => (),
+    }
+}
diff --git a/tests/ui/unsized/issue-115203.stderr b/tests/ui/unsized/issue-115203.stderr
new file mode 100644
index 00000000000..3ee734988c5
--- /dev/null
+++ b/tests/ui/unsized/issue-115203.stderr
@@ -0,0 +1,19 @@
+error[E0161]: cannot move a value of type `[i32]`
+  --> $DIR/issue-115203.rs:5:12
+   |
+LL |     match [a[..]] {
+   |            ^^^^^ the size of `[i32]` cannot be statically determined
+
+error[E0508]: cannot move out of type `[i32]`, a non-copy slice
+  --> $DIR/issue-115203.rs:5:12
+   |
+LL |     match [a[..]] {
+   |            ^^^^^
+   |            |
+   |            cannot move out of here
+   |            move occurs because value has type `[i32]`, which does not implement the `Copy` trait
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0161, E0508.
+For more information about an error, try `rustc --explain E0161`.
diff --git a/tests/ui/unsized/issue-115809.rs b/tests/ui/unsized/issue-115809.rs
new file mode 100644
index 00000000000..ff25365ea50
--- /dev/null
+++ b/tests/ui/unsized/issue-115809.rs
@@ -0,0 +1,13 @@
+// compile-flags: --emit=link -Zmir-opt-level=2 -Zpolymorphize=on
+
+fn foo<T>() {
+    let a: [i32; 0] = [];
+    match [a[..]] {
+        //~^ ERROR cannot move a value of type `[i32]
+        //~| ERROR cannot move out of type `[i32]`, a non-copy slice
+        [[x]] => {}
+        _ => (),
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/unsized/issue-115809.stderr b/tests/ui/unsized/issue-115809.stderr
new file mode 100644
index 00000000000..a92554b79b9
--- /dev/null
+++ b/tests/ui/unsized/issue-115809.stderr
@@ -0,0 +1,19 @@
+error[E0161]: cannot move a value of type `[i32]`
+  --> $DIR/issue-115809.rs:5:12
+   |
+LL |     match [a[..]] {
+   |            ^^^^^ the size of `[i32]` cannot be statically determined
+
+error[E0508]: cannot move out of type `[i32]`, a non-copy slice
+  --> $DIR/issue-115809.rs:5:12
+   |
+LL |     match [a[..]] {
+   |            ^^^^^
+   |            |
+   |            cannot move out of here
+   |            move occurs because value has type `[i32]`, which does not implement the `Copy` trait
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0161, E0508.
+For more information about an error, try `rustc --explain E0161`.
diff --git a/tests/ui/unsized/issue-75707.stderr b/tests/ui/unsized/issue-75707.stderr
index 97618ed05ed..aa7f9c78fa8 100644
--- a/tests/ui/unsized/issue-75707.stderr
+++ b/tests/ui/unsized/issue-75707.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `MyCall: Callback` is not satisfied
 LL |     f::<dyn Processing<Call = MyCall>>();
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Callback` is not implemented for `MyCall`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-75707.rs:1:1
+   |
+LL | pub trait Callback {
+   | ^^^^^^^^^^^^^^^^^^
 note: required by a bound in `f`
   --> $DIR/issue-75707.rs:9:9
    |
diff --git a/tests/ui/wf/hir-wf-canonicalized.stderr b/tests/ui/wf/hir-wf-canonicalized.stderr
index 9fd0f9c81eb..21122e37da5 100644
--- a/tests/ui/wf/hir-wf-canonicalized.stderr
+++ b/tests/ui/wf/hir-wf-canonicalized.stderr
@@ -3,12 +3,24 @@ error[E0277]: the trait bound `Bar<'a, T>: Foo` is not satisfied
    |
 LL |     callback: Box<dyn Callback<dyn Callback<Bar<'a, T>>>>,
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `Bar<'a, T>`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/hir-wf-canonicalized.rs:3:1
+   |
+LL | trait Foo {
+   | ^^^^^^^^^
 
 error[E0277]: the trait bound `(dyn Callback<Bar<'a, T>, for<'b, 'c, 'd> Output = ()> + 'static): Foo` is not satisfied
   --> $DIR/hir-wf-canonicalized.rs:10:15
    |
 LL |     callback: Box<dyn Callback<dyn Callback<Bar<'a, T>>>>,
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `(dyn Callback<Bar<'a, T>, for<'b, 'c, 'd> Output = ()> + 'static)`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/hir-wf-canonicalized.rs:3:1
+   |
+LL | trait Foo {
+   | ^^^^^^^^^
 
 error[E0277]: the size for values of type `(dyn Callback<Bar<'a, T>, for<'b, 'c, 'd> Output = ()> + 'static)` cannot be known at compilation time
   --> $DIR/hir-wf-canonicalized.rs:10:15
diff --git a/tests/ui/wf/issue-95665.stderr b/tests/ui/wf/issue-95665.stderr
index b1cda59a916..f80cd41a4c2 100644
--- a/tests/ui/wf/issue-95665.stderr
+++ b/tests/ui/wf/issue-95665.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `u8: Trait` is not satisfied
 LL |     static VAR: Struct<u8>;
    |                 ^^^^^^^^^^ the trait `Trait` is not implemented for `u8`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-95665.rs:4:1
+   |
+LL | pub trait Trait: {}
+   | ^^^^^^^^^^^^^^^
 note: required by a bound in `Struct`
   --> $DIR/issue-95665.rs:6:22
    |
diff --git a/tests/ui/wf/wf-complex-assoc-type.stderr b/tests/ui/wf/wf-complex-assoc-type.stderr
index ef613e3132d..6a623bec815 100644
--- a/tests/ui/wf/wf-complex-assoc-type.stderr
+++ b/tests/ui/wf/wf-complex-assoc-type.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `bool: MyTrait` is not satisfied
 LL |     type MyItem = Option<((AssertMyTrait<bool>, u8))>;
    |                            ^^^^^^^^^^^^^^^^^^^ the trait `MyTrait` is not implemented for `bool`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/wf-complex-assoc-type.rs:1:1
+   |
+LL | trait MyTrait {}
+   | ^^^^^^^^^^^^^
 note: required by a bound in `AssertMyTrait`
   --> $DIR/wf-complex-assoc-type.rs:2:25
    |
diff --git a/tests/ui/wf/wf-foreign-fn-decl-ret.stderr b/tests/ui/wf/wf-foreign-fn-decl-ret.stderr
index b03023b5fd1..0e93601043a 100644
--- a/tests/ui/wf/wf-foreign-fn-decl-ret.stderr
+++ b/tests/ui/wf/wf-foreign-fn-decl-ret.stderr
@@ -3,6 +3,12 @@ error[E0277]: the trait bound `(): Foo` is not satisfied
    |
 LL |     pub fn lint_me() -> <() as Foo>::Assoc;
    |                         ^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/wf-foreign-fn-decl-ret.rs:6:1
+   |
+LL | pub trait Foo {
+   | ^^^^^^^^^^^^^
 
 error[E0277]: the trait bound `u32: Unsatisfied` is not satisfied
   --> $DIR/wf-foreign-fn-decl-ret.rs:14:32
@@ -10,6 +16,11 @@ error[E0277]: the trait bound `u32: Unsatisfied` is not satisfied
 LL |     pub fn lint_me_aswell() -> Bar<u32>;
    |                                ^^^^^^^^ the trait `Unsatisfied` is not implemented for `u32`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/wf-foreign-fn-decl-ret.rs:1:1
+   |
+LL | pub trait Unsatisfied {}
+   | ^^^^^^^^^^^^^^^^^^^^^
 note: required by a bound in `Bar`
   --> $DIR/wf-foreign-fn-decl-ret.rs:4:19
    |
diff --git a/tests/ui/wf/wf-packed-on-proj-of-type-as-unimpl-trait.stderr b/tests/ui/wf/wf-packed-on-proj-of-type-as-unimpl-trait.stderr
index e460cdcd3f3..52f46562c37 100644
--- a/tests/ui/wf/wf-packed-on-proj-of-type-as-unimpl-trait.stderr
+++ b/tests/ui/wf/wf-packed-on-proj-of-type-as-unimpl-trait.stderr
@@ -3,6 +3,12 @@ error[E0277]: the trait bound `DefaultAllocator: Allocator` is not satisfied
    |
 LL | struct Foo(Matrix<<DefaultAllocator as Allocator>::Buffer>);
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Allocator` is not implemented for `DefaultAllocator`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/wf-packed-on-proj-of-type-as-unimpl-trait.rs:23:1
+   |
+LL | pub trait Allocator { type Buffer; }
+   | ^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/tests/ui/where-clauses/higher-ranked-fn-type.quiet.stderr b/tests/ui/where-clauses/higher-ranked-fn-type.quiet.stderr
index 191a8ca8ebc..29b36f44a4d 100644
--- a/tests/ui/where-clauses/higher-ranked-fn-type.quiet.stderr
+++ b/tests/ui/where-clauses/higher-ranked-fn-type.quiet.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `for<'b> fn(&'b ()): Foo` is not satisfied
 LL |     called()
    |     ^^^^^^ the trait `for<'b> Foo` is not implemented for `fn(&'b ())`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/higher-ranked-fn-type.rs:6:1
+   |
+LL | trait Foo {
+   | ^^^^^^^^^
 note: required by a bound in `called`
   --> $DIR/higher-ranked-fn-type.rs:12:25
    |
diff --git a/tests/ui/where-clauses/higher-ranked-fn-type.verbose.stderr b/tests/ui/where-clauses/higher-ranked-fn-type.verbose.stderr
index ce409f627be..54afeaa7eda 100644
--- a/tests/ui/where-clauses/higher-ranked-fn-type.verbose.stderr
+++ b/tests/ui/where-clauses/higher-ranked-fn-type.verbose.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `for<Region(BrNamed(DefId(0:6 ~ higher_ranked_fn_t
 LL |     called()
    |     ^^^^^^ the trait `for<Region(BrNamed(DefId(0:6 ~ higher_ranked_fn_type[9e51]::called::'b), 'b))> Foo` is not implemented for `fn(&ReLateBound(DebruijnIndex(1), BoundRegion { var: 0, kind: BrNamed(DefId(0:6 ~ higher_ranked_fn_type[9e51]::called::'b), 'b) }) ())`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/higher-ranked-fn-type.rs:6:1
+   |
+LL | trait Foo {
+   | ^^^^^^^^^
 note: required by a bound in `called`
   --> $DIR/higher-ranked-fn-type.rs:12:25
    |
diff --git a/tests/ui/where-clauses/where-clause-method-substituion.stderr b/tests/ui/where-clauses/where-clause-method-substituion.stderr
index 8c47ed6d431..2f3b615a13b 100644
--- a/tests/ui/where-clauses/where-clause-method-substituion.stderr
+++ b/tests/ui/where-clauses/where-clause-method-substituion.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `X: Foo<X>` is not satisfied
 LL |     1.method::<X>();
    |                ^ the trait `Foo<X>` is not implemented for `X`
    |
+help: this trait has no implementations, consider adding one
+  --> $DIR/where-clause-method-substituion.rs:1:1
+   |
+LL | trait Foo<T> {
+   | ^^^^^^^^^^^^
 note: required by a bound in `Bar::method`
   --> $DIR/where-clause-method-substituion.rs:6:34
    |
diff --git a/triagebot.toml b/triagebot.toml
index 39c18a55697..d9d523bef39 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -585,7 +585,7 @@ cc = ["@nnethercote"]
 [assign]
 warn_non_default_branch = true
 contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html"
-users_on_vacation = ["jyn514", "clubby789", "spastorino"]
+users_on_vacation = ["jyn514"]
 
 [assign.adhoc_groups]
 compiler-team = [