about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes1
-rw-r--r--.gitmodules3
-rw-r--r--.reuse/dep588
-rw-r--r--Cargo.lock794
-rw-r--r--LICENSES/BSD-2-Clause.txt9
-rw-r--r--LICENSES/NCSA.txt15
-rw-r--r--LICENSES/Unicode-DFS-2016.txt22
-rw-r--r--README.md2
-rw-r--r--RELEASES.md4
-rw-r--r--compiler/rustc/src/main.rs9
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs2
-rw-r--r--compiler/rustc_ast_lowering/messages.ftl (renamed from compiler/rustc_ast_lowering/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs61
-rw-r--r--compiler/rustc_ast_passes/messages.ftl (renamed from compiler/rustc_ast_passes/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs13
-rw-r--r--compiler/rustc_ast_passes/src/lib.rs2
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs4
-rw-r--r--compiler/rustc_attr/messages.ftl (renamed from compiler/rustc_attr/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_attr/src/lib.rs2
-rw-r--r--compiler/rustc_borrowck/messages.ftl (renamed from compiler/rustc_borrowck/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_borrowck/src/dataflow.rs1
-rw-r--r--compiler/rustc_borrowck/src/def_use.rs2
-rw-r--r--compiler/rustc_borrowck/src/invalidation.rs11
-rw-r--r--compiler/rustc_borrowck/src/lib.rs14
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/trace.rs3
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs24
-rw-r--r--compiler/rustc_borrowck/src/used_muts.rs3
-rw-r--r--compiler/rustc_builtin_macros/messages.ftl (renamed from compiler/rustc_builtin_macros/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs35
-rw-r--r--compiler/rustc_builtin_macros/src/concat.rs15
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/lib.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/example/alloc_system.rs12
-rw-r--r--compiler/rustc_codegen_cranelift/src/allocator.rs28
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/constant.rs4
-rw-r--r--compiler/rustc_codegen_gcc/example/alloc_system.rs12
-rw-r--r--compiler/rustc_codegen_gcc/messages.ftl (renamed from compiler/rustc_codegen_gcc/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_codegen_gcc/src/lib.rs2
-rw-r--r--compiler/rustc_codegen_llvm/messages.ftl (renamed from compiler/rustc_codegen_llvm/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs1
-rw-r--r--compiler/rustc_codegen_llvm/src/lib.rs2
-rw-r--r--compiler/rustc_codegen_ssa/messages.ftl (renamed from compiler/rustc_codegen_ssa/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs1
-rw-r--r--compiler/rustc_codegen_ssa/src/back/symbol_export.rs9
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs44
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs3
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/analyze.rs1
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/mod.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/statement.rs1
-rw-r--r--compiler/rustc_codegen_ssa/src/target_features.rs9
-rw-r--r--compiler/rustc_const_eval/messages.ftl (renamed from compiler/rustc_const_eval/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_const_eval/src/interpret/step.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/terminator.rs6
-rw-r--r--compiler/rustc_const_eval/src/lib.rs2
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs6
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs3
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/resolver.rs15
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs20
-rw-r--r--compiler/rustc_data_structures/src/flat_map_in_place.rs (renamed from compiler/rustc_data_structures/src/map_in_place.rs)15
-rw-r--r--compiler/rustc_data_structures/src/lib.rs2
-rw-r--r--compiler/rustc_data_structures/src/stable_hasher.rs16
-rw-r--r--compiler/rustc_data_structures/src/unord.rs33
-rw-r--r--compiler/rustc_driver_impl/messages.ftl (renamed from compiler/rustc_driver_impl/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs3
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0368.md2
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0710.md4
-rw-r--r--compiler/rustc_error_messages/messages.ftl (renamed from compiler/rustc_error_messages/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_error_messages/src/lib.rs2
-rw-r--r--compiler/rustc_errors/messages.ftl (renamed from compiler/rustc_errors/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_errors/src/lib.rs2
-rw-r--r--compiler/rustc_expand/messages.ftl (renamed from compiler/rustc_expand/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_expand/src/base.rs8
-rw-r--r--compiler/rustc_expand/src/config.rs2
-rw-r--r--compiler/rustc_expand/src/expand.rs2
-rw-r--r--compiler/rustc_expand/src/lib.rs2
-rw-r--r--compiler/rustc_expand/src/mbe/diagnostics.rs12
-rw-r--r--compiler/rustc_feature/src/accepted.rs4
-rw-r--r--compiler/rustc_feature/src/active.rs1
-rw-r--r--compiler/rustc_hir/src/hir.rs18
-rw-r--r--compiler/rustc_hir/src/stable_hash_impls.rs17
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl (renamed from compiler/rustc_hir_analysis/locales/en-US.ftl)19
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/errors.rs107
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/generics.rs12
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs23
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs36
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs33
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsicck.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check_unused.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/builtin.rs14
-rw-r--r--compiler/rustc_hir_analysis/src/collect/item_bounds.rs34
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs21
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs51
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs27
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl (renamed from compiler/rustc_hir_typeck/locales/en-US.ftl)4
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs18
-rw-r--r--compiler/rustc_hir_typeck/src/inherited.rs50
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs236
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/op.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs2
-rw-r--r--compiler/rustc_incremental/messages.ftl (renamed from compiler/rustc_incremental/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_incremental/src/lib.rs2
-rw-r--r--compiler/rustc_incremental/src/persist/fs.rs4
-rw-r--r--compiler/rustc_infer/messages.ftl (renamed from compiler/rustc_infer/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_infer/src/infer/canonical/canonicalizer.rs69
-rw-r--r--compiler/rustc_infer/src/infer/combine.rs48
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs10
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs2
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs26
-rw-r--r--compiler/rustc_infer/src/infer/region_constraints/mod.rs45
-rw-r--r--compiler/rustc_infer/src/infer/resolve.rs15
-rw-r--r--compiler/rustc_infer/src/lib.rs2
-rw-r--r--compiler/rustc_infer/src/traits/mod.rs6
-rw-r--r--compiler/rustc_interface/messages.ftl (renamed from compiler/rustc_interface/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_interface/src/lib.rs2
-rw-r--r--compiler/rustc_interface/src/passes.rs45
-rw-r--r--compiler/rustc_interface/src/queries.rs9
-rw-r--r--compiler/rustc_interface/src/util.rs2
-rw-r--r--compiler/rustc_lexer/src/unescape.rs6
-rw-r--r--compiler/rustc_lint/messages.ftl (renamed from compiler/rustc_lint/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_lint/src/internal.rs30
-rw-r--r--compiler/rustc_lint/src/levels.rs4
-rw-r--r--compiler/rustc_lint/src/lib.rs2
-rw-r--r--compiler/rustc_lint/src/unused.rs8
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs7
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp1
-rw-r--r--compiler/rustc_metadata/messages.ftl (renamed from compiler/rustc_metadata/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_metadata/src/fs.rs26
-rw-r--r--compiler/rustc_metadata/src/lib.rs2
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs23
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs8
-rw-r--r--compiler/rustc_middle/messages.ftl (renamed from compiler/rustc_middle/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_middle/src/arena.rs4
-rw-r--r--compiler/rustc_middle/src/hir/map/mod.rs2
-rw-r--r--compiler/rustc_middle/src/hir/mod.rs10
-rw-r--r--compiler/rustc_middle/src/infer/unify_key.rs37
-rw-r--r--compiler/rustc_middle/src/lib.rs2
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs3
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs1
-rw-r--r--compiler/rustc_middle/src/mir/query.rs4
-rw-r--r--compiler/rustc_middle/src/mir/spanview.rs2
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs47
-rw-r--r--compiler/rustc_middle/src/mir/terminator.rs13
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs23
-rw-r--r--compiler/rustc_middle/src/query/mod.rs22
-rw-r--r--compiler/rustc_middle/src/traits/solve.rs96
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs9
-rw-r--r--compiler/rustc_middle/src/ty/context.rs22
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs4
-rw-r--r--compiler/rustc_middle/src/ty/error.rs8
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs11
-rw-r--r--compiler/rustc_middle/src/ty/query.rs76
-rw-r--r--compiler/rustc_mir_build/messages.ftl (renamed from compiler/rustc_mir_build/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_mir_build/src/build/cfg.rs11
-rw-r--r--compiler/rustc_mir_build/src/build/custom/parse/instruction.rs8
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs16
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs11
-rw-r--r--compiler/rustc_mir_build/src/build/scope.rs4
-rw-r--r--compiler/rustc_mir_build/src/lib.rs2
-rw-r--r--compiler/rustc_mir_build/src/lints.rs1
-rw-r--r--compiler/rustc_mir_dataflow/messages.ftl (renamed from compiler/rustc_mir_dataflow/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/direction.rs1
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs3
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/liveness.rs1
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs3
-rw-r--r--compiler/rustc_mir_dataflow/src/lib.rs2
-rw-r--r--compiler/rustc_mir_dataflow/src/move_paths/builder.rs6
-rw-r--r--compiler/rustc_mir_dataflow/src/value_analysis.rs3
-rw-r--r--compiler/rustc_mir_transform/src/abort_unwinding_calls.rs2
-rw-r--r--compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs3
-rw-r--r--compiler/rustc_mir_transform/src/add_retag.rs2
-rw-r--r--compiler/rustc_mir_transform/src/check_unsafety.rs28
-rw-r--r--compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs1
-rw-r--r--compiler/rustc_mir_transform/src/const_prop.rs1
-rw-r--r--compiler/rustc_mir_transform/src/const_prop_lint.rs1
-rw-r--r--compiler/rustc_mir_transform/src/coverage/debug.rs1
-rw-r--r--compiler/rustc_mir_transform/src/coverage/graph.rs41
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mod.rs2
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans.rs2
-rw-r--r--compiler/rustc_mir_transform/src/coverage/tests.rs2
-rw-r--r--compiler/rustc_mir_transform/src/dead_store_elimination.rs4
-rw-r--r--compiler/rustc_mir_transform/src/dest_prop.rs7
-rw-r--r--compiler/rustc_mir_transform/src/elaborate_drops.rs119
-rw-r--r--compiler/rustc_mir_transform/src/generator.rs3
-rw-r--r--compiler/rustc_mir_transform/src/inline.rs10
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs12
-rw-r--r--compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs2
-rw-r--r--compiler/rustc_mir_transform/src/remove_uninit_drops.rs23
-rw-r--r--compiler/rustc_mir_transform/src/separate_const_switch.rs4
-rw-r--r--compiler/rustc_mir_transform/src/simplify.rs1
-rw-r--r--compiler/rustc_mir_transform/src/ssa.rs56
-rw-r--r--compiler/rustc_monomorphize/messages.ftl (renamed from compiler/rustc_monomorphize/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs3
-rw-r--r--compiler/rustc_monomorphize/src/lib.rs2
-rw-r--r--compiler/rustc_monomorphize/src/partitioning/merging.rs2
-rw-r--r--compiler/rustc_monomorphize/src/partitioning/mod.rs2
-rw-r--r--compiler/rustc_parse/messages.ftl (renamed from compiler/rustc_parse/locales/en-US.ftl)5
-rw-r--r--compiler/rustc_parse/src/errors.rs17
-rw-r--r--compiler/rustc_parse/src/lexer/diagnostics.rs2
-rw-r--r--compiler/rustc_parse/src/lib.rs2
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs41
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs2
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs10
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs4
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs12
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs2
-rw-r--r--compiler/rustc_passes/messages.ftl (renamed from compiler/rustc_passes/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_passes/src/dead.rs30
-rw-r--r--compiler/rustc_passes/src/lib.rs2
-rw-r--r--compiler/rustc_passes/src/reachable.rs8
-rw-r--r--compiler/rustc_plugin_impl/messages.ftl (renamed from compiler/rustc_plugin_impl/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_plugin_impl/src/lib.rs2
-rw-r--r--compiler/rustc_privacy/messages.ftl (renamed from compiler/rustc_privacy/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_privacy/src/lib.rs2
-rw-r--r--compiler/rustc_query_impl/src/lib.rs1
-rw-r--r--compiler/rustc_query_impl/src/on_disk_cache.rs14
-rw-r--r--compiler/rustc_query_impl/src/plumbing.rs23
-rw-r--r--compiler/rustc_query_system/messages.ftl (renamed from compiler/rustc_query_system/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_query_system/src/dep_graph/graph.rs289
-rw-r--r--compiler/rustc_query_system/src/dep_graph/mod.rs3
-rw-r--r--compiler/rustc_query_system/src/lib.rs2
-rw-r--r--compiler/rustc_query_system/src/query/config.rs2
-rw-r--r--compiler/rustc_query_system/src/query/plumbing.rs161
-rw-r--r--compiler/rustc_resolve/messages.ftl (renamed from compiler/rustc_resolve/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs2
-rw-r--r--compiler/rustc_resolve/src/lib.rs25
-rw-r--r--compiler/rustc_resolve/src/macros.rs18
-rw-r--r--compiler/rustc_session/messages.ftl (renamed from compiler/rustc_session/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_session/src/code_stats.rs12
-rw-r--r--compiler/rustc_session/src/config.rs7
-rw-r--r--compiler/rustc_session/src/lib.rs2
-rw-r--r--compiler/rustc_session/src/session.rs49
-rw-r--r--compiler/rustc_smir/Cargo.toml19
-rw-r--r--compiler/rustc_smir/README.md37
-rw-r--r--compiler/rustc_smir/rust-toolchain.toml2
-rw-r--r--compiler/rustc_smir/src/lib.rs8
-rw-r--r--compiler/rustc_smir/src/mir.rs10
-rw-r--r--compiler/rustc_smir/src/rustc_internal/mod.rs15
-rw-r--r--compiler/rustc_smir/src/rustc_smir/mod.rs48
-rw-r--r--compiler/rustc_smir/src/stable_mir/mod.rs60
-rw-r--r--compiler/rustc_smir/src/very_unstable.rs27
-rw-r--r--compiler/rustc_span/src/source_map.rs11
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_symbol_mangling/messages.ftl (renamed from compiler/rustc_symbol_mangling/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_symbol_mangling/src/lib.rs2
-rw-r--r--compiler/rustc_target/src/asm/aarch64.rs4
-rw-r--r--compiler/rustc_target/src/asm/arm.rs12
-rw-r--r--compiler/rustc_target/src/asm/mod.rs21
-rw-r--r--compiler/rustc_target/src/asm/riscv.rs4
-rw-r--r--compiler/rustc_target/src/asm/x86.rs10
-rw-r--r--compiler/rustc_trait_selection/messages.ftl (renamed from compiler/rustc_trait_selection/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly.rs6
-rw-r--r--compiler/rustc_trait_selection/src/solve/canonical/canonicalize.rs51
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt.rs35
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs98
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals.rs5
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/cache.rs27
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/mod.rs107
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs3
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs119
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs79
-rw-r--r--compiler/rustc_trait_selection/src/traits/misc.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs18
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs14
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs124
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs6
-rw-r--r--compiler/rustc_ty_utils/messages.ftl (renamed from compiler/rustc_ty_utils/locales/en-US.ftl)0
-rw-r--r--compiler/rustc_ty_utils/src/assoc.rs65
-rw-r--r--compiler/rustc_ty_utils/src/lib.rs2
-rw-r--r--compiler/rustc_ty_utils/src/ty.rs13
-rw-r--r--compiler/rustc_type_ir/src/fold.rs2
-rw-r--r--compiler/rustc_type_ir/src/sty.rs2
-rw-r--r--config.example.toml (renamed from config.toml.example)0
-rw-r--r--library/alloc/src/boxed.rs1
-rw-r--r--library/alloc/src/collections/vec_deque/drain.rs40
-rw-r--r--library/alloc/src/collections/vec_deque/mod.rs27
-rw-r--r--library/alloc/src/rc.rs18
-rw-r--r--library/alloc/src/rc/tests.rs15
-rw-r--r--library/alloc/src/sync.rs20
-rw-r--r--library/alloc/src/tests.rs24
-rw-r--r--library/core/src/cell.rs6
-rw-r--r--library/core/src/convert/num.rs26
-rw-r--r--library/core/src/intrinsics/mir.rs3
-rw-r--r--library/core/src/marker.rs2
-rw-r--r--library/core/src/num/nonzero.rs16
-rw-r--r--library/core/src/ptr/const_ptr.rs4
-rw-r--r--library/core/src/ptr/mod.rs4
-rw-r--r--library/core/src/ptr/mut_ptr.rs4
-rw-r--r--library/std/src/path.rs6
-rw-r--r--library/std/src/sys/common/mod.rs1
-rw-r--r--library/std/src/sys/common/thread_local/fast_local.rs276
-rw-r--r--library/std/src/sys/common/thread_local/mod.rs109
-rw-r--r--library/std/src/sys/common/thread_local/os_local.rs217
-rw-r--r--library/std/src/sys/common/thread_local/static_local.rs115
-rw-r--r--library/std/src/thread/local.rs567
-rw-r--r--library/std/src/thread/mod.rs37
-rw-r--r--src/bootstrap/README.md2
-rw-r--r--src/bootstrap/bin/main.rs8
-rw-r--r--src/bootstrap/builder.rs11
-rw-r--r--src/bootstrap/config.rs4
-rwxr-xr-xsrc/bootstrap/configure.py6
-rw-r--r--src/bootstrap/dist.rs2
-rw-r--r--src/bootstrap/doc.rs1
-rw-r--r--src/bootstrap/format.rs8
-rw-r--r--src/bootstrap/lib.rs7
-rw-r--r--src/bootstrap/native.rs4
-rw-r--r--src/bootstrap/test.rs55
-rwxr-xr-xsrc/ci/docker/host-x86_64/dist-various-2/build-solaris-toolchain.sh10
-rw-r--r--src/ci/docker/host-x86_64/mingw-check/Dockerfile4
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-llvm-14/Dockerfile4
-rw-r--r--src/ci/github-actions/problem_matchers.json15
-rwxr-xr-xsrc/ci/scripts/collect-cpu-stats.sh3
-rwxr-xr-xsrc/ci/scripts/run-build-from-ci.sh2
-rwxr-xr-xsrc/ci/scripts/upload-artifacts.sh2
-rw-r--r--src/doc/footer.inc2
-rw-r--r--src/doc/rustc/src/instrument-coverage.md2
-rw-r--r--src/librustdoc/clean/mod.rs108
-rw-r--r--src/librustdoc/formats/cache.rs1
-rw-r--r--src/librustdoc/html/render/context.rs55
-rw-r--r--src/librustdoc/html/render/mod.rs733
-rw-r--r--src/librustdoc/html/render/print_item.rs9
-rw-r--r--src/librustdoc/html/render/search_index.rs23
-rw-r--r--src/librustdoc/html/render/sidebar.rs561
-rw-r--r--src/librustdoc/html/render/span_map.rs6
-rw-r--r--src/librustdoc/html/static/COPYRIGHT.txt4
-rw-r--r--src/librustdoc/html/static/fonts/FiraSans-LICENSE.txt4
-rw-r--r--src/librustdoc/html/static/fonts/NanumBarunGothic-LICENSE.txt4
-rw-r--r--src/librustdoc/html/static/fonts/SourceCodePro-LICENSE.txt4
-rw-r--r--src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md5
-rw-r--r--src/librustdoc/html/static/js/main.js39
-rw-r--r--src/librustdoc/html/static/js/search.js397
-rw-r--r--src/librustdoc/html/static/js/storage.js47
-rw-r--r--src/librustdoc/html/templates/item_info.html7
-rw-r--r--src/librustdoc/html/templates/page.html39
-rw-r--r--src/librustdoc/html/templates/short_item_info.html23
-rw-r--r--src/librustdoc/html/templates/sidebar.html37
-rw-r--r--src/librustdoc/visit_ast.rs29
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/CHANGELOG.md153
-rw-r--r--src/tools/clippy/COPYRIGHT4
-rw-r--r--src/tools/clippy/Cargo.toml2
-rw-r--r--src/tools/clippy/README.md4
-rw-r--r--src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md14
-rw-r--r--src/tools/clippy/clippy_lints/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_lints/src/almost_complete_range.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/collection_is_never_read.rs122
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/derivable_impls.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/exit.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/fn_null_check.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/format.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/instant_subtraction.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/len_zero.rs125
-rw-r--r--src/tools/clippy/clippy_lints/src/let_underscore.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs45
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_bits.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/match_result_ok.rs25
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_assert_message.rs82
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_doc.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs37
-rw-r--r--src/tools/clippy/clippy_lints/src/neg_multiply.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_async_block.rs84
-rw-r--r--src/tools/clippy/clippy_lints/src/size_of_ref.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/swap.rs111
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/utils.rs57
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/use_self.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/author.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/mod.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/wildcard_imports.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs148
-rw-r--r--src/tools/clippy/clippy_utils/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/macros.rs117
-rw-r--r--src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs5
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs6
-rw-r--r--src/tools/clippy/declare_clippy_lint/Cargo.toml2
-rw-r--r--src/tools/clippy/lintcheck/Cargo.toml14
-rw-r--r--src/tools/clippy/lintcheck/README.md9
-rw-r--r--src/tools/clippy/lintcheck/src/config.rs160
-rw-r--r--src/tools/clippy/lintcheck/src/popular-crates.rs65
-rw-r--r--src/tools/clippy/rust-toolchain2
-rw-r--r--src/tools/clippy/rustc_tools_util/README.md4
-rw-r--r--src/tools/clippy/src/driver.rs2
-rw-r--r--src/tools/clippy/src/main.rs2
-rw-r--r--src/tools/clippy/tests/dogfood.rs19
-rw-r--r--src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.rs (renamed from tests/ui-toml/array_size_threshold/array_size_threshold.rs)0
-rw-r--r--src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.stderr (renamed from tests/ui-toml/array_size_threshold/array_size_threshold.stderr)0
-rw-r--r--src/tools/clippy/tests/ui-toml/array_size_threshold/clippy.toml (renamed from tests/ui-toml/array_size_threshold/clippy.toml)0
-rw-r--r--src/tools/clippy/tests/ui/arithmetic_side_effects.rs28
-rw-r--r--src/tools/clippy/tests/ui/arithmetic_side_effects.stderr288
-rw-r--r--src/tools/clippy/tests/ui/async_yields_async.fixed1
-rw-r--r--src/tools/clippy/tests/ui/async_yields_async.rs1
-rw-r--r--src/tools/clippy/tests/ui/async_yields_async.stderr12
-rw-r--r--src/tools/clippy/tests/ui/collection_is_never_read.rs165
-rw-r--r--src/tools/clippy/tests/ui/collection_is_never_read.stderr52
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-10148.rs9
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-10148.stderr12
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6179.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs17
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed3
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs3
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr48
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed3
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs3
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr52
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.fixed37
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.rs37
-rw-r--r--src/tools/clippy/tests/ui/format.fixed6
-rw-r--r--src/tools/clippy/tests/ui/format.rs6
-rw-r--r--src/tools/clippy/tests/ui/format.stderr30
-rw-r--r--src/tools/clippy/tests/ui/impl_trait_in_params.stderr4
-rw-r--r--src/tools/clippy/tests/ui/implicit_clone.fixed2
-rw-r--r--src/tools/clippy/tests/ui/implicit_clone.rs2
-rw-r--r--src/tools/clippy/tests/ui/len_without_is_empty.rs92
-rw-r--r--src/tools/clippy/tests/ui/len_without_is_empty.stderr20
-rw-r--r--src/tools/clippy/tests/ui/let_unit.fixed4
-rw-r--r--src/tools/clippy/tests/ui/let_unit.rs4
-rw-r--r--src/tools/clippy/tests/ui/let_with_type_underscore.rs19
-rw-r--r--src/tools/clippy/tests/ui/let_with_type_underscore.stderr39
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.fixed1
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.rs1
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.stderr20
-rw-r--r--src/tools/clippy/tests/ui/match_result_ok.fixed2
-rw-r--r--src/tools/clippy/tests/ui/match_result_ok.stderr2
-rw-r--r--src/tools/clippy/tests/ui/missing_assert_message.rs84
-rw-r--r--src/tools/clippy/tests/ui/missing_assert_message.stderr131
-rw-r--r--src/tools/clippy/tests/ui/missing_doc.stderr70
-rw-r--r--src/tools/clippy/tests/ui/missing_doc_impl.stderr56
-rw-r--r--src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs28
-rw-r--r--src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr62
-rw-r--r--src/tools/clippy/tests/ui/new_ret_no_self.rs2
-rw-r--r--src/tools/clippy/tests/ui/redundant_async_block.fixed64
-rw-r--r--src/tools/clippy/tests/ui/redundant_async_block.rs64
-rw-r--r--src/tools/clippy/tests/ui/redundant_async_block.stderr28
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed1
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs1
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr12
-rw-r--r--src/tools/clippy/tests/ui/swap.fixed6
-rw-r--r--src/tools/clippy/tests/ui/swap.stderr24
-rw-r--r--src/tools/clippy/tests/ui/trailing_empty_array.rs2
-rw-r--r--src/tools/clippy/tests/ui/use_self.fixed10
-rw-r--r--src/tools/clippy/tests/ui/use_self.rs10
-rw-r--r--src/tools/collect-license-metadata/src/licenses.rs9
-rw-r--r--src/tools/collect-license-metadata/src/path_tree.rs96
-rw-r--r--src/tools/generate-copyright/src/main.rs6
-rw-r--r--src/tools/miri/src/bin/miri.rs5
-rw-r--r--src/tools/miri/tests/pass/intrinsics-integer.rs11
-rw-r--r--src/tools/miri/tests/pass/intrinsics-math.rs11
-rw-r--r--src/tools/miri/tests/pass/issues/issue-30530.rs11
-rw-r--r--src/tools/miri/tests/pass/tag-align-dyn-u64.rs11
m---------src/tools/rust-installer0
-rw-r--r--src/tools/rust-installer/.gitignore5
-rw-r--r--src/tools/rust-installer/Cargo.toml28
-rw-r--r--src/tools/rust-installer/README.md71
-rwxr-xr-xsrc/tools/rust-installer/combine-installers.sh15
-rwxr-xr-xsrc/tools/rust-installer/gen-install-script.sh15
-rwxr-xr-xsrc/tools/rust-installer/gen-installer.sh15
-rw-r--r--src/tools/rust-installer/install-template.sh996
-rwxr-xr-xsrc/tools/rust-installer/make-tarballs.sh15
-rw-r--r--src/tools/rust-installer/rust-installer-version1
-rw-r--r--src/tools/rust-installer/src/combiner.rs161
-rw-r--r--src/tools/rust-installer/src/compression.rs214
-rw-r--r--src/tools/rust-installer/src/generator.rs178
-rw-r--r--src/tools/rust-installer/src/lib.rs17
-rw-r--r--src/tools/rust-installer/src/main.rs27
-rw-r--r--src/tools/rust-installer/src/remove_dir_all.rs860
-rw-r--r--src/tools/rust-installer/src/scripter.rs68
-rw-r--r--src/tools/rust-installer/src/tarballer.rs143
-rw-r--r--src/tools/rust-installer/src/util.rs156
-rwxr-xr-xsrc/tools/rust-installer/test.sh1341
-rw-r--r--src/tools/rust-installer/test/image-docdir1/share/doc/rust/README1
-rw-r--r--src/tools/rust-installer/test/image-docdir1/share/doc/rust/rustdocs.txt1
-rw-r--r--src/tools/rust-installer/test/image-docdir2/share/doc/cargo/README1
-rw-r--r--src/tools/rust-installer/test/image-docdir2/share/doc/cargo/cargodocs.txt1
-rw-r--r--src/tools/rust-installer/test/image1/bin/bad-bin1
-rwxr-xr-xsrc/tools/rust-installer/test/image1/bin/program1
-rwxr-xr-xsrc/tools/rust-installer/test/image1/bin/program21
-rw-r--r--src/tools/rust-installer/test/image1/dir-to-install/foo0
-rw-r--r--src/tools/rust-installer/test/image1/dir-to-not-install/foo0
-rw-r--r--src/tools/rust-installer/test/image1/something-to-install0
-rw-r--r--src/tools/rust-installer/test/image1/something-to-not-install0
-rwxr-xr-xsrc/tools/rust-installer/test/image2/bin/oldprogram1
-rw-r--r--src/tools/rust-installer/test/image2/dir-to-install/bar0
-rw-r--r--src/tools/rust-installer/test/image2/something-to-install0
-rwxr-xr-xsrc/tools/rust-installer/test/image3/bin/cargo1
-rw-r--r--src/tools/rust-installer/test/image4/baz0
-rw-r--r--src/tools/rust-installer/test/image4/dir-to-install/qux/bar0
-rw-r--r--src/tools/rust-installer/test/image5/dir-to-install/foo0
-rw-r--r--src/tools/rustc-workspace-hack/Cargo.toml8
-rw-r--r--src/tools/rustdoc-gui/.eslintrc.js96
-rw-r--r--src/tools/rustdoc-gui/tester.js64
-rw-r--r--src/tools/rustdoc-js/.eslintrc.js96
-rw-r--r--src/tools/rustdoc-js/tester.js90
-rw-r--r--src/tools/tidy/src/deps.rs5
-rw-r--r--src/tools/tidy/src/pal.rs1
-rw-r--r--src/tools/tidy/src/style.rs69
-rw-r--r--tests/mir-opt/building/custom/terminators.drop_first.built.after.mir5
-rw-r--r--tests/mir-opt/building/custom/terminators.rs3
-rw-r--r--tests/mir-opt/building/match_false_edges.full_tested_match.built.after.mir1
-rw-r--r--tests/mir-opt/building/match_false_edges.full_tested_match2.built.after.mir1
-rw-r--r--tests/mir-opt/building/match_false_edges.main.built.after.mir1
-rw-r--r--tests/mir-opt/building/uniform_array_move_out.move_out_by_subslice.built.after.mir1
-rw-r--r--tests/mir-opt/building/uniform_array_move_out.move_out_from_end.built.after.mir1
-rw-r--r--tests/mir-opt/copy-prop/reborrow.demiraw.CopyProp.diff56
-rw-r--r--tests/mir-opt/copy-prop/reborrow.miraw.CopyProp.diff52
-rw-r--r--tests/mir-opt/copy-prop/reborrow.remut.CopyProp.diff50
-rw-r--r--tests/mir-opt/copy-prop/reborrow.reraw.CopyProp.diff50
-rw-r--r--tests/mir-opt/copy-prop/reborrow.rs46
-rw-r--r--tests/mir-opt/issue_72181.main.built.after.mir2
-rw-r--r--tests/mir-opt/issue_91633.bar.built.after.mir1
-rw-r--r--tests/mir-opt/issue_91633.hey.built.after.mir1
-rw-r--r--tests/run-make-fulldeps/rustdoc-themes/foo.rs2
-rw-r--r--tests/run-make/coverage-reports/expected_show_coverage.continue.txt2
-rw-r--r--tests/rustdoc-gui/scrape-examples-button-focus.goml10
-rw-r--r--tests/rustdoc-gui/sidebar.goml14
-rw-r--r--tests/rustdoc-js-std/println-typo.js12
-rw-r--r--tests/rustdoc-ui/crate-reference-in-block-module.rs5
-rw-r--r--tests/rustdoc-ui/crate-reference-in-block-module.stderr0
-rw-r--r--tests/rustdoc/anonymous-reexport.rs8
-rw-r--r--tests/rustdoc/issue-108679-reexport-of-reexport.rs29
-rw-r--r--tests/rustdoc/issue-108931-anonymous-reexport.rs21
-rw-r--r--tests/rustdoc/normalize-assoc-item.rs4
-rw-r--r--tests/ui-fulldeps/stable-mir/crate-info.rs104
-rw-r--r--tests/ui/asm/aarch64/parse-error.rs23
-rw-r--r--tests/ui/asm/aarch64/parse-error.stderr169
-rw-r--r--tests/ui/asm/bad-template.aarch64_mirunsafeck.stderr5
-rw-r--r--tests/ui/asm/bad-template.aarch64_thirunsafeck.stderr5
-rw-r--r--tests/ui/asm/bad-template.x86_64_mirunsafeck.stderr5
-rw-r--r--tests/ui/asm/bad-template.x86_64_thirunsafeck.stderr5
-rw-r--r--tests/ui/asm/x86_64/issue-89875.rs4
-rw-r--r--tests/ui/asm/x86_64/parse-error.rs23
-rw-r--r--tests/ui/asm/x86_64/parse-error.stderr173
-rw-r--r--tests/ui/binding/issue-53114-safety-checks.rs16
-rw-r--r--tests/ui/binding/issue-53114-safety-checks.stderr72
-rw-r--r--tests/ui/borrowck/let_underscore_temporary.rs27
-rw-r--r--tests/ui/check-cfg/my-awesome-platform.json12
-rw-r--r--tests/ui/check-cfg/values-target-json.rs21
-rw-r--r--tests/ui/check-cfg/values-target-json.stderr13
-rw-r--r--tests/ui/coherence/coherence-impls-copy.stderr6
-rw-r--r--tests/ui/coherence/deep-bad-copy-reason.rs2
-rw-r--r--tests/ui/coherence/deep-bad-copy-reason.stderr2
-rw-r--r--tests/ui/coherence/illegal-copy-bad-projection.rs16
-rw-r--r--tests/ui/coherence/illegal-copy-bad-projection.stderr9
-rw-r--r--tests/ui/const-generics/bad-generic-in-copy-impl.rs9
-rw-r--r--tests/ui/const-generics/bad-generic-in-copy-impl.stderr9
-rw-r--r--tests/ui/const-generics/type_mismatch.rs1
-rw-r--r--tests/ui/const-generics/type_mismatch.stderr18
-rw-r--r--tests/ui/const-generics/type_not_in_scope.rs1
-rw-r--r--tests/ui/const-generics/type_not_in_scope.stderr14
-rw-r--r--tests/ui/consts/min_const_fn/min_const_fn_unsafe_bad.rs3
-rw-r--r--tests/ui/consts/min_const_fn/min_const_fn_unsafe_bad.stderr11
-rw-r--r--tests/ui/dst/issue-90528-unsizing-suggestion-1.rs20
-rw-r--r--tests/ui/dst/issue-90528-unsizing-suggestion-1.stderr56
-rw-r--r--tests/ui/dst/issue-90528-unsizing-suggestion-2.rs28
-rw-r--r--tests/ui/dst/issue-90528-unsizing-suggestion-2.stderr94
-rw-r--r--tests/ui/dst/issue-90528-unsizing-suggestion-3.rs22
-rw-r--r--tests/ui/dst/issue-90528-unsizing-suggestion-3.stderr75
-rw-r--r--tests/ui/dst/issue-90528-unsizing-suggestion-4.rs26
-rw-r--r--tests/ui/dst/issue-90528-unsizing-suggestion-4.stderr79
-rw-r--r--tests/ui/dyn-star/feature-gate-dyn_star.rs2
-rw-r--r--tests/ui/dyn-star/feature-gate-dyn_star.stderr4
-rw-r--r--tests/ui/dyn-star/gated-span.rs8
-rw-r--r--tests/ui/dyn-star/gated-span.stderr12
-rw-r--r--tests/ui/dyn-star/no-explicit-dyn-star-cast.rs4
-rw-r--r--tests/ui/dyn-star/no-explicit-dyn-star-cast.stderr8
-rw-r--r--tests/ui/error-codes/E0184.stderr2
-rw-r--r--tests/ui/error-codes/E0206.rs2
-rw-r--r--tests/ui/error-codes/E0206.stderr2
-rw-r--r--tests/ui/exclusive-drop-and-copy.rs4
-rw-r--r--tests/ui/exclusive-drop-and-copy.stderr4
-rw-r--r--tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr6
-rw-r--r--tests/ui/fn/issue-39259.rs13
-rw-r--r--tests/ui/fn/issue-39259.stderr15
-rw-r--r--tests/ui/higher-rank-trait-bounds/fn-ptr.classic.stderr19
-rw-r--r--tests/ui/higher-rank-trait-bounds/fn-ptr.rs14
-rw-r--r--tests/ui/impl-trait/in-trait/new-lowering-strategy/simple-impl-trait.rs17
-rw-r--r--tests/ui/issues/issue-27340.rs2
-rw-r--r--tests/ui/issues/issue-27340.stderr2
-rw-r--r--tests/ui/issues/issue-3029.rs2
-rw-r--r--tests/ui/issues/issue-66667-function-cmp-cycle.rs3
-rw-r--r--tests/ui/issues/issue-66667-function-cmp-cycle.stderr34
-rw-r--r--tests/ui/iterators/into-iter-on-arrays-lint.fixed2
-rw-r--r--tests/ui/iterators/into-iter-on-arrays-lint.rs2
-rw-r--r--tests/ui/lifetimes/issue-95023.stderr6
-rw-r--r--tests/ui/lint/unused/unused-allocation.rs7
-rw-r--r--tests/ui/lint/unused/unused-allocation.stderr20
-rw-r--r--tests/ui/macros/bad-concat.stderr2
-rw-r--r--tests/ui/macros/concat.stderr4
-rw-r--r--tests/ui/macros/issue-106837.rs10
-rw-r--r--tests/ui/macros/issue-106837.stderr18
-rw-r--r--tests/ui/macros/issue-98790.rs24
-rw-r--r--tests/ui/macros/nonterminal-matching.stderr1
-rw-r--r--tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs2
-rw-r--r--tests/ui/opt-in-copy.rs4
-rw-r--r--tests/ui/opt-in-copy.stderr4
-rw-r--r--tests/ui/parser/integer-literal-start-ident.rs2
-rw-r--r--tests/ui/parser/integer-literal-start-ident.stderr10
-rw-r--r--tests/ui/parser/issues/issue-104088.rs6
-rw-r--r--tests/ui/parser/issues/issue-104088.stderr18
-rw-r--r--tests/ui/range/range_traits-2.stderr2
-rw-r--r--tests/ui/range/range_traits-3.stderr2
-rw-r--r--tests/ui/range/range_traits-6.stderr2
-rw-r--r--tests/ui/rfc-2632-const-trait-impl/gate.rs8
-rw-r--r--tests/ui/rfc-2632-const-trait-impl/gate.stderr15
-rw-r--r--tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.rs7
-rw-r--r--tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.stderr8
-rw-r--r--tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.rs9
-rw-r--r--tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.stderr11
-rw-r--r--tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs6
-rw-r--r--tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr11
-rw-r--r--tests/ui/self/arbitrary_self_types_trait.rs5
-rw-r--r--tests/ui/span/E0204.rs8
-rw-r--r--tests/ui/span/E0204.stderr8
-rw-r--r--tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs12
-rw-r--r--tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr11
-rw-r--r--tests/ui/str/str-escape.rs22
-rw-r--r--tests/ui/str/str-escape.stderr20
-rw-r--r--tests/ui/structs-enums/align-struct.rs26
-rw-r--r--tests/ui/suggestions/correct-binder-for-arbitrary-bound-sugg.rs16
-rw-r--r--tests/ui/suggestions/correct-binder-for-arbitrary-bound-sugg.stderr22
-rw-r--r--tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.fixed2
-rw-r--r--tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.rs2
-rw-r--r--tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.stderr6
-rw-r--r--tests/ui/suggestions/missing-bound-in-derive-copy-impl.rs2
-rw-r--r--tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr6
-rw-r--r--tests/ui/suggestions/missing-bound-in-manual-copy-impl-2.fixed2
-rw-r--r--tests/ui/suggestions/missing-bound-in-manual-copy-impl-2.rs2
-rw-r--r--tests/ui/suggestions/missing-bound-in-manual-copy-impl-2.stderr2
-rw-r--r--tests/ui/suggestions/missing-bound-in-manual-copy-impl.fixed2
-rw-r--r--tests/ui/suggestions/missing-bound-in-manual-copy-impl.rs2
-rw-r--r--tests/ui/suggestions/missing-bound-in-manual-copy-impl.stderr2
-rw-r--r--tests/ui/threads-sendsync/issue-43733-2.rs2
-rw-r--r--tests/ui/threads-sendsync/issue-43733.rs8
-rw-r--r--tests/ui/traits/copy-is-not-modulo-regions.not_static.stderr2
-rw-r--r--tests/ui/traits/copy-is-not-modulo-regions.rs2
-rw-r--r--tests/ui/traits/inductive-overflow/lifetime.rs6
-rw-r--r--tests/ui/traits/inductive-overflow/lifetime.stderr14
-rw-r--r--tests/ui/traits/issue-50480.rs4
-rw-r--r--tests/ui/traits/issue-50480.stderr4
-rw-r--r--tests/ui/traits/issue-87558.stderr6
-rw-r--r--tests/ui/traits/new-solver/canonical-int-var-eq-in-response.rs21
-rw-r--r--tests/ui/traits/new-solver/canonical-ty-var-eq-in-response.rs39
-rw-r--r--tests/ui/traits/new-solver/cast-checks-handling-projections.rs (renamed from tests/ui/typeck/lazy-norm/cast-checks-handling-projections.rs)2
-rw-r--r--tests/ui/traits/new-solver/deduce-ty-from-object.rs6
-rw-r--r--tests/ui/traits/new-solver/equating-projection-cyclically.rs (renamed from tests/ui/typeck/lazy-norm/equating-projection-cyclically.rs)0
-rw-r--r--tests/ui/traits/new-solver/equating-projection-cyclically.stderr (renamed from tests/ui/typeck/lazy-norm/equating-projection-cyclically.stderr)0
-rw-r--r--tests/ui/traits/new-solver/int-var-alias-eq.rs18
-rw-r--r--tests/ui/traits/new-solver/lazy-nested-obligations-1.rs13
-rw-r--r--tests/ui/traits/new-solver/lazy-nested-obligations-2.rs23
-rw-r--r--tests/ui/traits/new-solver/lazy-nested-obligations-3.rs38
-rw-r--r--tests/ui/traits/new-solver/normalize-param-env-1.rs40
-rw-r--r--tests/ui/traits/new-solver/normalize-param-env-2.rs26
-rw-r--r--tests/ui/traits/new-solver/normalize-param-env-3.rs32
-rw-r--r--tests/ui/traits/unsend-future.rs21
-rw-r--r--tests/ui/traits/unsend-future.stderr24
-rw-r--r--tests/ui/transmutability/issue-101739-1.rs2
-rw-r--r--tests/ui/transmutability/issue-101739-1.stderr12
-rw-r--r--tests/ui/typeck/lazy-norm/cast-checks-handling-projections.stderr9
-rw-r--r--tests/ui/union/field_checks.rs10
-rw-r--r--tests/ui/union/field_checks.stderr30
-rw-r--r--tests/ui/union/issue-41073.rs2
-rw-r--r--tests/ui/union/issue-41073.stderr6
-rw-r--r--tests/ui/union/union-copy.rs2
-rw-r--r--tests/ui/union/union-copy.stderr2
-rw-r--r--tests/ui/union/union-with-drop-fields.mirunsafeck.stderr18
-rw-r--r--tests/ui/union/union-with-drop-fields.rs6
-rw-r--r--tests/ui/union/union-with-drop-fields.thirunsafeck.stderr18
-rw-r--r--tests/ui/union/unresolved-field-isnt-copy.rs8
-rw-r--r--tests/ui/union/unresolved-field-isnt-copy.stderr9
-rw-r--r--tests/ui/unsafe/unsafe-fn-deref-ptr.mir.stderr28
-rw-r--r--tests/ui/unsafe/unsafe-fn-deref-ptr.rs3
-rw-r--r--tests/ui/unsafe/unsafe-fn-deref-ptr.thir.stderr28
-rw-r--r--tests/ui/wf/hir-wf-check-erase-regions.rs4
-rw-r--r--tests/ui/wf/hir-wf-check-erase-regions.stderr16
-rw-r--r--triagebot.toml9
707 files changed, 15011 insertions, 5075 deletions
diff --git a/.gitattributes b/.gitattributes
index 51a670b5fbe..d29c15fe712 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -9,7 +9,6 @@
 src/etc/installer/gfx/* binary
 src/vendor/** -text
 Cargo.lock linguist-generated=false
-config.toml.example linguist-language=TOML
 
 # Older git versions try to fix line endings on images and fonts, this prevents it.
 *.png binary
diff --git a/.gitmodules b/.gitmodules
index 4011a6fa6b9..e79f2f089c1 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,3 @@
-[submodule "src/rust-installer"]
-	path = src/tools/rust-installer
-	url = https://github.com/rust-lang/rust-installer.git
 [submodule "src/doc/nomicon"]
 	path = src/doc/nomicon
 	url = https://github.com/rust-lang/nomicon.git
diff --git a/.reuse/dep5 b/.reuse/dep5
index 5135f92a9d8..9a59f455fe9 100644
--- a/.reuse/dep5
+++ b/.reuse/dep5
@@ -1,13 +1,79 @@
+# WARNING: this metadata is currently incomplete, do not rely on it yet.
+
 Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 Files-Excluded:
  src/llvm-project
 
-Files: *
+# Note that we're explicitly listing the individual files at the root of the
+# repository rather than just having `Files: *`. This is explicitly done to
+# help downstream forks of the Rust compiler: this way, the files they add
+# won't be automatically marked as authored by the Rust project.
+Files: compiler/*
+       library/*
+       tests/*
+       src/*
+       .github/*
+       Cargo.lock
+       Cargo.toml
+       CODE_OF_CONDUCT.md
+       config.example.toml
+       configure
+       CONTRIBUTING.md
+       COPYRIGHT
+       LICENSE-APACHE
+       LICENSE-MIT
+       README.md
+       RELEASES.md
+       rustfmt.toml
+       triagebot.toml
+       x
+       x.ps1
+       x.py
+       .editorconfig
+       .git-blame-ignore-revs
+       .gitattributes
+       .gitignore
+       .gitmodules
+       .mailmap
 Copyright: The Rust Project Developers (see https://thanks.rust-lang.org)
 License: MIT or Apache-2.0
 
+Files: compiler/rustc_apfloat/*
+Copyright: LLVM APFloat authors
+           The Rust Project Developers (see https://thanks.rust-lang.org)
+License: NCSA AND (MIT OR Apache-2.0)
+
+Files: compiler/rustc_codegen_cranelift/src/cranelift_native.rs
+Copyright: The Cranelift Project Developers
+           The Rust Project Developers (see https://thanks.rust-lang.org)
+License: Apache-2.0 WITH LLVM-exception AND (Apache-2.0 OR MIT)
+
+Files: compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp
+Copyright: LLVM authors
+           The Rust Project Developers (see https://thanks.rust-lang.org)
+License: Apache-2.0 WITH LLVM-exception AND (Apache-2.0 OR MIT)
+
+Files: library/core/src/unicode/unicode_data.rs
+Copyright: 1991-2022 Unicode, Inc. All rights reserved.
+License: Unicode-DFS-2016
+
+Files: library/std/src/sync/mpmc/*
+Copyright: 2019 The Crossbeam Project Developers
+           The Rust Project Developers (see https://thanks.rust-lang.org)
+License: MIT OR Apache-2.0
+
+Files: library/std/src/sys/unix/locks/fuchsia_mutex.rs
+Copyright: 2016 The Fuchsia Authors
+           The Rust Project Developers (see https://thanks.rust-lang.org)
+License: BSD-2-Clause AND (MIT OR Apache-2.0)
+
+Files: src/test/rustdoc/auxiliary/enum-primitive.rs
+Copyright: 2015 Anders Kaseorg <andersk@mit.edu>
+License: MIT
+
 Files: src/librustdoc/html/static/fonts/FiraSans*
-Copyright: 2014, Mozilla Foundation, 2014, Telefonica S.A.
+Copyright: 2014, Mozilla Foundation
+           2014, Telefonica S.A.
 License: OFL-1.1
 
 Files: src/librustdoc/html/static/fonts/NanumBarun*
@@ -15,9 +81,19 @@ Copyright: 2010 NAVER Corporation
 License: OFL-1.1
 
 Files: src/librustdoc/html/static/fonts/SourceCodePro*
-Copyright: 2010, 2012 Adobe Systems Incorporated
+       src/librustdoc/html/static/fonts/SourceSerif4*
+Copyright: 2010, 2012, 2014-2023, Adobe Systems Incorporated
 License: OFL-1.1
 
-Files: src/librustdoc/html/static/fonts/SourceSerif4*
-Copyright: 2014-2021 Adobe Systems Incorporated
-License: OFL-1.1
+Files: src/librustdoc/html/static/css/normalize.css
+Copyright: Nicolas Gallagher and Jonathan Neal
+License: MIT
+
+Files: src/librustdoc/html/static/css/themes/ayu.css
+Copyright: Ike Ku, Jessica Stokes, Leon Guan
+           The Rust Project Developers (see https://thanks.rust-lang.org)
+License: MIT OR Apache-2.0
+
+Files: src/doc/rustc-dev-guide/mermaid.min.js
+Copyright: Knut Sveidqvist
+License: MIT
diff --git a/Cargo.lock b/Cargo.lock
index 82530c019a9..51332919fe7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -42,6 +42,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107"
 dependencies = [
  "cfg-if",
+ "getrandom",
  "once_cell",
  "version_check",
 ]
@@ -113,6 +114,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "arc-swap"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6"
+
+[[package]]
 name = "array_tool"
 version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -202,9 +209,9 @@ checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce"
 
 [[package]]
 name = "base64"
-version = "0.13.1"
+version = "0.21.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
 
 [[package]]
 name = "base64ct"
@@ -258,9 +265,9 @@ dependencies = [
 
 [[package]]
 name = "bstr"
-version = "1.0.1"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fca0852af221f458706eb0725c03e4ed6c46af9ac98e6a689d5e634215d594dd"
+checksum = "5ffdb39cb703212f3c11973452c2861b972f757b021158f3516ba10f2fa8b2c1"
 dependencies = [
  "memchr",
  "once_cell",
@@ -269,12 +276,21 @@ dependencies = [
 ]
 
 [[package]]
+name = "btoi"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dd6407f73a9b8b6162d8a2ef999fe6afd7cc15902ebf42c5cd296addf17e0ad"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
 name = "build-manifest"
 version = "0.1.0"
 dependencies = [
  "anyhow",
  "flate2",
- "hex 0.4.2",
+ "hex",
  "rayon",
  "serde",
  "serde_json",
@@ -356,8 +372,10 @@ dependencies = [
  "fwdansi",
  "git2",
  "git2-curl",
+ "gix",
+ "gix-features",
  "glob",
- "hex 0.4.2",
+ "hex",
  "hmac",
  "home",
  "http-auth",
@@ -496,18 +514,18 @@ dependencies = [
 
 [[package]]
 name = "cargo-util"
-version = "0.2.3"
+version = "0.2.4"
 dependencies = [
  "anyhow",
  "core-foundation",
- "crypto-hash",
  "filetime",
- "hex 0.4.2",
+ "hex",
  "jobserver",
  "libc",
  "log",
  "miow 0.5.0",
  "same-file",
+ "sha2",
  "shell-escape",
  "tempfile",
  "walkdir",
@@ -720,7 +738,7 @@ dependencies = [
 
 [[package]]
 name = "clippy"
-version = "0.1.69"
+version = "0.1.70"
 dependencies = [
  "clap 4.1.4",
  "clippy_lints",
@@ -763,7 +781,7 @@ dependencies = [
 
 [[package]]
 name = "clippy_lints"
-version = "0.1.69"
+version = "0.1.70"
 dependencies = [
  "cargo_metadata 0.15.3",
  "clippy_utils",
@@ -786,7 +804,7 @@ dependencies = [
 
 [[package]]
 name = "clippy_utils"
-version = "0.1.69"
+version = "0.1.70"
 dependencies = [
  "arrayvec 0.7.0",
  "if_chain",
@@ -795,6 +813,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "clru"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807"
+
+[[package]]
 name = "collect-license-metadata"
 version = "0.1.0"
 dependencies = [
@@ -843,24 +867,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "commoncrypto"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d056a8586ba25a1e4d61cb090900e495952c7886786fc55f909ab2f819b69007"
-dependencies = [
- "commoncrypto-sys",
-]
-
-[[package]]
-name = "commoncrypto-sys"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fed34f46747aa73dfaa578069fd8279d2818ade2b55f38f22a9401c7f4083e2"
-dependencies = [
- "libc",
-]
-
-[[package]]
 name = "compiler_builtins"
 version = "0.1.87"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1080,18 +1086,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "crypto-hash"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a77162240fd97248d19a564a565eb563a3f592b386e4136fb300909e67dddca"
-dependencies = [
- "commoncrypto",
- "hex 0.3.2",
- "openssl",
- "winapi",
-]
-
-[[package]]
 name = "cstr"
 version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1156,7 +1150,7 @@ checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69"
 
 [[package]]
 name = "declare_clippy_lint"
-version = "0.1.69"
+version = "0.1.70"
 dependencies = [
  "itertools",
  "quote",
@@ -1295,9 +1289,9 @@ dependencies = [
 
 [[package]]
 name = "dunce"
-version = "1.0.2"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "453440c271cf5577fd2a40e4942540cb7d0d2f85e27c8d07dd0023c925a67541"
+checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c"
 
 [[package]]
 name = "ecdsa"
@@ -1507,14 +1501,14 @@ checksum = "a214f5bb88731d436478f3ae1f8a277b62124089ba9fb67f4f93fb100ef73c90"
 
 [[package]]
 name = "filetime"
-version = "0.2.14"
+version = "0.2.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8"
+checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412"
 dependencies = [
  "cfg-if",
  "libc",
  "redox_syscall",
- "winapi",
+ "windows-sys 0.45.0",
 ]
 
 [[package]]
@@ -1817,6 +1811,559 @@ dependencies = [
 ]
 
 [[package]]
+name = "gix"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dabfac58aecb4a38cdd2568de66eb1f0d968fd6726f5a80cb8bea7944ef10cc0"
+dependencies = [
+ "gix-actor",
+ "gix-attributes",
+ "gix-config",
+ "gix-credentials",
+ "gix-date",
+ "gix-diff",
+ "gix-discover",
+ "gix-features",
+ "gix-glob",
+ "gix-hash",
+ "gix-hashtable",
+ "gix-index",
+ "gix-lock",
+ "gix-mailmap",
+ "gix-object",
+ "gix-odb",
+ "gix-pack",
+ "gix-path",
+ "gix-prompt",
+ "gix-protocol",
+ "gix-ref",
+ "gix-refspec",
+ "gix-revision",
+ "gix-sec",
+ "gix-tempfile",
+ "gix-transport",
+ "gix-traverse",
+ "gix-url",
+ "gix-validate",
+ "gix-worktree",
+ "log",
+ "once_cell",
+ "prodash",
+ "signal-hook",
+ "smallvec",
+ "thiserror",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "gix-actor"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc22b0cdc52237667c301dd7cdc6ead8f8f73c9f824e9942c8ebd6b764f6c0bf"
+dependencies = [
+ "bstr 1.3.0",
+ "btoi",
+ "gix-date",
+ "itoa",
+ "nom",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-attributes"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2231a25934a240d0a4b6f4478401c73ee81d8be52de0293eedbc172334abf3e1"
+dependencies = [
+ "bstr 1.3.0",
+ "gix-features",
+ "gix-glob",
+ "gix-path",
+ "gix-quote",
+ "thiserror",
+ "unicode-bom",
+]
+
+[[package]]
+name = "gix-bitmap"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "024bca0c7187517bda5ea24ab148c9ca8208dd0c3e2bea88cdb2008f91791a6d"
+dependencies = [
+ "thiserror",
+]
+
+[[package]]
+name = "gix-chunk"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0d39583cab06464b8bf73b3f1707458270f0e7383cb24c3c9c1a16e6f792978"
+dependencies = [
+ "thiserror",
+]
+
+[[package]]
+name = "gix-command"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2c6f75c1e0f924de39e750880a6e21307194bb1ab773efe3c7d2d787277f8ab"
+dependencies = [
+ "bstr 1.3.0",
+]
+
+[[package]]
+name = "gix-config"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52c62e26ce11f607712e4f49a0a192ed87675d30187fd61be070abbd607d12f1"
+dependencies = [
+ "bstr 1.3.0",
+ "gix-config-value",
+ "gix-features",
+ "gix-glob",
+ "gix-path",
+ "gix-ref",
+ "gix-sec",
+ "memchr",
+ "nom",
+ "once_cell",
+ "smallvec",
+ "thiserror",
+ "unicode-bom",
+]
+
+[[package]]
+name = "gix-config-value"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "693d4a4ba0531e46fe558459557a5b29fb86c3e4b2666c1c0861d93c7c678331"
+dependencies = [
+ "bitflags",
+ "bstr 1.3.0",
+ "gix-path",
+ "libc",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-credentials"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5be32b5fe339a31b8e53fa854081dc914c45020dcb64637f3c21baf69c96fc1b"
+dependencies = [
+ "bstr 1.3.0",
+ "gix-command",
+ "gix-config-value",
+ "gix-path",
+ "gix-prompt",
+ "gix-sec",
+ "gix-url",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-date"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b96271912ce39822501616f177dea7218784e6c63be90d5f36322ff3a722aae2"
+dependencies = [
+ "bstr 1.3.0",
+ "itoa",
+ "thiserror",
+ "time 0.3.17",
+]
+
+[[package]]
+name = "gix-diff"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "585b0834d4b6791a848637c4e109545fda9b0f29b591ba55edb33ceda6e7856b"
+dependencies = [
+ "gix-hash",
+ "gix-object",
+ "imara-diff",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-discover"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91c204adba5ebd211c74735cbb65817d277e154486bac0dffa3701f163b80350"
+dependencies = [
+ "bstr 1.3.0",
+ "dunce",
+ "gix-hash",
+ "gix-path",
+ "gix-ref",
+ "gix-sec",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-features"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e6a9dfa7b3c1a99315203e8b97f8f99f3bd95731590607abeaa5ca31bc41fe3"
+dependencies = [
+ "bytes",
+ "crc32fast",
+ "crossbeam-channel",
+ "flate2",
+ "gix-hash",
+ "libc",
+ "once_cell",
+ "parking_lot 0.12.1",
+ "prodash",
+ "sha1_smol",
+ "thiserror",
+ "walkdir",
+]
+
+[[package]]
+name = "gix-glob"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93e43efd776bc543f46f0fd0ca3d920c37af71a764a16f2aebd89765e9ff2993"
+dependencies = [
+ "bitflags",
+ "bstr 1.3.0",
+]
+
+[[package]]
+name = "gix-hash"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c0c5a9f4d621d4f4ea046bb331df5c746ca735b8cae5b234cc2be70ee4dbef0"
+dependencies = [
+ "hex",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-hashtable"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9609c1b8f36f12968e6a6098f7cdb52004f7d42d570f47a2d6d7c16612f19acb"
+dependencies = [
+ "gix-hash",
+ "hashbrown 0.13.1",
+ "parking_lot 0.12.1",
+]
+
+[[package]]
+name = "gix-index"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c12caf7886c7ba06f2b28835cdc2be1dca86bd047d00299d2d49e707ce1c2616"
+dependencies = [
+ "bitflags",
+ "bstr 1.3.0",
+ "btoi",
+ "filetime",
+ "gix-bitmap",
+ "gix-features",
+ "gix-hash",
+ "gix-lock",
+ "gix-object",
+ "gix-traverse",
+ "itoa",
+ "memmap2 0.5.10",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-lock"
+version = "4.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66119ff8a4a395d0ea033fef718bc85f8b4f0855874f4ce1e005fc16cfe1f66e"
+dependencies = [
+ "fastrand",
+ "gix-tempfile",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-mailmap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b66aea5e52875cd4915f4957a6f4b75831a36981e2ec3f5fad9e370e444fe1a"
+dependencies = [
+ "bstr 1.3.0",
+ "gix-actor",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-object"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8df068db9180ee935fbb70504848369e270bdcb576b05c0faa8b9fd3b86fc017"
+dependencies = [
+ "bstr 1.3.0",
+ "btoi",
+ "gix-actor",
+ "gix-features",
+ "gix-hash",
+ "gix-validate",
+ "hex",
+ "itoa",
+ "nom",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-odb"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9a5f9e1afbd509761977a2ea02869cedaaba500b4e783deb2e4de5179a55a80"
+dependencies = [
+ "arc-swap",
+ "gix-features",
+ "gix-hash",
+ "gix-object",
+ "gix-pack",
+ "gix-path",
+ "gix-quote",
+ "parking_lot 0.12.1",
+ "tempfile",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-pack"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51db84e1459a8022e518d40a8778028d793dbb28e4d35c9a5eaf92658fb0775"
+dependencies = [
+ "clru",
+ "gix-chunk",
+ "gix-diff",
+ "gix-features",
+ "gix-hash",
+ "gix-hashtable",
+ "gix-object",
+ "gix-path",
+ "gix-tempfile",
+ "gix-traverse",
+ "memmap2 0.5.10",
+ "parking_lot 0.12.1",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-packetline"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d63e5e5a9a92d4fc6b63ff9d94954d25c779ce25c98d5bbe2e4399aa42f7073c"
+dependencies = [
+ "bstr 1.3.0",
+ "hex",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-path"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6c104a66dec149cb8f7aaafc6ab797654cf82d67f050fd0cb7e7294e328354b"
+dependencies = [
+ "bstr 1.3.0",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-prompt"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a20cebf73229debaa82574c4fd20dcaf00fa8d4bfce823a862c4e990d7a0b5b4"
+dependencies = [
+ "gix-command",
+ "gix-config-value",
+ "nix",
+ "parking_lot 0.12.1",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-protocol"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d372ab11d5d28ac21800e3f1a6603a67c1ead57f6f5fab07e1e73e960f331c1"
+dependencies = [
+ "bstr 1.3.0",
+ "btoi",
+ "gix-credentials",
+ "gix-features",
+ "gix-hash",
+ "gix-transport",
+ "maybe-async",
+ "nom",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-quote"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a282f5a8d9ee0b09ec47390ac727350c48f2f5c76d803cd8da6b3e7ad56e0bcb"
+dependencies = [
+ "bstr 1.3.0",
+ "btoi",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-ref"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90a0ed29e581f04b904ecd0c32b11f33b8209b5a0af9c43f415249a4f2fba632"
+dependencies = [
+ "gix-actor",
+ "gix-features",
+ "gix-hash",
+ "gix-lock",
+ "gix-object",
+ "gix-path",
+ "gix-tempfile",
+ "gix-validate",
+ "memmap2 0.5.10",
+ "nom",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-refspec"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aba332462bda2e8efeae4302b39a6ed01ad56ef772fd5b7ef197cf2798294d65"
+dependencies = [
+ "bstr 1.3.0",
+ "gix-hash",
+ "gix-revision",
+ "gix-validate",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-revision"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed98e4a0254953c64bc913bd23146a1de662067d5cf974cbdde396958b39e5b0"
+dependencies = [
+ "bstr 1.3.0",
+ "gix-date",
+ "gix-hash",
+ "gix-hashtable",
+ "gix-object",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-sec"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8ffa5bf0772f9b01de501c035b6b084cf9b8bb07dec41e3afc6a17336a65f47"
+dependencies = [
+ "bitflags",
+ "dirs",
+ "gix-path",
+ "libc",
+ "windows",
+]
+
+[[package]]
+name = "gix-tempfile"
+version = "4.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88751f247234b1f73c8e8056fd835a0999b04e596e052302cb71186005dc4b27"
+dependencies = [
+ "libc",
+ "once_cell",
+ "parking_lot 0.12.1",
+ "signal-hook",
+ "signal-hook-registry",
+ "tempfile",
+]
+
+[[package]]
+name = "gix-transport"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d633947b36a2fbbc089195bdc71621158f1660c2ff2a6b12b0279c16e2f764bc"
+dependencies = [
+ "base64",
+ "bstr 1.3.0",
+ "curl",
+ "gix-command",
+ "gix-credentials",
+ "gix-features",
+ "gix-packetline",
+ "gix-quote",
+ "gix-sec",
+ "gix-url",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-traverse"
+version = "0.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd9a4a07bb22168dc79c60e1a6a41919d198187ca83d8a5940ad8d7122a45df3"
+dependencies = [
+ "gix-hash",
+ "gix-hashtable",
+ "gix-object",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-url"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "044072b7ce8601b62dcec841b92129f5cc677072823324121b395d766ac5f528"
+dependencies = [
+ "bstr 1.3.0",
+ "gix-features",
+ "gix-path",
+ "home",
+ "thiserror",
+ "url",
+]
+
+[[package]]
+name = "gix-validate"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b69ddb780ea1465255e66818d75b7098371c58dbc9560da4488a44b9f5c7e443"
+dependencies = [
+ "bstr 1.3.0",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-worktree"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7cb9af6e56152953d8fe113c4f9d7cf60cf7a982362711e9200a255579b49cb"
+dependencies = [
+ "bstr 1.3.0",
+ "gix-attributes",
+ "gix-features",
+ "gix-glob",
+ "gix-hash",
+ "gix-index",
+ "gix-object",
+ "gix-path",
+ "io-close",
+ "thiserror",
+]
+
+[[package]]
 name = "glob"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1933,12 +2480,6 @@ dependencies = [
 
 [[package]]
 name = "hex"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
-
-[[package]]
-name = "hex"
 version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
@@ -2131,6 +2672,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "imara-diff"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e98c1d0ad70fc91b8b9654b1f33db55e59579d3b3de2bffdced0fdb810570cb8"
+dependencies = [
+ "ahash 0.8.2",
+ "hashbrown 0.12.3",
+]
+
+[[package]]
 name = "indenter"
 version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2200,6 +2751,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "io-close"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cadcf447f06744f8ce713d2d6239bb5bde2c357a452397a9ed90c625da390bc"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
 name = "io-lifetimes"
 version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2232,9 +2793,9 @@ dependencies = [
 
 [[package]]
 name = "itoa"
-version = "1.0.2"
+version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
+checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
 
 [[package]]
 name = "jemalloc-sys"
@@ -2521,6 +3082,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
 
 [[package]]
+name = "maybe-async"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f1b8c13cb1f814b634a96b2c725449fe7ed464a7b8781de8688be5ffbd3f305"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
 name = "md-5"
 version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2577,7 +3149,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cbdc226fa10994e8f66a4d2f6f000148bc563a1c671b6dcd2135737018033d8a"
 dependencies = [
  "log",
- "memmap2",
+ "memmap2 0.2.1",
  "parking_lot 0.11.2",
  "perf-event-open-sys",
  "rustc-hash",
@@ -2604,6 +3176,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "memmap2"
+version = "0.5.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327"
+dependencies = [
+ "libc",
+]
+
+[[package]]
 name = "memoffset"
 version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2705,6 +3286,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
 
 [[package]]
+name = "nix"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "libc",
+ "static_assertions",
+]
+
+[[package]]
 name = "nom"
 version = "7.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2760,6 +3353,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "num_threads"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
+dependencies = [
+ "libc",
+]
+
+[[package]]
 name = "object"
 version = "0.29.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3260,6 +3862,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "prodash"
+version = "23.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d73c6b64cb5b99eb63ca97d378685712617ec0172ff5c04cd47a489d3e2c51f8"
+dependencies = [
+ "parking_lot 0.12.1",
+]
+
+[[package]]
 name = "profiler_builtins"
 version = "0.0.0"
 dependencies = [
@@ -3605,11 +4216,14 @@ dependencies = [
  "bstr 0.2.17",
  "clap 3.2.20",
  "getrandom",
+ "hashbrown 0.12.3",
  "libc",
  "libz-sys",
+ "once_cell",
  "rand",
  "regex",
  "serde_json",
+ "smallvec",
  "syn",
  "url",
  "winapi",
@@ -3900,7 +4514,7 @@ dependencies = [
  "jobserver",
  "libc",
  "measureme",
- "memmap2",
+ "memmap2 0.2.1",
  "parking_lot 0.11.2",
  "rustc-hash",
  "rustc-rayon",
@@ -4669,15 +5283,9 @@ dependencies = [
 name = "rustc_smir"
 version = "0.0.0"
 dependencies = [
- "rustc_borrowck",
- "rustc_driver",
- "rustc_hir",
- "rustc_interface",
  "rustc_middle",
- "rustc_mir_dataflow",
- "rustc_mir_transform",
- "rustc_serialize",
- "rustc_trait_selection",
+ "rustc_span",
+ "tracing",
 ]
 
 [[package]]
@@ -5115,6 +5723,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "sha1_smol"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
+
+[[package]]
 name = "sha2"
 version = "0.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5147,6 +5761,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "42a568c8f2cd051a4d283bd6eb0343ac214c1b0f1ac19f93e1175b2dee38c73d"
 
 [[package]]
+name = "signal-hook"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9"
+dependencies = [
+ "libc",
+ "signal-hook-registry",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
 name = "signature"
 version = "1.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5186,9 +5819,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
 
 [[package]]
 name = "smallvec"
-version = "1.8.1"
+version = "1.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc88c725d61fc6c3132893370cac4a0200e3fedf5da8331c570664b1987f5ca2"
+checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
 
 [[package]]
 name = "snap"
@@ -5611,6 +6244,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
 dependencies = [
  "itoa",
+ "libc",
+ "num_threads",
  "serde",
  "time-core",
  "time-macros",
@@ -5851,7 +6486,7 @@ version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "54ddb6f31025943e2f9d59237f433711c461a43d9415974c3eb3a4902edc1c1f"
 dependencies = [
- "bstr 1.0.1",
+ "bstr 1.3.0",
  "cargo_metadata 0.15.3",
  "color-eyre",
  "colored",
@@ -5975,6 +6610,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "unicode-bom"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63ec69f541d875b783ca40184d655f2927c95f0bffd486faa83cd3ac3529ec32"
+
+[[package]]
 name = "unicode-ident"
 version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -6243,6 +6884,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
+name = "windows"
+version = "0.43.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04662ed0e3e5630dfa9b26e4cb823b817f1a9addda855d973a9458c236556244"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
 name = "windows-sys"
 version = "0.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/LICENSES/BSD-2-Clause.txt b/LICENSES/BSD-2-Clause.txt
new file mode 100644
index 00000000000..5f662b354cd
--- /dev/null
+++ b/LICENSES/BSD-2-Clause.txt
@@ -0,0 +1,9 @@
+Copyright (c) <year> <owner> 
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/LICENSES/NCSA.txt b/LICENSES/NCSA.txt
new file mode 100644
index 00000000000..cf5413effa2
--- /dev/null
+++ b/LICENSES/NCSA.txt
@@ -0,0 +1,15 @@
+University of Illinois/NCSA Open Source License
+
+Copyright (c) <Year> <Owner Organization Name>. All rights reserved.
+
+Developed by: <Name of Development Group> <Name of Institution> <URL for Development Group/Institution>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers.
+
+     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution.
+
+     * Neither the names of <Name of Development Group, Name of Institution>, nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
diff --git a/LICENSES/Unicode-DFS-2016.txt b/LICENSES/Unicode-DFS-2016.txt
new file mode 100644
index 00000000000..71fd6ac5e12
--- /dev/null
+++ b/LICENSES/Unicode-DFS-2016.txt
@@ -0,0 +1,22 @@
+UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
+
+Unicode Data Files include all data files under the directories http://www.unicode.org/Public/, http://www.unicode.org/reports/, http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and http://www.unicode.org/utility/trac/browser/.
+
+Unicode Data Files do not include PDF online code charts under the directory http://www.unicode.org/Public/.
+
+Software includes any source code published in the Unicode Standard or under the directories http://www.unicode.org/Public/, http://www.unicode.org/reports/, http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and http://www.unicode.org/utility/trac/browser/.
+
+NOTICE TO USER: Carefully read the following legal agreement. BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright © 1991-2016 Unicode, Inc. All rights reserved. Distributed under the Terms of Use in http://www.unicode.org/copyright.html.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of the Unicode data files and any associated documentation (the "Data Files") or Unicode software and any associated documentation (the "Software") to deal in the Data Files or Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Data Files or Software, and to permit persons to whom the Data Files or Software are furnished to do so, provided that either
+
+     (a) this copyright and permission notice appear with all copies of the Data Files or Software, or
+     (b) this copyright and permission notice appear in associated Documentation.
+
+THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in these Data Files or Software without prior written authorization of the copyright holder.
diff --git a/README.md b/README.md
index c424bd12ffd..c19e129a920 100644
--- a/README.md
+++ b/README.md
@@ -99,7 +99,7 @@ See [the rustc-dev-guide for more info][sysllvm].
    The Rust build system uses a file named `config.toml` in the root of the
    source tree to determine various configuration settings for the build.
    Set up the defaults intended for distros to get started. You can see a full
-   list of options in `config.toml.example`.
+   list of options in `config.example.toml`.
 
    ```sh
    printf 'profile = "user" \nchangelog-seen = 2 \n' > config.toml
diff --git a/RELEASES.md b/RELEASES.md
index f418ab23d10..4e974bbe974 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -3861,6 +3861,8 @@ Version 1.41.1 (2020-02-27)
 * [Always check types of static items][69145]
 * [Always check lifetime bounds of `Copy` impls][69145]
 * [Fix miscompilation in callers of `Layout::repeat`][69225]
+* [Rust 1.41.0 was announced as the last Rust release with tier 1 or tier 2 support for 32-bit Apple targets][apple-32bit-drop].
+  That announcement did not expect a patch release. 1.41.1 also includes release binaries for these targets.
 
 [69225]: https://github.com/rust-lang/rust/issues/69225
 [69145]: https://github.com/rust-lang/rust/pull/69145
@@ -3953,7 +3955,7 @@ Misc
 Compatibility Notes
 -------------------
 
-- [As previously announced 1.41.0 will be the last tier 1 release for 32-bit
+- [As previously announced 1.41 will be the last tier 1 release for 32-bit
   Apple targets.][apple-32bit-drop] This means that the source code is still
   available to build, but the targets are no longer being tested and release
   binaries for those platforms will no longer be distributed by the Rust project.
diff --git a/compiler/rustc/src/main.rs b/compiler/rustc/src/main.rs
index e21c9b66044..434b978ae31 100644
--- a/compiler/rustc/src/main.rs
+++ b/compiler/rustc/src/main.rs
@@ -24,6 +24,15 @@
 // The two crates we link to here, `std` and `rustc_driver`, are both dynamic
 // libraries. So we must reference jemalloc symbols one way or another, because
 // this file is the only object code in the rustc executable.
+//
+// NOTE: if you are reading this comment because you want to set a custom `global_allocator` for
+// benchmarking, consider using the benchmarks in the `rustc-perf` collector suite instead:
+// https://github.com/rust-lang/rustc-perf/blob/master/collector/README.md#profiling
+//
+// NOTE: if you are reading this comment because you want to replace jemalloc with another allocator
+// to compare their performance, see
+// https://github.com/rust-lang/rust/commit/b90cfc887c31c3e7a9e6d462e2464db1fe506175#diff-43914724af6e464c1da2171e4a9b6c7e607d5bc1203fa95c0ab85be4122605ef
+// for an example of how to do so.
 
 #[unix_sigpipe = "sig_dfl"]
 fn main() {
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 7dcb03b4c78..6fed0b660e8 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -12,7 +12,7 @@ use crate::ptr::P;
 use crate::token::{self, Token};
 use crate::tokenstream::*;
 
-use rustc_data_structures::map_in_place::MapInPlace;
+use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
 use rustc_data_structures::sync::Lrc;
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::Ident;
diff --git a/compiler/rustc_ast_lowering/locales/en-US.ftl b/compiler/rustc_ast_lowering/messages.ftl
index 3ccd84398ec..3ccd84398ec 100644
--- a/compiler/rustc_ast_lowering/locales/en-US.ftl
+++ b/compiler/rustc_ast_lowering/messages.ftl
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index b20157f2c7c..ea7fa02521e 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -92,7 +92,7 @@ mod lifetime_collector;
 mod pat;
 mod path;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 struct LoweringContext<'a, 'hir> {
     tcx: TyCtxt<'hir>,
@@ -435,7 +435,9 @@ fn compute_hir_hash(
 
 pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> {
     let sess = tcx.sess;
-    tcx.ensure().output_filenames(());
+    // Queries that borrow `resolver_for_lowering`.
+    tcx.ensure_with_value().output_filenames(());
+    tcx.ensure_with_value().early_lint_checks(());
     let (mut resolver, krate) = tcx.resolver_for_lowering(()).steal();
 
     let ast_index = index_crate(&resolver.node_id_to_def_id, &krate);
@@ -463,8 +465,10 @@ pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> {
         rustc_span::hygiene::clear_syntax_context_map();
     }
 
-    let hir_hash = compute_hir_hash(tcx, &owners);
-    hir::Crate { owners, hir_hash }
+    // Don't hash unless necessary, because it's expensive.
+    let opt_hir_hash =
+        if tcx.sess.needs_crate_hash() { Some(compute_hir_hash(tcx, &owners)) } else { None };
+    hir::Crate { owners, opt_hir_hash }
 }
 
 #[derive(Copy, Clone, PartialEq, Debug)]
@@ -657,42 +661,33 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
         bodies.sort_by_key(|(k, _)| *k);
         let bodies = SortedMap::from_presorted_elements(bodies);
-        let (hash_including_bodies, hash_without_bodies) = self.hash_owner(node, &bodies);
-        let (nodes, parenting) =
-            index::index_hir(self.tcx.sess, &*self.tcx.definitions_untracked(), node, &bodies);
-        let nodes = hir::OwnerNodes { hash_including_bodies, hash_without_bodies, nodes, bodies };
-        let attrs = {
-            let hash = self.tcx.with_stable_hashing_context(|mut hcx| {
+
+        // Don't hash unless necessary, because it's expensive.
+        let (opt_hash_including_bodies, attrs_hash) = if self.tcx.sess.needs_crate_hash() {
+            self.tcx.with_stable_hashing_context(|mut hcx| {
+                let mut stable_hasher = StableHasher::new();
+                hcx.with_hir_bodies(node.def_id(), &bodies, |hcx| {
+                    node.hash_stable(hcx, &mut stable_hasher)
+                });
+                let h1 = stable_hasher.finish();
+
                 let mut stable_hasher = StableHasher::new();
                 attrs.hash_stable(&mut hcx, &mut stable_hasher);
-                stable_hasher.finish()
-            });
-            hir::AttributeMap { map: attrs, hash }
+                let h2 = stable_hasher.finish();
+
+                (Some(h1), Some(h2))
+            })
+        } else {
+            (None, None)
         };
+        let (nodes, parenting) =
+            index::index_hir(self.tcx.sess, &*self.tcx.definitions_untracked(), node, &bodies);
+        let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies };
+        let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash };
 
         self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map })
     }
 
-    /// Hash the HIR node twice, one deep and one shallow hash. This allows to differentiate
-    /// queries which depend on the full HIR tree and those which only depend on the item signature.
-    fn hash_owner(
-        &mut self,
-        node: hir::OwnerNode<'hir>,
-        bodies: &SortedMap<hir::ItemLocalId, &'hir hir::Body<'hir>>,
-    ) -> (Fingerprint, Fingerprint) {
-        self.tcx.with_stable_hashing_context(|mut hcx| {
-            let mut stable_hasher = StableHasher::new();
-            hcx.with_hir_bodies(node.def_id(), bodies, |hcx| {
-                node.hash_stable(hcx, &mut stable_hasher)
-            });
-            let hash_including_bodies = stable_hasher.finish();
-            let mut stable_hasher = StableHasher::new();
-            hcx.without_hir_bodies(|hcx| node.hash_stable(hcx, &mut stable_hasher));
-            let hash_without_bodies = stable_hasher.finish();
-            (hash_including_bodies, hash_without_bodies)
-        })
-    }
-
     /// This method allocates a new `HirId` for the given `NodeId` and stores it in
     /// the `LoweringContext`'s `NodeId => HirId` map.
     /// Take care not to call this method if the resulting `HirId` is then not
diff --git a/compiler/rustc_ast_passes/locales/en-US.ftl b/compiler/rustc_ast_passes/messages.ftl
index 747bd52b22c..747bd52b22c 100644
--- a/compiler/rustc_ast_passes/locales/en-US.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 96042ea3078..9af25e5cae2 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -337,9 +337,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
             ast::TyKind::Never => {
                 gate_feature_post!(&self, never_type, ty.span, "the `!` type is experimental");
             }
-            ast::TyKind::TraitObject(_, ast::TraitObjectSyntax::DynStar, ..) => {
-                gate_feature_post!(&self, dyn_star, ty.span, "dyn* trait objects are unstable");
-            }
             _ => {}
         }
         visit::walk_ty(self, ty)
@@ -425,14 +422,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
             ast::ExprKind::TryBlock(_) => {
                 gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental");
             }
-            ast::ExprKind::Closure(box ast::Closure { constness: ast::Const::Yes(_), .. }) => {
-                gate_feature_post!(
-                    &self,
-                    const_closures,
-                    e.span,
-                    "const closures are experimental"
-                );
-            }
             _ => {}
         }
         visit::walk_expr(self, e)
@@ -594,6 +583,8 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
     gate_all!(inline_const_pat, "inline-const in pattern position is experimental");
     gate_all!(associated_const_equality, "associated const equality is incomplete");
     gate_all!(yeet_expr, "`do yeet` expression is experimental");
+    gate_all!(dyn_star, "`dyn*` trait objects are experimental");
+    gate_all!(const_closures, "const closures are experimental");
 
     // All uses of `gate_all!` below this point were added in #65742,
     // and subsequently disabled (with the non-early gating readded).
diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs
index 6207a32b28d..e2c666604b3 100644
--- a/compiler/rustc_ast_passes/src/lib.rs
+++ b/compiler/rustc_ast_passes/src/lib.rs
@@ -21,4 +21,4 @@ pub mod feature_gate;
 pub mod node_count;
 pub mod show_span;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index cacfe9eb2f1..a4d91a95662 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -244,6 +244,10 @@ impl<'a> State<'a> {
             (&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => {
                 parser::PREC_FORCE_PAREN
             }
+            // For a binary expression like `(match () { _ => a }) OP b`, the parens are required
+            // otherwise the parser would interpret `match () { _ => a }` as a statement,
+            // with the remaining `OP b` not making sense. So we force parens.
+            (&ast::ExprKind::Match(..), _) => parser::PREC_FORCE_PAREN,
             _ => left_prec,
         };
 
diff --git a/compiler/rustc_attr/locales/en-US.ftl b/compiler/rustc_attr/messages.ftl
index a7f8c993d42..a7f8c993d42 100644
--- a/compiler/rustc_attr/locales/en-US.ftl
+++ b/compiler/rustc_attr/messages.ftl
diff --git a/compiler/rustc_attr/src/lib.rs b/compiler/rustc_attr/src/lib.rs
index 5fede0a58ac..49818c14f27 100644
--- a/compiler/rustc_attr/src/lib.rs
+++ b/compiler/rustc_attr/src/lib.rs
@@ -26,4 +26,4 @@ pub use rustc_ast::attr::*;
 
 pub(crate) use rustc_ast::HashStableContext;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_borrowck/locales/en-US.ftl b/compiler/rustc_borrowck/messages.ftl
index a3b6b5e8138..a3b6b5e8138 100644
--- a/compiler/rustc_borrowck/locales/en-US.ftl
+++ b/compiler/rustc_borrowck/messages.ftl
diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs
index d2574aa58c2..0762987e229 100644
--- a/compiler/rustc_borrowck/src/dataflow.rs
+++ b/compiler/rustc_borrowck/src/dataflow.rs
@@ -390,6 +390,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
             | mir::StatementKind::Deinit(..)
             | mir::StatementKind::StorageLive(..)
             | mir::StatementKind::Retag { .. }
+            | mir::StatementKind::PlaceMention(..)
             | mir::StatementKind::AscribeUserType(..)
             | mir::StatementKind::Coverage(..)
             | mir::StatementKind::Intrinsic(..)
diff --git a/compiler/rustc_borrowck/src/def_use.rs b/compiler/rustc_borrowck/src/def_use.rs
index 8e62a0198be..9e9f0b4b4ad 100644
--- a/compiler/rustc_borrowck/src/def_use.rs
+++ b/compiler/rustc_borrowck/src/def_use.rs
@@ -72,6 +72,8 @@ pub fn categorize(context: PlaceContext) -> Option<DefUse> {
         PlaceContext::MutatingUse(MutatingUseContext::Drop) =>
             Some(DefUse::Drop),
 
+        // This statement exists to help unsafeck. It does not require the place to be live.
+        PlaceContext::NonUse(NonUseContext::PlaceMention) => None,
         // Debug info is neither def nor use.
         PlaceContext::NonUse(NonUseContext::VarDebugInfo) => None,
 
diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs
index 6217676d5c1..a71c4163286 100644
--- a/compiler/rustc_borrowck/src/invalidation.rs
+++ b/compiler/rustc_borrowck/src/invalidation.rs
@@ -79,6 +79,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
             }
             // Only relevant for mir typeck
             StatementKind::AscribeUserType(..)
+            // Only relevant for unsafeck
+            | StatementKind::PlaceMention(..)
             // Doesn't have any language semantics
             | StatementKind::Coverage(..)
             // Does not actually affect borrowck
@@ -118,15 +120,6 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
                     LocalMutationIsAllowed::Yes,
                 );
             }
-            TerminatorKind::DropAndReplace {
-                place: drop_place,
-                value: new_value,
-                target: _,
-                unwind: _,
-            } => {
-                self.mutate_place(location, *drop_place, Deep);
-                self.consume_operand(location, new_value);
-            }
             TerminatorKind::Call {
                 func,
                 args,
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 0fc3240c560..5e77f6b190a 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -100,7 +100,7 @@ use places_conflict::{places_conflict, PlaceConflictBias};
 use region_infer::RegionInferenceContext;
 use renumber::RegionCtxt;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 // FIXME(eddyb) perhaps move this somewhere more centrally.
 #[derive(Debug)]
@@ -690,6 +690,8 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx
             }
             // Only relevant for mir typeck
             StatementKind::AscribeUserType(..)
+            // Only relevant for unsafeck
+            | StatementKind::PlaceMention(..)
             // Doesn't have any language semantics
             | StatementKind::Coverage(..)
             // These do not actually affect borrowck
@@ -743,15 +745,6 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx
                     flow_state,
                 );
             }
-            TerminatorKind::DropAndReplace {
-                place: drop_place,
-                value: new_value,
-                target: _,
-                unwind: _,
-            } => {
-                self.mutate_place(loc, (*drop_place, span), Deep, flow_state);
-                self.consume_operand(loc, (new_value, span), flow_state);
-            }
             TerminatorKind::Call {
                 func,
                 args,
@@ -866,7 +859,6 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx
             | TerminatorKind::Assert { .. }
             | TerminatorKind::Call { .. }
             | TerminatorKind::Drop { .. }
-            | TerminatorKind::DropAndReplace { .. }
             | TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ }
             | TerminatorKind::FalseUnwind { real_target: _, unwind: _ }
             | TerminatorKind::Goto { .. }
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
index ec4b2e9d3e4..748c8b9e442 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
@@ -325,7 +325,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
         if errors.is_empty() {
             definition_ty
         } else {
-            let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
+            let reported = infcx.err_ctxt().report_fulfillment_errors(&errors);
             self.tcx.ty_error(reported)
         }
     }
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
index a687d3f5352..9731b10aa99 100644
--- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
+++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
@@ -435,8 +435,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> {
         //
         // What we *actually* generate is a store to a temporary
         // for the call (`TMP = call()...`) and then a
-        // `DropAndReplace` to swap that with `X`
-        // (`DropAndReplace` has very particular semantics).
+        // `Drop(X)` followed by `X = TMP`  to swap that with `X`.
     }
 }
 
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 06c7b8b8f87..3919c4793a0 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -772,7 +772,9 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
 
         match context {
             PlaceContext::MutatingUse(_) => ty::Invariant,
-            PlaceContext::NonUse(StorageDead | StorageLive | VarDebugInfo) => ty::Invariant,
+            PlaceContext::NonUse(StorageDead | StorageLive | PlaceMention | VarDebugInfo) => {
+                ty::Invariant
+            }
             PlaceContext::NonMutatingUse(
                 Inspect | Copy | Move | SharedBorrow | ShallowBorrow | UniqueBorrow | AddressOf
                 | Projection,
@@ -1282,6 +1284,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             | StatementKind::Retag { .. }
             | StatementKind::Coverage(..)
             | StatementKind::ConstEvalCounter
+            | StatementKind::PlaceMention(..)
             | StatementKind::Nop => {}
             StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => {
                 bug!("Statement not allowed in this MIR phase")
@@ -1312,24 +1315,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 // no checks needed for these
             }
 
-            TerminatorKind::DropAndReplace { place, value, target: _, unwind: _ } => {
-                let place_ty = place.ty(body, tcx).ty;
-                let rv_ty = value.ty(body, tcx);
-
-                let locations = term_location.to_locations();
-                if let Err(terr) =
-                    self.sub_types(rv_ty, place_ty, locations, ConstraintCategory::Assignment)
-                {
-                    span_mirbug!(
-                        self,
-                        term,
-                        "bad DropAndReplace ({:?} = {:?}): {:?}",
-                        place_ty,
-                        rv_ty,
-                        terr
-                    );
-                }
-            }
             TerminatorKind::SwitchInt { discr, .. } => {
                 self.check_operand(discr, term_location);
 
@@ -1629,7 +1614,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             }
             TerminatorKind::Unreachable => {}
             TerminatorKind::Drop { target, unwind, .. }
-            | TerminatorKind::DropAndReplace { target, unwind, .. }
             | TerminatorKind::Assert { target, cleanup: unwind, .. } => {
                 self.assert_iscleanup(body, block_data, target, is_cleanup);
                 if let Some(unwind) = unwind {
diff --git a/compiler/rustc_borrowck/src/used_muts.rs b/compiler/rustc_borrowck/src/used_muts.rs
index 18dbe24d3b8..c5991e0bc25 100644
--- a/compiler/rustc_borrowck/src/used_muts.rs
+++ b/compiler/rustc_borrowck/src/used_muts.rs
@@ -71,9 +71,6 @@ impl<'visit, 'cx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'tc
             TerminatorKind::Call { destination, .. } => {
                 self.remove_never_initialized_mut_locals(*destination);
             }
-            TerminatorKind::DropAndReplace { place, .. } => {
-                self.remove_never_initialized_mut_locals(*place);
-            }
             _ => {}
         }
 
diff --git a/compiler/rustc_builtin_macros/locales/en-US.ftl b/compiler/rustc_builtin_macros/messages.ftl
index 4d088e27b36..4d088e27b36 100644
--- a/compiler/rustc_builtin_macros/locales/en-US.ftl
+++ b/compiler/rustc_builtin_macros/messages.ftl
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index 3fdbc971527..8c1579baacb 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -203,17 +203,6 @@ pub fn parse_asm_args<'a>(
         // Validate the order of named, positional & explicit register operands and
         // clobber_abi/options. We do this at the end once we have the full span
         // of the argument available.
-        if !args.options_spans.is_empty() {
-            diag.struct_span_err(span, "arguments are not allowed after options")
-                .span_labels(args.options_spans.clone(), "previous options")
-                .span_label(span, "argument")
-                .emit();
-        } else if let Some((_, abi_span)) = args.clobber_abis.last() {
-            diag.struct_span_err(span, "arguments are not allowed after clobber_abi")
-                .span_label(*abi_span, "clobber_abi")
-                .span_label(span, "argument")
-                .emit();
-        }
         if explicit_reg {
             if name.is_some() {
                 diag.struct_span_err(span, "explicit register arguments cannot have names").emit();
@@ -227,17 +216,6 @@ pub fn parse_asm_args<'a>(
                     .emit();
                 continue;
             }
-            if !args.reg_args.is_empty() {
-                let mut err = diag.struct_span_err(
-                    span,
-                    "named arguments cannot follow explicit register arguments",
-                );
-                err.span_label(span, "named argument");
-                for pos in &args.reg_args {
-                    err.span_label(args.operands[*pos].1, "explicit register argument");
-                }
-                err.emit();
-            }
             args.named_args.insert(name, slot);
         } else {
             if !args.named_args.is_empty() || !args.reg_args.is_empty() {
@@ -478,15 +456,6 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
 
     let full_span = span_start.to(p.prev_token.span);
 
-    if !args.options_spans.is_empty() {
-        let mut err = p
-            .sess
-            .span_diagnostic
-            .struct_span_err(full_span, "clobber_abi is not allowed after options");
-        err.span_labels(args.options_spans.clone(), "options");
-        return Err(err);
-    }
-
     match &new_abis[..] {
         // should have errored above during parsing
         [] => unreachable!(),
@@ -699,6 +668,10 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
                                         args.operands[idx].1,
                                         "explicit register arguments cannot be used in the asm template",
                                     );
+                                    err.span_help(
+                                        args.operands[idx].1,
+                                        "use the register name directly in the assembly code",
+                                    );
                                 }
                                 err.emit();
                                 None
diff --git a/compiler/rustc_builtin_macros/src/concat.rs b/compiler/rustc_builtin_macros/src/concat.rs
index 7da9bdc38a2..36682bbe070 100644
--- a/compiler/rustc_builtin_macros/src/concat.rs
+++ b/compiler/rustc_builtin_macros/src/concat.rs
@@ -42,6 +42,18 @@ pub fn expand_concat(
                     has_errors = true;
                 }
             },
+            // We also want to allow negative numeric literals.
+            ast::ExprKind::Unary(ast::UnOp::Neg, ref expr) if let ast::ExprKind::Lit(token_lit) = expr.kind => {
+                match ast::LitKind::from_token_lit(token_lit) {
+                    Ok(ast::LitKind::Int(i, _)) => accumulator.push_str(&format!("-{i}")),
+                    Ok(ast::LitKind::Float(f, _)) => accumulator.push_str(&format!("-{f}")),
+                    Err(err) => {
+                        report_lit_error(&cx.sess.parse_sess, err, token_lit, e.span);
+                        has_errors = true;
+                    }
+                    _ => missing_literal.push(e.span),
+                }
+            }
             ast::ExprKind::IncludedBytes(..) => {
                 cx.span_err(e.span, "cannot concatenate a byte string literal")
             }
@@ -53,9 +65,10 @@ pub fn expand_concat(
             }
         }
     }
+
     if !missing_literal.is_empty() {
         let mut err = cx.struct_span_err(missing_literal, "expected a literal");
-        err.note("only literals (like `\"foo\"`, `42` and `3.14`) can be passed to `concat!()`");
+        err.note("only literals (like `\"foo\"`, `-42` and `3.14`) can be passed to `concat!()`");
         err.emit();
         return DummyResult::any(sp);
     } else if has_errors {
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 1f819beeb5d..6b3053fdfac 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -1052,6 +1052,7 @@ impl<'a> MethodDef<'a> {
     ///         ::core::hash::Hash::hash(&{ self.y }, state)
     ///     }
     /// }
+    /// ```
     fn expand_struct_method_body<'b>(
         &self,
         cx: &mut ExtCtxt<'_>,
diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index 8afb6e56069..71177b8789b 100644
--- a/compiler/rustc_builtin_macros/src/lib.rs
+++ b/compiler/rustc_builtin_macros/src/lib.rs
@@ -56,7 +56,7 @@ pub mod proc_macro_harness;
 pub mod standard_library_imports;
 pub mod test_harness;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
     let mut register = |name, kind| resolver.register_builtin_macro(name, kind);
diff --git a/compiler/rustc_codegen_cranelift/example/alloc_system.rs b/compiler/rustc_codegen_cranelift/example/alloc_system.rs
index 50261c19397..e64daf96b01 100644
--- a/compiler/rustc_codegen_cranelift/example/alloc_system.rs
+++ b/compiler/rustc_codegen_cranelift/example/alloc_system.rs
@@ -1,12 +1,6 @@
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
+// SPDX-License-Identifier: MIT OR Apache-2.0
+// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org)
+
 #![no_std]
 
 pub struct System;
diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs
index 1c73957ca57..2c246ceb37d 100644
--- a/compiler/rustc_codegen_cranelift/src/allocator.rs
+++ b/compiler/rustc_codegen_cranelift/src/allocator.rs
@@ -4,6 +4,7 @@
 use crate::prelude::*;
 
 use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS};
+use rustc_codegen_ssa::base::allocator_kind_for_codegen;
 use rustc_session::config::OomStrategy;
 use rustc_span::symbol::sym;
 
@@ -13,24 +14,15 @@ pub(crate) fn codegen(
     module: &mut impl Module,
     unwind_context: &mut UnwindContext,
 ) -> bool {
-    let any_dynamic_crate = tcx.dependency_formats(()).iter().any(|(_, list)| {
-        use rustc_middle::middle::dependency_format::Linkage;
-        list.iter().any(|&linkage| linkage == Linkage::Dynamic)
-    });
-    if any_dynamic_crate {
-        false
-    } else if let Some(kind) = tcx.allocator_kind(()) {
-        codegen_inner(
-            module,
-            unwind_context,
-            kind,
-            tcx.alloc_error_handler_kind(()).unwrap(),
-            tcx.sess.opts.unstable_opts.oom,
-        );
-        true
-    } else {
-        false
-    }
+    let Some(kind) = allocator_kind_for_codegen(tcx) else { return false };
+    codegen_inner(
+        module,
+        unwind_context,
+        kind,
+        tcx.alloc_error_handler_kind(()).unwrap(),
+        tcx.sess.opts.unstable_opts.oom,
+    );
+    true
 }
 
 fn codegen_inner(
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 7f857528c7c..230256ba5aa 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -499,7 +499,6 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
             TerminatorKind::Yield { .. }
             | TerminatorKind::FalseEdge { .. }
             | TerminatorKind::FalseUnwind { .. }
-            | TerminatorKind::DropAndReplace { .. }
             | TerminatorKind::GeneratorDrop => {
                 bug!("shouldn't exist at codegen {:?}", bb_data.terminator());
             }
@@ -820,6 +819,7 @@ fn codegen_stmt<'tcx>(
         | StatementKind::Nop
         | StatementKind::FakeRead(..)
         | StatementKind::Retag { .. }
+        | StatementKind::PlaceMention(..)
         | StatementKind::AscribeUserType(..) => {}
 
         StatementKind::Coverage { .. } => fx.tcx.sess.fatal("-Zcoverage is unimplemented"),
diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs
index 49c4f1aaaef..efdf9f6d5bc 100644
--- a/compiler/rustc_codegen_cranelift/src/constant.rs
+++ b/compiler/rustc_codegen_cranelift/src/constant.rs
@@ -529,6 +529,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>(
                         | StatementKind::StorageDead(_)
                         | StatementKind::Retag(_, _)
                         | StatementKind::AscribeUserType(_, _)
+                        | StatementKind::PlaceMention(..)
                         | StatementKind::Coverage(_)
                         | StatementKind::ConstEvalCounter
                         | StatementKind::Nop => {}
@@ -543,8 +544,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>(
                     | TerminatorKind::Unreachable
                     | TerminatorKind::Drop { .. }
                     | TerminatorKind::Assert { .. } => {}
-                    TerminatorKind::DropAndReplace { .. }
-                    | TerminatorKind::Yield { .. }
+                    TerminatorKind::Yield { .. }
                     | TerminatorKind::GeneratorDrop
                     | TerminatorKind::FalseEdge { .. }
                     | TerminatorKind::FalseUnwind { .. } => unreachable!(),
diff --git a/compiler/rustc_codegen_gcc/example/alloc_system.rs b/compiler/rustc_codegen_gcc/example/alloc_system.rs
index fd01fcf1fc8..9ec18da90d8 100644
--- a/compiler/rustc_codegen_gcc/example/alloc_system.rs
+++ b/compiler/rustc_codegen_gcc/example/alloc_system.rs
@@ -1,12 +1,6 @@
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
+// SPDX-License-Identifier: MIT OR Apache-2.0
+// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org)
+
 #![no_std]
 #![feature(allocator_api, rustc_private)]
 #![cfg_attr(any(unix, target_os = "redox"), feature(libc))]
diff --git a/compiler/rustc_codegen_gcc/locales/en-US.ftl b/compiler/rustc_codegen_gcc/messages.ftl
index 0a94a08f8dc..0a94a08f8dc 100644
--- a/compiler/rustc_codegen_gcc/locales/en-US.ftl
+++ b/compiler/rustc_codegen_gcc/messages.ftl
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
index 1b7feb5f8a1..0b661505acc 100644
--- a/compiler/rustc_codegen_gcc/src/lib.rs
+++ b/compiler/rustc_codegen_gcc/src/lib.rs
@@ -87,7 +87,7 @@ use rustc_span::Symbol;
 use rustc_span::fatal_error::FatalError;
 use tempfile::TempDir;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 pub struct PrintOnPanic<F: Fn() -> String>(pub F);
 
diff --git a/compiler/rustc_codegen_llvm/locales/en-US.ftl b/compiler/rustc_codegen_llvm/messages.ftl
index e5df417370b..e5df417370b 100644
--- a/compiler/rustc_codegen_llvm/locales/en-US.ftl
+++ b/compiler/rustc_codegen_llvm/messages.ftl
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs
index 93419d27a62..978141917c6 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs
@@ -438,6 +438,7 @@ fn build_enum_variant_member_di_node<'ll, 'tcx>(
 ///         DW_TAG_structure_type            (type of variant 1)
 ///         DW_TAG_structure_type            (type of variant 2)
 ///         DW_TAG_structure_type            (type of variant 3)
+/// ```
 struct VariantMemberInfo<'a, 'll> {
     variant_index: VariantIdx,
     variant_name: Cow<'a, str>,
diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs
index c41e74c51a0..8dafe1b750b 100644
--- a/compiler/rustc_codegen_llvm/src/lib.rs
+++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -84,7 +84,7 @@ mod type_of;
 mod va_arg;
 mod value;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 #[derive(Clone)]
 pub struct LlvmCodegenBackend(());
diff --git a/compiler/rustc_codegen_ssa/locales/en-US.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index 8fe5f8d50ab..8fe5f8d50ab 100644
--- a/compiler/rustc_codegen_ssa/locales/en-US.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index 52c01b423a7..23e2b272410 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -720,6 +720,7 @@ impl<'a> Linker for GccLinker<'a> {
                 let mut arg = OsString::from("--version-script=");
                 arg.push(path);
                 self.linker_arg(arg);
+                self.linker_arg("--no-undefined-version");
             }
         }
     }
diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
index fd81b1c6fe1..7b58e55dbe8 100644
--- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
+++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
@@ -1,3 +1,5 @@
+use crate::base::allocator_kind_for_codegen;
+
 use std::collections::hash_map::Entry::*;
 
 use rustc_ast::expand::allocator::ALLOCATOR_METHODS;
@@ -58,7 +60,7 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap<
 
     let mut reachable_non_generics: DefIdMap<_> = tcx
         .reachable_set(())
-        .iter()
+        .items()
         .filter_map(|&def_id| {
             // We want to ignore some FFI functions that are not exposed from
             // this crate. Reachable FFI functions can be lumped into two
@@ -136,7 +138,7 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap<
             };
             (def_id.to_def_id(), info)
         })
-        .collect();
+        .into();
 
     if let Some(id) = tcx.proc_macro_decls_static(()) {
         reachable_non_generics.insert(
@@ -200,7 +202,8 @@ fn exported_symbols_provider_local(
         ));
     }
 
-    if tcx.allocator_kind(()).is_some() {
+    // Mark allocator shim symbols as exported only if they were generated.
+    if allocator_kind_for_codegen(tcx).is_some() {
         for symbol_name in ALLOCATOR_METHODS
             .iter()
             .map(|method| format!("__rust_{}", method.name))
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index 73179249bc4..abc510e360d 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -13,6 +13,7 @@ use crate::mir::place::PlaceRef;
 use crate::traits::*;
 use crate::{CachedModuleCodegen, CompiledModule, CrateInfo, MemFlags, ModuleCodegen, ModuleKind};
 
+use rustc_ast::expand::allocator::AllocatorKind;
 use rustc_attr as attr;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
@@ -545,6 +546,23 @@ pub fn collect_debugger_visualizers_transitive(
         .collect::<BTreeSet<_>>()
 }
 
+/// Decide allocator kind to codegen. If `Some(_)` this will be the same as
+/// `tcx.allocator_kind`, but it may be `None` in more cases (e.g. if using
+/// allocator definitions from a dylib dependency).
+pub fn allocator_kind_for_codegen(tcx: TyCtxt<'_>) -> Option<AllocatorKind> {
+    // If the crate doesn't have an `allocator_kind` set then there's definitely
+    // no shim to generate. Otherwise we also check our dependency graph for all
+    // our output crate types. If anything there looks like its a `Dynamic`
+    // linkage, then it's already got an allocator shim and we'll be using that
+    // one instead. If nothing exists then it's our job to generate the
+    // allocator!
+    let any_dynamic_crate = tcx.dependency_formats(()).iter().any(|(_, list)| {
+        use rustc_middle::middle::dependency_format::Linkage;
+        list.iter().any(|&linkage| linkage == Linkage::Dynamic)
+    });
+    if any_dynamic_crate { None } else { tcx.allocator_kind(()) }
+}
+
 pub fn codegen_crate<B: ExtraBackendMethods>(
     backend: B,
     tcx: TyCtxt<'_>,
@@ -615,20 +633,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
     );
 
     // Codegen an allocator shim, if necessary.
-    //
-    // If the crate doesn't have an `allocator_kind` set then there's definitely
-    // no shim to generate. Otherwise we also check our dependency graph for all
-    // our output crate types. If anything there looks like its a `Dynamic`
-    // linkage, then it's already got an allocator shim and we'll be using that
-    // one instead. If nothing exists then it's our job to generate the
-    // allocator!
-    let any_dynamic_crate = tcx.dependency_formats(()).iter().any(|(_, list)| {
-        use rustc_middle::middle::dependency_format::Linkage;
-        list.iter().any(|&linkage| linkage == Linkage::Dynamic)
-    });
-    let allocator_module = if any_dynamic_crate {
-        None
-    } else if let Some(kind) = tcx.allocator_kind(()) {
+    if let Some(kind) = allocator_kind_for_codegen(tcx) {
         let llmod_id =
             cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string();
         let module_llvm = tcx.sess.time("write_allocator_module", || {
@@ -642,13 +647,10 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
             )
         });
 
-        Some(ModuleCodegen { name: llmod_id, module_llvm, kind: ModuleKind::Allocator })
-    } else {
-        None
-    };
-
-    if let Some(allocator_module) = allocator_module {
-        ongoing_codegen.submit_pre_codegened_module_to_llvm(tcx, allocator_module);
+        ongoing_codegen.submit_pre_codegened_module_to_llvm(
+            tcx,
+            ModuleCodegen { name: llmod_id, module_llvm, kind: ModuleKind::Allocator },
+        );
     }
 
     // For better throughput during parallel processing by LLVM, we used to sort
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index c62968e5354..8b6bf886b0d 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -242,6 +242,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
                     // Note that this is also allowed if `actually_rustdoc` so
                     // if a target is documenting some wasm-specific code then
                     // it's not spuriously denied.
+                    //
+                    // This exception needs to be kept in sync with allowing
+                    // `#[target_feature]` on `main` and `start`.
                 } else if !tcx.features().target_feature_11 {
                     let mut err = feature_err(
                         &tcx.sess.parse_sess,
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index ebe9e50ffe6..8e721a84eaf 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -56,7 +56,7 @@ pub mod mono_item;
 pub mod target_features;
 pub mod traits;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 pub struct ModuleCodegen<M> {
     /// The name of the module. When the crate may be saved between
diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
index 95aad10fdb0..0ce395e912d 100644
--- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
@@ -295,7 +295,6 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
                 TerminatorKind::Call { cleanup: unwind, .. }
                 | TerminatorKind::InlineAsm { cleanup: unwind, .. }
                 | TerminatorKind::Assert { cleanup: unwind, .. }
-                | TerminatorKind::DropAndReplace { unwind, .. }
                 | TerminatorKind::Drop { unwind, .. } => {
                     if let Some(unwind) = unwind {
                         debug!(
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index 57a19a4ab1e..71c71d59b7a 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -1305,10 +1305,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     mergeable_succ(),
                 ),
 
-            mir::TerminatorKind::DropAndReplace { .. } => {
-                bug!("undesugared DropAndReplace in codegen: {:?}", terminator);
-            }
-
             mir::TerminatorKind::Call {
                 ref func,
                 ref args,
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index 2ec9fdbf44f..5cffca5230a 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -258,6 +258,10 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     // Apply debuginfo to the newly allocated locals.
     fx.debug_introduce_locals(&mut start_bx);
 
+    // The builders will be created separately for each basic block at `codegen_block`.
+    // So drop the builder of `start_llbb` to avoid having two at the same time.
+    drop(start_bx);
+
     // Codegen the body of each block using reverse postorder
     for (bb, _) in traversal::reverse_postorder(&mir) {
         fx.codegen_block(bb);
diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs
index 60fbceb344d..41f585f7fcc 100644
--- a/compiler/rustc_codegen_ssa/src/mir/statement.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs
@@ -92,6 +92,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             | mir::StatementKind::Retag { .. }
             | mir::StatementKind::AscribeUserType(..)
             | mir::StatementKind::ConstEvalCounter
+            | mir::StatementKind::PlaceMention(..)
             | mir::StatementKind::Nop => {}
         }
     }
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs
index 4301e4fe69b..754b085f1a8 100644
--- a/compiler/rustc_codegen_ssa/src/target_features.rs
+++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -1,7 +1,7 @@
 use rustc_ast::ast;
 use rustc_attr::InstructionSetAttr;
 use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::FxIndexSet;
 use rustc_errors::Applicability;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
@@ -192,7 +192,7 @@ const X86_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
     ("fxsr", None),
     ("gfni", Some(sym::avx512_target_feature)),
     ("lzcnt", None),
-    ("movbe", Some(sym::movbe_target_feature)),
+    ("movbe", None),
     ("pclmulqdq", None),
     ("popcnt", None),
     ("rdrand", None),
@@ -394,7 +394,6 @@ pub fn from_target_feature(
                 Some(sym::sse4a_target_feature) => rust_features.sse4a_target_feature,
                 Some(sym::tbm_target_feature) => rust_features.tbm_target_feature,
                 Some(sym::wasm_target_feature) => rust_features.wasm_target_feature,
-                Some(sym::movbe_target_feature) => rust_features.movbe_target_feature,
                 Some(sym::rtm_target_feature) => rust_features.rtm_target_feature,
                 Some(sym::ermsb_target_feature) => rust_features.ermsb_target_feature,
                 Some(sym::bpf_target_feature) => rust_features.bpf_target_feature,
@@ -418,7 +417,7 @@ pub fn from_target_feature(
 
 /// Computes the set of target features used in a function for the purposes of
 /// inline assembly.
-fn asm_target_features(tcx: TyCtxt<'_>, did: DefId) -> &FxHashSet<Symbol> {
+fn asm_target_features(tcx: TyCtxt<'_>, did: DefId) -> &FxIndexSet<Symbol> {
     let mut target_features = tcx.sess.unstable_target_features.clone();
     if tcx.def_kind(did).has_codegen_attrs() {
         let attrs = tcx.codegen_fn_attrs(did);
@@ -442,7 +441,7 @@ fn asm_target_features(tcx: TyCtxt<'_>, did: DefId) -> &FxHashSet<Symbol> {
 pub fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) {
     if let DefKind::AssocFn = tcx.def_kind(id) {
         let parent_id = tcx.local_parent(id);
-        if let DefKind::Impl { of_trait: true } = tcx.def_kind(parent_id) {
+        if let DefKind::Trait | DefKind::Impl { of_trait: true } = tcx.def_kind(parent_id) {
             tcx.sess
                 .struct_span_err(
                     attr_span,
diff --git a/compiler/rustc_const_eval/locales/en-US.ftl b/compiler/rustc_const_eval/messages.ftl
index 33bb116d6fa..33bb116d6fa 100644
--- a/compiler/rustc_const_eval/locales/en-US.ftl
+++ b/compiler/rustc_const_eval/messages.ftl
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index 6863435e508..9a366364e76 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -114,7 +114,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             Intrinsic(box intrinsic) => self.emulate_nondiverging_intrinsic(intrinsic)?,
 
             // Statements we do not track.
-            AscribeUserType(..) => {}
+            PlaceMention(..) | AscribeUserType(..) => {}
 
             // Currently, Miri discards Coverage statements. Coverage statements are only injected
             // via an optional compile time MIR pass and have no side effects. Since Coverage
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index 2aea7c79b6d..685a5599cde 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -171,11 +171,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             Unreachable => throw_ub!(Unreachable),
 
             // These should never occur for MIR we actually run.
-            DropAndReplace { .. }
-            | FalseEdge { .. }
-            | FalseUnwind { .. }
-            | Yield { .. }
-            | GeneratorDrop => span_bug!(
+            FalseEdge { .. } | FalseUnwind { .. } | Yield { .. } | GeneratorDrop => span_bug!(
                 terminator.source_info.span,
                 "{:#?} should have been eliminated by MIR pass",
                 terminator.kind
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index ed9efe568fb..16b83af91ac 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -39,7 +39,7 @@ use rustc_macros::fluent_messages;
 use rustc_middle::ty;
 use rustc_middle::ty::query::Providers;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 pub fn provide(providers: &mut Providers) {
     const_eval::provide(providers);
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index cc3aa84bd71..db55dbc2bfd 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -690,6 +690,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
             | StatementKind::StorageLive(_)
             | StatementKind::StorageDead(_)
             | StatementKind::Retag { .. }
+            | StatementKind::PlaceMention(..)
             | StatementKind::AscribeUserType(..)
             | StatementKind::Coverage(..)
             | StatementKind::Intrinsic(..)
@@ -769,7 +770,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
 
                         let errors = ocx.select_all_or_error();
                         if !errors.is_empty() {
-                            infcx.err_ctxt().report_fulfillment_errors(&errors, None);
+                            infcx.err_ctxt().report_fulfillment_errors(&errors);
                         }
                     }
 
@@ -985,8 +986,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
 
             // Forbid all `Drop` terminators unless the place being dropped is a local with no
             // projections that cannot be `NeedsNonConstDrop`.
-            TerminatorKind::Drop { place: dropped_place, .. }
-            | TerminatorKind::DropAndReplace { place: dropped_place, .. } => {
+            TerminatorKind::Drop { place: dropped_place, .. } => {
                 // If we are checking live drops after drop-elaboration, don't emit duplicate
                 // errors here.
                 if super::post_drop_elaboration::checking_enabled(self.ccx) {
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs
index cf4e875c91f..43806035a44 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs
@@ -80,8 +80,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
         trace!("visit_terminator: terminator={:?} location={:?}", terminator, location);
 
         match &terminator.kind {
-            mir::TerminatorKind::Drop { place: dropped_place, .. }
-            | mir::TerminatorKind::DropAndReplace { place: dropped_place, .. } => {
+            mir::TerminatorKind::Drop { place: dropped_place, .. } => {
                 let dropped_ty = dropped_place.ty(self.body, self.tcx).ty;
                 if !NeedsNonConstDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
                     // Instead of throwing a bug, we just return here. This is because we have to
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
index 805e6096b35..78c74e1892d 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
@@ -222,23 +222,8 @@ where
         // The effect of assignment to the return place in `TerminatorKind::Call` is not applied
         // here; that occurs in `apply_call_return_effect`.
 
-        if let mir::TerminatorKind::DropAndReplace { value, place, .. } = &terminator.kind {
-            let qualif = qualifs::in_operand::<Q, _>(
-                self.ccx,
-                &mut |l| self.state.qualif.contains(l),
-                value,
-            );
-
-            if !place.is_indirect() {
-                self.assign_qualif_direct(place, qualif);
-            }
-        }
-
         // We ignore borrow on drop because custom drop impls are not allowed in consts.
         // FIXME: Reconsider if accounting for borrows in drops is necessary for const drop.
-
-        // We need to assign qualifs to the dropped location before visiting the operand that
-        // replaces it since qualifs can be cleared on move.
         self.super_terminator(terminator, location);
     }
 }
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 272fe3d1b31..49b1e6d967c 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -679,6 +679,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     }
                 }
             }
+            StatementKind::PlaceMention(..) => {
+                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
+                    self.fail(
+                        location,
+                        "`PlaceMention` should have been removed after drop lowering phase",
+                    );
+                }
+            }
             StatementKind::AscribeUserType(..) => {
                 if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
                     self.fail(
@@ -846,18 +854,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     self.check_edge(location, *unwind, EdgeKind::Unwind);
                 }
             }
-            TerminatorKind::DropAndReplace { target, unwind, .. } => {
-                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
-                    self.fail(
-                        location,
-                        "`DropAndReplace` should have been removed during drop elaboration",
-                    );
-                }
-                self.check_edge(location, *target, EdgeKind::Normal);
-                if let Some(unwind) = unwind {
-                    self.check_edge(location, *unwind, EdgeKind::Unwind);
-                }
-            }
             TerminatorKind::Call { func, args, destination, target, cleanup, .. } => {
                 let func_ty = func.ty(&self.body.local_decls, self.tcx);
                 match func_ty.kind() {
diff --git a/compiler/rustc_data_structures/src/map_in_place.rs b/compiler/rustc_data_structures/src/flat_map_in_place.rs
index a0d4b7ade1f..f58844f2817 100644
--- a/compiler/rustc_data_structures/src/map_in_place.rs
+++ b/compiler/rustc_data_structures/src/flat_map_in_place.rs
@@ -2,14 +2,7 @@ use smallvec::{Array, SmallVec};
 use std::ptr;
 use thin_vec::ThinVec;
 
-pub trait MapInPlace<T>: Sized {
-    fn map_in_place<F>(&mut self, mut f: F)
-    where
-        F: FnMut(T) -> T,
-    {
-        self.flat_map_in_place(|e| Some(f(e)))
-    }
-
+pub trait FlatMapInPlace<T>: Sized {
     fn flat_map_in_place<F, I>(&mut self, f: F)
     where
         F: FnMut(T) -> I,
@@ -66,14 +59,14 @@ macro_rules! flat_map_in_place {
     };
 }
 
-impl<T> MapInPlace<T> for Vec<T> {
+impl<T> FlatMapInPlace<T> for Vec<T> {
     flat_map_in_place!();
 }
 
-impl<T, A: Array<Item = T>> MapInPlace<T> for SmallVec<A> {
+impl<T, A: Array<Item = T>> FlatMapInPlace<T> for SmallVec<A> {
     flat_map_in_place!();
 }
 
-impl<T> MapInPlace<T> for ThinVec<T> {
+impl<T> FlatMapInPlace<T> for ThinVec<T> {
     flat_map_in_place!();
 }
diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs
index a94e52fdfe6..c595bf830a3 100644
--- a/compiler/rustc_data_structures/src/lib.rs
+++ b/compiler/rustc_data_structures/src/lib.rs
@@ -50,6 +50,7 @@ pub fn cold_path<F: FnOnce() -> R, R>(f: F) -> R {
 pub mod base_n;
 pub mod binary_search_util;
 pub mod captures;
+pub mod flat_map_in_place;
 pub mod flock;
 pub mod functor;
 pub mod fx;
@@ -57,7 +58,6 @@ pub mod graph;
 pub mod intern;
 pub mod jobserver;
 pub mod macros;
-pub mod map_in_place;
 pub mod obligation_forest;
 pub mod owning_ref;
 pub mod sip128;
diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs
index e0d77cdaebb..de9842156d6 100644
--- a/compiler/rustc_data_structures/src/stable_hasher.rs
+++ b/compiler/rustc_data_structures/src/stable_hasher.rs
@@ -617,18 +617,10 @@ where
     }
 }
 
-impl<K, R, HCX> HashStable<HCX> for ::std::collections::HashSet<K, R>
-where
-    K: ToStableHashKey<HCX> + Eq,
-    R: BuildHasher,
-{
-    fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
-        stable_hash_reduce(hcx, hasher, self.iter(), self.len(), |hasher, hcx, key| {
-            let key = key.to_stable_hash_key(hcx);
-            key.hash_stable(hcx, hasher);
-        });
-    }
-}
+// It is not safe to implement HashStable for HashSet or any other collection type
+// with unstable but observable iteration order.
+// See https://github.com/rust-lang/compiler-team/issues/533 for further information.
+impl<V, HCX> !HashStable<HCX> for std::collections::HashSet<V> {}
 
 impl<K, V, HCX> HashStable<HCX> for ::std::collections::BTreeMap<K, V>
 where
diff --git a/compiler/rustc_data_structures/src/unord.rs b/compiler/rustc_data_structures/src/unord.rs
index f35f18e51cb..5c2435a0122 100644
--- a/compiler/rustc_data_structures/src/unord.rs
+++ b/compiler/rustc_data_structures/src/unord.rs
@@ -109,6 +109,12 @@ impl<T, I: Iterator<Item = T>> UnordItems<T, I> {
     }
 }
 
+impl<T> UnordItems<T, std::iter::Empty<T>> {
+    pub fn empty() -> Self {
+        UnordItems(std::iter::empty())
+    }
+}
+
 impl<'a, T: Clone + 'a, I: Iterator<Item = &'a T>> UnordItems<&'a T, I> {
     #[inline]
     pub fn cloned(self) -> UnordItems<T, impl Iterator<Item = T>> {
@@ -133,6 +139,20 @@ impl<T: Ord, I: Iterator<Item = T>> UnordItems<T, I> {
         items
     }
 
+    #[inline]
+    pub fn into_sorted_stable_ord(self, use_stable_sort: bool) -> Vec<T>
+    where
+        T: Ord + StableOrd,
+    {
+        let mut items: Vec<T> = self.0.collect();
+        if use_stable_sort {
+            items.sort();
+        } else {
+            items.sort_unstable()
+        }
+        items
+    }
+
     pub fn into_sorted_small_vec<HCX, const LEN: usize>(self, hcx: &HCX) -> SmallVec<[T; LEN]>
     where
         T: ToStableHashKey<HCX>,
@@ -176,6 +196,11 @@ impl<V: Eq + Hash> UnordSet<V> {
     }
 
     #[inline]
+    pub fn is_empty(&self) -> bool {
+        self.inner.is_empty()
+    }
+
+    #[inline]
     pub fn insert(&mut self, v: V) -> bool {
         self.inner.insert(v)
     }
@@ -253,7 +278,7 @@ impl<V: Eq + Hash> UnordSet<V> {
     // We can safely extend this UnordSet from a set of unordered values because that
     // won't expose the internal ordering anywhere.
     #[inline]
-    pub fn extend<I: Iterator<Item = V>>(&mut self, items: UnordItems<V, I>) {
+    pub fn extend_unord<I: Iterator<Item = V>>(&mut self, items: UnordItems<V, I>) {
         self.inner.extend(items.0)
     }
 
@@ -277,6 +302,12 @@ impl<V: Hash + Eq> FromIterator<V> for UnordSet<V> {
     }
 }
 
+impl<V: Hash + Eq> From<FxHashSet<V>> for UnordSet<V> {
+    fn from(value: FxHashSet<V>) -> Self {
+        UnordSet { inner: value }
+    }
+}
+
 impl<HCX, V: Hash + Eq + HashStable<HCX>> HashStable<HCX> for UnordSet<V> {
     #[inline]
     fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
diff --git a/compiler/rustc_driver_impl/locales/en-US.ftl b/compiler/rustc_driver_impl/messages.ftl
index f19b1ff6426..f19b1ff6426 100644
--- a/compiler/rustc_driver_impl/locales/en-US.ftl
+++ b/compiler/rustc_driver_impl/messages.ftl
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 464ddae476a..555917c8b5e 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -64,7 +64,7 @@ use crate::session_diagnostics::{
     RLinkWrongFileType, RlinkNotAFile, RlinkUnableToRead,
 };
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[
     // tidy-alphabetical-start
@@ -331,6 +331,7 @@ fn run_compiler(
             if let Some(ppm) = &sess.opts.pretty {
                 if ppm.needs_ast_map() {
                     queries.global_ctxt()?.enter(|tcx| {
+                        tcx.ensure().early_lint_checks(());
                         pretty::print_after_hir_lowering(tcx, *ppm);
                         Ok(())
                     })?;
diff --git a/compiler/rustc_error_codes/src/error_codes/E0368.md b/compiler/rustc_error_codes/src/error_codes/E0368.md
index 7b9d9334821..b18e8758d71 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0368.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0368.md
@@ -41,7 +41,7 @@ impl Add for Foo {
 
 fn main() {
     let mut x: Foo = Foo(5);
-    x += Foo(7); // error, `+= cannot be applied to the type `Foo`
+    x += Foo(7); // error, `+=` cannot be applied to the type `Foo`
 }
 ```
 
diff --git a/compiler/rustc_error_codes/src/error_codes/E0710.md b/compiler/rustc_error_codes/src/error_codes/E0710.md
index b7037ea611b..84d55d52426 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0710.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0710.md
@@ -3,14 +3,14 @@ An unknown tool name was found in a scoped lint.
 Erroneous code examples:
 
 ```compile_fail,E0710
-#[allow(clipp::filter_map)] // error!`
+#[allow(clipp::filter_map)] // error!
 fn main() {
     // business logic
 }
 ```
 
 ```compile_fail,E0710
-#[warn(clipp::filter_map)] // error!`
+#[warn(clipp::filter_map)] // error!
 fn main() {
     // business logic
 }
diff --git a/compiler/rustc_error_messages/locales/en-US.ftl b/compiler/rustc_error_messages/messages.ftl
index e6292374448..e6292374448 100644
--- a/compiler/rustc_error_messages/locales/en-US.ftl
+++ b/compiler/rustc_error_messages/messages.ftl
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index 010e5f060bf..301dacc2824 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -34,7 +34,7 @@ use intl_memoizer::IntlLangMemoizer;
 pub use fluent_bundle::{self, types::FluentType, FluentArgs, FluentError, FluentValue};
 pub use unic_langid::{langid, LanguageIdentifier};
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 pub type FluentBundle = fluent_bundle::bundle::FluentBundle<FluentResource, IntlLangMemoizer>;
 
diff --git a/compiler/rustc_errors/locales/en-US.ftl b/compiler/rustc_errors/messages.ftl
index dde1d6c0a81..dde1d6c0a81 100644
--- a/compiler/rustc_errors/locales/en-US.ftl
+++ b/compiler/rustc_errors/messages.ftl
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 99af872f07f..bab4f31e777 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -76,7 +76,7 @@ pub use snippet::Style;
 pub type PErr<'a> = DiagnosticBuilder<'a, ErrorGuaranteed>;
 pub type PResult<'a, T> = Result<T, PErr<'a>>;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 // `PResult` is used a lot. Make sure it doesn't unintentionally get bigger.
 // (See also the comment on `DiagnosticBuilderInner`'s `diagnostic` field.)
diff --git a/compiler/rustc_expand/locales/en-US.ftl b/compiler/rustc_expand/messages.ftl
index cfae781bdee..cfae781bdee 100644
--- a/compiler/rustc_expand/locales/en-US.ftl
+++ b/compiler/rustc_expand/messages.ftl
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 22bc90f5cac..713e4fbbdce 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -12,13 +12,13 @@ use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::visit::{AssocCtxt, Visitor};
 use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind};
 use rustc_attr::{self as attr, Deprecation, Stability};
-use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
+use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::sync::{self, Lrc};
 use rustc_errors::{
     Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, MultiSpan, PResult,
 };
 use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT;
-use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics};
+use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, RegisteredTools};
 use rustc_parse::{self, parser, MACRO_ARGUMENTS};
 use rustc_session::errors::report_lit_error;
 use rustc_session::{parse::ParseSess, Limit, Session};
@@ -947,14 +947,14 @@ pub trait ResolverExpand {
     fn declare_proc_macro(&mut self, id: NodeId);
 
     /// Tools registered with `#![register_tool]` and used by tool attributes and lints.
-    fn registered_tools(&self) -> &FxHashSet<Ident>;
+    fn registered_tools(&self) -> &RegisteredTools;
 }
 
 pub trait LintStoreExpand {
     fn pre_expansion_lint(
         &self,
         sess: &Session,
-        registered_tools: &FxHashSet<Ident>,
+        registered_tools: &RegisteredTools,
         node_id: NodeId,
         attrs: &[Attribute],
         items: &[P<Item>],
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index 01500c2c77c..d6cb173ba9b 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -12,8 +12,8 @@ use rustc_ast::tokenstream::{LazyAttrTokenStream, TokenTree};
 use rustc_ast::NodeId;
 use rustc_ast::{self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem};
 use rustc_attr as attr;
+use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
 use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::map_in_place::MapInPlace;
 use rustc_feature::{Feature, Features, State as FeatureState};
 use rustc_feature::{
     ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES, STABLE_REMOVED_FEATURES,
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 79d058d9c97..4092a192e0c 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -20,7 +20,7 @@ use rustc_ast::{ForeignItemKind, HasAttrs, HasNodeId};
 use rustc_ast::{Inline, ItemKind, MacStmtStyle, MetaItemKind, ModKind};
 use rustc_ast::{NestedMetaItem, NodeId, PatKind, StmtKind, TyKind};
 use rustc_ast_pretty::pprust;
-use rustc_data_structures::map_in_place::MapInPlace;
+use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::PResult;
 use rustc_feature::Features;
diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs
index 634e206e58a..ced7531c3fe 100644
--- a/compiler/rustc_expand/src/lib.rs
+++ b/compiler/rustc_expand/src/lib.rs
@@ -64,4 +64,4 @@ mod mut_visit {
     mod tests;
 }
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs
index f469b2daef5..b1d9cea2773 100644
--- a/compiler/rustc_expand/src/mbe/diagnostics.rs
+++ b/compiler/rustc_expand/src/mbe/diagnostics.rs
@@ -1,12 +1,10 @@
-use std::borrow::Cow;
-
 use crate::base::{DummyResult, ExtCtxt, MacResult};
 use crate::expand::{parse_ast_fragment, AstFragmentKind};
 use crate::mbe::{
     macro_parser::{MatcherLoc, NamedParseResult, ParseResult::*, TtParser},
     macro_rules::{try_match_macro, Tracker},
 };
-use rustc_ast::token::{self, Token};
+use rustc_ast::token::{self, Token, TokenKind};
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast_pretty::pprust;
 use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage};
@@ -14,6 +12,7 @@ use rustc_parse::parser::{Parser, Recovery};
 use rustc_span::source_map::SourceMap;
 use rustc_span::symbol::Ident;
 use rustc_span::Span;
+use std::borrow::Cow;
 
 use super::macro_rules::{parser_from_cx, NoopTracker};
 
@@ -63,6 +62,13 @@ pub(super) fn failed_to_match_macro<'cx>(
         err.note(format!("while trying to match {remaining_matcher}"));
     }
 
+    if let MatcherLoc::Token { token: expected_token } = &remaining_matcher
+        && (matches!(expected_token.kind, TokenKind::Interpolated(_))
+            || matches!(token.kind, TokenKind::Interpolated(_)))
+    {
+        err.note("captured metavariables except for `$tt`, `$ident` and `$lifetime` cannot be compared to other tokens");
+    }
+
     // Check whether there's a missing comma in this macro call, like `println!("{}" a);`
     if let Some((arg, comma_span)) = arg.add_comma() {
         for lhs in lhses {
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs
index e7b2df34ccc..ac93dd555b7 100644
--- a/compiler/rustc_feature/src/accepted.rs
+++ b/compiler/rustc_feature/src/accepted.rs
@@ -90,7 +90,7 @@ declare_features! (
     (accepted, clone_closures, "1.26.0", Some(44490), None),
     /// Allows coercing non capturing closures to function pointers.
     (accepted, closure_to_fn_coercion, "1.19.0", Some(39817), None),
-    /// Allows using `cmpxchg16b` from `core::arch::x86_64`.
+    /// Allows using the CMPXCHG16B target feature.
     (accepted, cmpxchg16b_target_feature, "CURRENT_RUSTC_VERSION", Some(44839), None),
     /// Allows usage of the `compile_error!` macro.
     (accepted, compile_error, "1.20.0", Some(40872), None),
@@ -238,6 +238,8 @@ declare_features! (
     (accepted, min_const_unsafe_fn, "1.33.0", Some(55607), None),
     /// Allows using `Self` and associated types in struct expressions and patterns.
     (accepted, more_struct_aliases, "1.16.0", Some(37544), None),
+    /// Allows using the MOVBE target feature.
+    (accepted, movbe_target_feature, "CURRENT_RUSTC_VERSION", Some(44839), None),
     /// Allows patterns with concurrent by-move and by-ref bindings.
     /// For example, you can write `Foo(a, ref b)` where `a` is by-move and `b` is by-ref.
     (accepted, move_ref_pattern, "1.49.0", Some(68354), None),
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index a01914f969e..2fa7bceb8bc 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -259,7 +259,6 @@ declare_features! (
     (active, ermsb_target_feature, "1.49.0", Some(44839), None),
     (active, hexagon_target_feature, "1.27.0", Some(44839), None),
     (active, mips_target_feature, "1.27.0", Some(44839), None),
-    (active, movbe_target_feature, "1.34.0", Some(44839), None),
     (active, powerpc_target_feature, "1.27.0", Some(44839), None),
     (active, riscv_target_feature, "1.45.0", Some(44839), None),
     (active, rtm_target_feature, "1.35.0", Some(44839), None),
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 19d3d41c984..f00a5f24e45 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -815,12 +815,13 @@ pub struct ParentedNode<'tcx> {
 #[derive(Debug)]
 pub struct AttributeMap<'tcx> {
     pub map: SortedMap<ItemLocalId, &'tcx [Attribute]>,
-    pub hash: Fingerprint,
+    // Only present when the crate hash is needed.
+    pub opt_hash: Option<Fingerprint>,
 }
 
 impl<'tcx> AttributeMap<'tcx> {
     pub const EMPTY: &'static AttributeMap<'static> =
-        &AttributeMap { map: SortedMap::new(), hash: Fingerprint::ZERO };
+        &AttributeMap { map: SortedMap::new(), opt_hash: Some(Fingerprint::ZERO) };
 
     #[inline]
     pub fn get(&self, id: ItemLocalId) -> &'tcx [Attribute] {
@@ -832,10 +833,9 @@ impl<'tcx> AttributeMap<'tcx> {
 /// These nodes are mapped by `ItemLocalId` alongside the index of their parent node.
 /// The HIR tree, including bodies, is pre-hashed.
 pub struct OwnerNodes<'tcx> {
-    /// Pre-computed hash of the full HIR.
-    pub hash_including_bodies: Fingerprint,
-    /// Pre-computed hash of the item signature, without recursing into the body.
-    pub hash_without_bodies: Fingerprint,
+    /// Pre-computed hash of the full HIR. Used in the crate hash. Only present
+    /// when incr. comp. is enabled.
+    pub opt_hash_including_bodies: Option<Fingerprint>,
     /// Full HIR for the current owner.
     // The zeroth node's parent should never be accessed: the owner's parent is computed by the
     // hir_owner_parent query. It is set to `ItemLocalId::INVALID` to force an ICE if accidentally
@@ -872,8 +872,7 @@ impl fmt::Debug for OwnerNodes<'_> {
                     .collect::<Vec<_>>(),
             )
             .field("bodies", &self.bodies)
-            .field("hash_without_bodies", &self.hash_without_bodies)
-            .field("hash_including_bodies", &self.hash_including_bodies)
+            .field("opt_hash_including_bodies", &self.opt_hash_including_bodies)
             .finish()
     }
 }
@@ -940,7 +939,8 @@ impl<T> MaybeOwner<T> {
 #[derive(Debug)]
 pub struct Crate<'hir> {
     pub owners: IndexVec<LocalDefId, MaybeOwner<&'hir OwnerInfo<'hir>>>,
-    pub hir_hash: Fingerprint,
+    // Only present when incr. comp. is enabled.
+    pub opt_hir_hash: Option<Fingerprint>,
 }
 
 #[derive(Debug, HashStable_Generic)]
diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs
index 85d0e02d0b6..97fa710b354 100644
--- a/compiler/rustc_hir/src/stable_hash_impls.rs
+++ b/compiler/rustc_hir/src/stable_hash_impls.rs
@@ -100,24 +100,23 @@ impl<'tcx, HirCtx: crate::HashStableContext> HashStable<HirCtx> for OwnerNodes<'
         // `local_id_to_def_id` is also ignored because is dependent on the body, then just hashing
         // the body satisfies the condition of two nodes being different have different
         // `hash_stable` results.
-        let OwnerNodes { hash_including_bodies, hash_without_bodies: _, nodes: _, bodies: _ } =
-            *self;
-        hash_including_bodies.hash_stable(hcx, hasher);
+        let OwnerNodes { opt_hash_including_bodies, nodes: _, bodies: _ } = *self;
+        opt_hash_including_bodies.unwrap().hash_stable(hcx, hasher);
     }
 }
 
 impl<'tcx, HirCtx: crate::HashStableContext> HashStable<HirCtx> for AttributeMap<'tcx> {
     fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
-        // We ignore the `map` since it refers to information included in `hash` which is hashed in
-        // the collector and used for the crate hash.
-        let AttributeMap { hash, map: _ } = *self;
-        hash.hash_stable(hcx, hasher);
+        // We ignore the `map` since it refers to information included in `opt_hash` which is
+        // hashed in the collector and used for the crate hash.
+        let AttributeMap { opt_hash, map: _ } = *self;
+        opt_hash.unwrap().hash_stable(hcx, hasher);
     }
 }
 
 impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for Crate<'_> {
     fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
-        let Crate { owners: _, hir_hash } = self;
-        hir_hash.hash_stable(hcx, hasher)
+        let Crate { owners: _, opt_hir_hash } = self;
+        opt_hir_hash.unwrap().hash_stable(hcx, hasher)
     }
 }
diff --git a/compiler/rustc_hir_analysis/locales/en-US.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 50e857ef60d..0105cbf36de 100644
--- a/compiler/rustc_hir_analysis/locales/en-US.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -42,6 +42,9 @@ hir_analysis_assoc_type_binding_not_allowed =
     associated type bindings are not allowed here
     .label = associated type not allowed here
 
+hir_analysis_parenthesized_fn_trait_expansion =
+    parenthesized trait syntax expands to `{$expanded_type}`
+
 hir_analysis_typeof_reserved_keyword_used =
     `typeof` is a reserved keyword but unimplemented
     .suggestion = consider replacing `typeof(...)` with an actual type
@@ -89,14 +92,14 @@ hir_analysis_missing_type_params =
     .note = because of the default `Self` reference, type parameters must be specified on object types
 
 hir_analysis_copy_impl_on_type_with_dtor =
-    the trait `Copy` may not be implemented for this type; the type has a destructor
+    the trait `Copy` cannot be implemented for this type; the type has a destructor
     .label = `Copy` not allowed on types with destructors
 
 hir_analysis_multiple_relaxed_default_bounds =
     type parameter has more than one relaxed default bound, only one is supported
 
 hir_analysis_copy_impl_on_non_adt =
-    the trait `Copy` may not be implemented for this type
+    the trait `Copy` cannot be implemented for this type
     .label = type is not a structure or enumeration
 
 hir_analysis_const_impl_for_non_const_trait =
@@ -125,9 +128,14 @@ hir_analysis_where_clause_on_main = `main` function is not allowed to have a `wh
 hir_analysis_track_caller_on_main = `main` function is not allowed to be `#[track_caller]`
     .suggestion = remove this annotation
 
+hir_analysis_target_feature_on_main = `main` function is not allowed to have `#[target_feature]`
+
 hir_analysis_start_not_track_caller = `start` is not allowed to be `#[track_caller]`
     .label = `start` is not allowed to be `#[track_caller]`
 
+hir_analysis_start_not_target_feature = `start` is not allowed to have `#[target_feature]`
+    .label = `start` is not allowed to have `#[target_feature]`
+
 hir_analysis_start_not_async = `start` is not allowed to be `async`
     .label = `start` is not allowed to be `async`
 
@@ -163,3 +171,10 @@ hir_analysis_pass_to_variadic_function = can't pass `{$ty}` to variadic function
     .help = cast the value to `{$cast_ty}`
 
 hir_analysis_cast_thin_pointer_to_fat_pointer = cannot cast thin pointer `{$expr_ty}` to fat pointer `{$cast_ty}`
+
+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<...>`
+
+hir_analysis_invalid_union_field_sugg =
+    wrap the field type in `ManuallyDrop<...>`
diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs
index c49e4d9d581..156334fe785 100644
--- a/compiler/rustc_hir_analysis/src/astconv/errors.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs
@@ -1,10 +1,14 @@
 use crate::astconv::AstConv;
-use crate::errors::{ManualImplementation, MissingTypeParams};
+use crate::errors::{
+    AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams,
+    ParenthesizedFnTraitExpansion,
+};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_infer::traits::FulfillmentError;
+use rustc_middle::ty::TyCtxt;
 use rustc_middle::ty::{self, Ty};
 use rustc_session::parse::feature_err;
 use rustc_span::edit_distance::find_best_match_for_name;
@@ -78,43 +82,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             // Do not suggest the other syntax if we are in trait impl:
             // the desugaring would contain an associated type constraint.
             if !is_impl {
-                let args = trait_segment
-                    .args
-                    .as_ref()
-                    .and_then(|args| args.args.get(0))
-                    .and_then(|arg| match arg {
-                        hir::GenericArg::Type(ty) => match ty.kind {
-                            hir::TyKind::Tup(t) => t
-                                .iter()
-                                .map(|e| sess.source_map().span_to_snippet(e.span))
-                                .collect::<Result<Vec<_>, _>>()
-                                .map(|a| a.join(", ")),
-                            _ => sess.source_map().span_to_snippet(ty.span),
-                        }
-                        .map(|s| format!("({})", s))
-                        .ok(),
-                        _ => None,
-                    })
-                    .unwrap_or_else(|| "()".to_string());
-                let ret = trait_segment
-                    .args()
-                    .bindings
-                    .iter()
-                    .find_map(|b| match (b.ident.name == sym::Output, &b.kind) {
-                        (true, hir::TypeBindingKind::Equality { term }) => {
-                            let span = match term {
-                                hir::Term::Ty(ty) => ty.span,
-                                hir::Term::Const(c) => self.tcx().hir().span(c.hir_id),
-                            };
-                            sess.source_map().span_to_snippet(span).ok()
-                        }
-                        _ => None,
-                    })
-                    .unwrap_or_else(|| "()".to_string());
                 err.span_suggestion(
                     span,
                     "use parenthetical notation instead",
-                    format!("{}{} -> {}", trait_segment.ident, args, ret),
+                    fn_trait_to_string(self.tcx(), trait_segment, true),
                     Applicability::MaybeIncorrect,
                 );
             }
@@ -629,3 +600,69 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         err.emit();
     }
 }
+
+/// Emits an error regarding forbidden type binding associations
+pub fn prohibit_assoc_ty_binding(
+    tcx: TyCtxt<'_>,
+    span: Span,
+    segment: Option<(&hir::PathSegment<'_>, Span)>,
+) {
+    tcx.sess.emit_err(AssocTypeBindingNotAllowed { span, fn_trait_expansion: if let Some((segment, span)) = segment && segment.args().parenthesized {
+        Some(ParenthesizedFnTraitExpansion { span, expanded_type: fn_trait_to_string(tcx, segment, false) })
+    } else {
+        None
+    }});
+}
+
+pub(crate) fn fn_trait_to_string(
+    tcx: TyCtxt<'_>,
+    trait_segment: &hir::PathSegment<'_>,
+    parenthesized: bool,
+) -> String {
+    let args = trait_segment
+        .args
+        .as_ref()
+        .and_then(|args| args.args.get(0))
+        .and_then(|arg| match arg {
+            hir::GenericArg::Type(ty) => match ty.kind {
+                hir::TyKind::Tup(t) => t
+                    .iter()
+                    .map(|e| tcx.sess.source_map().span_to_snippet(e.span))
+                    .collect::<Result<Vec<_>, _>>()
+                    .map(|a| a.join(", ")),
+                _ => tcx.sess.source_map().span_to_snippet(ty.span),
+            }
+            .map(|s| {
+                // `s.empty()` checks to see if the type is the unit tuple, if so we don't want a comma
+                if parenthesized || s.is_empty() { format!("({})", s) } else { format!("({},)", s) }
+            })
+            .ok(),
+            _ => None,
+        })
+        .unwrap_or_else(|| "()".to_string());
+
+    let ret = trait_segment
+        .args()
+        .bindings
+        .iter()
+        .find_map(|b| match (b.ident.name == sym::Output, &b.kind) {
+            (true, hir::TypeBindingKind::Equality { term }) => {
+                let span = match term {
+                    hir::Term::Ty(ty) => ty.span,
+                    hir::Term::Const(c) => tcx.hir().span(c.hir_id),
+                };
+
+                (span != tcx.hir().span(trait_segment.hir_id))
+                    .then_some(tcx.sess.source_map().span_to_snippet(span).ok())
+                    .flatten()
+            }
+            _ => None,
+        })
+        .unwrap_or_else(|| "()".to_string());
+
+    if parenthesized {
+        format!("{}{} -> {}", trait_segment.ident, args, ret)
+    } else {
+        format!("{}<{}, Output={}>", trait_segment.ident, args, ret)
+    }
+}
diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs
index 7f6518ffd71..2f4963f6bc3 100644
--- a/compiler/rustc_hir_analysis/src/astconv/generics.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs
@@ -1,9 +1,8 @@
 use super::IsMethodCall;
 use crate::astconv::{
-    CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch,
-    GenericArgCountResult, GenericArgPosition,
+    errors::prohibit_assoc_ty_binding, CreateSubstsForGenericArgsCtxt, ExplicitLateBound,
+    GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition,
 };
-use crate::errors::AssocTypeBindingNotAllowed;
 use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs};
 use rustc_ast::ast::ParamKindOrd;
 use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
@@ -433,7 +432,7 @@ pub(crate) fn check_generic_arg_count(
         (gen_pos != GenericArgPosition::Type || infer_args) && !gen_args.has_lifetime_params();
 
     if gen_pos != GenericArgPosition::Type && let Some(b) = gen_args.bindings.first() {
-            prohibit_assoc_ty_binding(tcx, b.span);
+             prohibit_assoc_ty_binding(tcx, b.span, None);
         }
 
     let explicit_late_bound =
@@ -589,11 +588,6 @@ pub(crate) fn check_generic_arg_count(
     }
 }
 
-/// Emits an error regarding forbidden type binding associations
-pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) {
-    tcx.sess.emit_err(AssocTypeBindingNotAllowed { span });
-}
-
 /// Prohibits explicit lifetime arguments if late-bound lifetime parameters
 /// are present. This is used both for datatypes and function calls.
 pub(crate) fn prohibit_explicit_late_bound_lifetimes(
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index 899029d98e0..3cd4c4afe86 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -5,9 +5,8 @@
 mod errors;
 pub mod generics;
 
-use crate::astconv::generics::{
-    check_generic_arg_count, create_substs_for_generic_args, prohibit_assoc_ty_binding,
-};
+use crate::astconv::errors::prohibit_assoc_ty_binding;
+use crate::astconv::generics::{check_generic_arg_count, create_substs_for_generic_args};
 use crate::bounds::Bounds;
 use crate::collect::HirPlaceholderCollector;
 use crate::errors::{
@@ -295,7 +294,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             ty::BoundConstness::NotConst,
         );
         if let Some(b) = item_segment.args().bindings.first() {
-            prohibit_assoc_ty_binding(self.tcx(), b.span);
+            prohibit_assoc_ty_binding(self.tcx(), b.span, Some((item_segment, span)));
         }
 
         substs
@@ -631,7 +630,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         );
 
         if let Some(b) = item_segment.args().bindings.first() {
-            prohibit_assoc_ty_binding(self.tcx(), b.span);
+            prohibit_assoc_ty_binding(self.tcx(), b.span, Some((item_segment, span)));
         }
 
         args
@@ -825,7 +824,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             constness,
         );
         if let Some(b) = trait_segment.args().bindings.first() {
-            prohibit_assoc_ty_binding(self.tcx(), b.span);
+            prohibit_assoc_ty_binding(self.tcx(), b.span, Some((trait_segment, span)));
         }
         self.tcx().mk_trait_ref(trait_def_id, substs)
     }
@@ -2596,7 +2595,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         for segment in segments {
             // Only emit the first error to avoid overloading the user with error messages.
             if let Some(b) = segment.args().bindings.first() {
-                prohibit_assoc_ty_binding(self.tcx(), b.span);
+                prohibit_assoc_ty_binding(self.tcx(), b.span, None);
                 return true;
             }
         }
@@ -3049,10 +3048,18 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             }
             &hir::TyKind::OpaqueDef(item_id, lifetimes, in_trait) => {
                 let opaque_ty = tcx.hir().item(item_id);
-                let def_id = item_id.owner_id.to_def_id();
 
                 match opaque_ty.kind {
                     hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => {
+                        let local_def_id = item_id.owner_id.def_id;
+                        // If this is an RPITIT and we are using the new RPITIT lowering scheme, we
+                        // generate the def_id of an associated type for the trait and return as
+                        // type a projection.
+                        let def_id = if in_trait && tcx.lower_impl_trait_in_trait_to_assoc_ty() {
+                            tcx.associated_item_for_impl_trait_in_trait(local_def_id).to_def_id()
+                        } else {
+                            local_def_id.to_def_id()
+                        };
                         self.impl_trait_ty_to_ty(def_id, lifetimes, origin, in_trait)
                     }
                     ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i),
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 3449d3d439d..14dc9d89180 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -1,5 +1,5 @@
 use crate::check::intrinsicck::InlineAsmCtxt;
-use crate::errors::LinkageType;
+use crate::errors::{self, LinkageType};
 
 use super::compare_impl_item::check_type_bounds;
 use super::compare_impl_item::{compare_impl_method, compare_impl_ty};
@@ -114,9 +114,11 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
                     allowed_union_field(*elem, tcx, param_env)
                 }
                 _ => {
-                    // Fallback case: allow `ManuallyDrop` and things that are `Copy`.
+                    // Fallback case: allow `ManuallyDrop` and things that are `Copy`,
+                    // also no need to report an error if the type is unresolved.
                     ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop())
                         || ty.is_copy_modulo_regions(tcx, param_env)
+                        || ty.references_error()
                 }
             }
         }
@@ -131,26 +133,14 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
                     Some(Node::Field(field)) => (field.span, field.ty.span),
                     _ => unreachable!("mir field has to correspond to hir field"),
                 };
-                struct_span_err!(
-                    tcx.sess,
+                tcx.sess.emit_err(errors::InvalidUnionField {
                     field_span,
-                    E0740,
-                    "unions cannot contain fields that may need dropping"
-                )
-                .note(
-                    "a type is guaranteed not to need dropping \
-                    when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type",
-                )
-                .multipart_suggestion_verbose(
-                    "when the type does not implement `Copy`, \
-                    wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped",
-                    vec![
-                        (ty_span.shrink_to_lo(), "std::mem::ManuallyDrop<".into()),
-                        (ty_span.shrink_to_hi(), ">".into()),
-                    ],
-                    Applicability::MaybeIncorrect,
-                )
-                .emit();
+                    sugg: errors::InvalidUnionFieldSuggestion {
+                        lo: ty_span.shrink_to_lo(),
+                        hi: ty_span.shrink_to_hi(),
+                    },
+                    note: (),
+                });
                 return false;
             } else if field_ty.needs_drop(tcx, param_env) {
                 // This should never happen. But we can get here e.g. in case of name resolution errors.
@@ -454,7 +444,7 @@ fn check_opaque_meets_bounds<'tcx>(
     // version.
     let errors = ocx.select_all_or_error();
     if !errors.is_empty() {
-        infcx.err_ctxt().report_fulfillment_errors(&errors, None);
+        infcx.err_ctxt().report_fulfillment_errors(&errors);
     }
     match origin {
         // Checked when type checking the function containing them.
@@ -1555,6 +1545,6 @@ pub(super) fn check_generator_obligations(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     let errors = fulfillment_cx.select_all_or_error(&infcx);
     debug!(?errors);
     if !errors.is_empty() {
-        infcx.err_ctxt().report_fulfillment_errors(&errors, None);
+        infcx.err_ctxt().report_fulfillment_errors(&errors);
     }
 }
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 5adc7a87323..6e6f8c1533b 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -320,7 +320,7 @@ fn compare_method_predicate_entailment<'tcx>(
                 });
             }
             CheckImpliedWfMode::Skip => {
-                let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
+                let reported = infcx.err_ctxt().report_fulfillment_errors(&errors);
                 return Err(reported);
             }
         }
@@ -720,7 +720,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
     // RPITs.
     let errors = ocx.select_all_or_error();
     if !errors.is_empty() {
-        let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
+        let reported = infcx.err_ctxt().report_fulfillment_errors(&errors);
         return Err(reported);
     }
 
@@ -830,7 +830,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ImplTraitInTraitCollector<'_, 'tcx> {
 
     fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
         if let ty::Alias(ty::Projection, proj) = ty.kind()
-            && self.interner().def_kind(proj.def_id) == DefKind::ImplTraitPlaceholder
+            && self.interner().is_impl_trait_in_trait(proj.def_id)
         {
             if let Some((ty, _)) = self.types.get(&proj.def_id) {
                 return *ty;
@@ -1731,7 +1731,7 @@ pub(super) fn compare_impl_const_raw(
     // version.
     let errors = ocx.select_all_or_error();
     if !errors.is_empty() {
-        return Err(infcx.err_ctxt().report_fulfillment_errors(&errors, None));
+        return Err(infcx.err_ctxt().report_fulfillment_errors(&errors));
     }
 
     let outlives_environment = OutlivesEnvironment::new(param_env);
@@ -1831,7 +1831,7 @@ fn compare_type_predicate_entailment<'tcx>(
     // version.
     let errors = ocx.select_all_or_error();
     if !errors.is_empty() {
-        let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
+        let reported = infcx.err_ctxt().report_fulfillment_errors(&errors);
         return Err(reported);
     }
 
@@ -1995,13 +1995,20 @@ pub(super) fn check_type_bounds<'tcx>(
     let infcx = tcx.infer_ctxt().build();
     let ocx = ObligationCtxt::new(&infcx);
 
-    let impl_ty_span = match tcx.hir().get_by_def_id(impl_ty_def_id) {
-        hir::Node::TraitItem(hir::TraitItem {
-            kind: hir::TraitItemKind::Type(_, Some(ty)),
-            ..
-        }) => ty.span,
-        hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Type(ty), .. }) => ty.span,
-        _ => bug!(),
+    // A synthetic impl Trait for RPITIT desugaring has no HIR, which we currently use to get the
+    // span for an impl's associated type. Instead, for these, use the def_span for the synthesized
+    // associated type.
+    let impl_ty_span = if tcx.opt_rpitit_info(impl_ty.def_id).is_some() {
+        tcx.def_span(impl_ty_def_id)
+    } else {
+        match tcx.hir().get_by_def_id(impl_ty_def_id) {
+            hir::Node::TraitItem(hir::TraitItem {
+                kind: hir::TraitItemKind::Type(_, Some(ty)),
+                ..
+            }) => ty.span,
+            hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Type(ty), .. }) => ty.span,
+            _ => bug!(),
+        }
     };
     let assumed_wf_types = ocx.assumed_wf_types(param_env, impl_ty_span, impl_ty_def_id);
 
@@ -2044,7 +2051,7 @@ pub(super) fn check_type_bounds<'tcx>(
     // version.
     let errors = ocx.select_all_or_error();
     if !errors.is_empty() {
-        let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
+        let reported = infcx.err_ctxt().report_fulfillment_errors(&errors);
         return Err(reported);
     }
 
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
index b1d5a27be93..172b84bafb2 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
@@ -1,5 +1,5 @@
 use rustc_ast::InlineAsmTemplatePiece;
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::FxIndexSet;
 use rustc_hir as hir;
 use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy};
 use rustc_session::lint;
@@ -51,7 +51,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
         template: &[InlineAsmTemplatePiece],
         is_input: bool,
         tied_input: Option<(&'tcx hir::Expr<'tcx>, Option<InlineAsmType>)>,
-        target_features: &FxHashSet<Symbol>,
+        target_features: &FxIndexSet<Symbol>,
     ) -> Option<InlineAsmType> {
         let ty = (self.get_operand_ty)(expr);
         if ty.has_non_region_infer() {
@@ -201,7 +201,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
         // (!). In that case we still need the earlier check to verify that the
         // register class is usable at all.
         if let Some(feature) = feature {
-            if !target_features.contains(&feature) {
+            if !target_features.contains(feature) {
                 let msg = &format!("`{}` target feature is not enabled", feature);
                 let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
                 err.note(&format!(
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 4cccdf30c5f..71050864ce0 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -111,7 +111,7 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>(
 
     let errors = wfcx.select_all_or_error();
     if !errors.is_empty() {
-        infcx.err_ctxt().report_fulfillment_errors(&errors, None);
+        infcx.err_ctxt().report_fulfillment_errors(&errors);
         return;
     }
 
diff --git a/compiler/rustc_hir_analysis/src/check_unused.rs b/compiler/rustc_hir_analysis/src/check_unused.rs
index f3f5851d8f9..268b9ac530f 100644
--- a/compiler/rustc_hir_analysis/src/check_unused.rs
+++ b/compiler/rustc_hir_analysis/src/check_unused.rs
@@ -10,7 +10,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
     for item_def_id in tcx.hir().body_owners() {
         let imports = tcx.used_trait_imports(item_def_id);
         debug!("GatherVisitor: item_def_id={:?} with imports {:#?}", item_def_id, imports);
-        used_trait_imports.extend(imports.items().copied());
+        used_trait_imports.extend_unord(imports.items().copied());
     }
 
     for &id in tcx.maybe_unused_trait_imports(()) {
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index ffb68abf978..5e8f69677cf 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -2,6 +2,7 @@
 //! up data structures required by type-checking/codegen.
 
 use crate::errors::{CopyImplOnNonAdt, CopyImplOnTypeWithDtor, DropImplOnWrongItem};
+use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{struct_span_err, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -86,7 +87,7 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
                 tcx.sess,
                 span,
                 E0204,
-                "the trait `Copy` may not be implemented for this type"
+                "the trait `Copy` cannot be implemented for this type"
             );
 
             // We'll try to suggest constraining type parameters to fulfill the requirements of
@@ -94,7 +95,14 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
             let mut errors: BTreeMap<_, Vec<_>> = Default::default();
             let mut bounds = vec![];
 
+            let mut seen_tys = FxHashSet::default();
+
             for (field, ty, reason) in fields {
+                // Only report an error once per type.
+                if !seen_tys.insert(ty) {
+                    continue;
+                }
+
                 let field_span = tcx.def_span(field.did);
                 err.span_label(field_span, "this field does not implement `Copy`");
 
@@ -337,7 +345,7 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef
                     }),
                 );
                 if !errors.is_empty() {
-                    infcx.err_ctxt().report_fulfillment_errors(&errors, None);
+                    infcx.err_ctxt().report_fulfillment_errors(&errors);
                 }
 
                 // Finally, resolve all regions.
@@ -577,7 +585,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn
         predicate_for_trait_def(tcx, param_env, cause, trait_def_id, 0, [source, target]);
     let errors = traits::fully_solve_obligation(&infcx, predicate);
     if !errors.is_empty() {
-        infcx.err_ctxt().report_fulfillment_errors(&errors, None);
+        infcx.err_ctxt().report_fulfillment_errors(&errors);
     }
 
     // Finally, resolve all regions.
diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
index 6f6f993f727..7dce29cc0bb 100644
--- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
+++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
@@ -3,7 +3,7 @@ use crate::astconv::AstConv;
 use rustc_hir as hir;
 use rustc_infer::traits::util;
 use rustc_middle::ty::subst::InternalSubsts;
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, ImplTraitInTraitData, Ty, TyCtxt};
 use rustc_span::def_id::DefId;
 use rustc_span::Span;
 
@@ -58,17 +58,10 @@ fn opaque_type_bounds<'tcx>(
     tcx: TyCtxt<'tcx>,
     opaque_def_id: DefId,
     ast_bounds: &'tcx [hir::GenericBound<'tcx>],
+    item_ty: Ty<'tcx>,
     span: Span,
-    in_trait: bool,
 ) -> &'tcx [(ty::Predicate<'tcx>, Span)] {
     ty::print::with_no_queries!({
-        let substs = InternalSubsts::identity_for_item(tcx, opaque_def_id);
-        let item_ty = if in_trait {
-            tcx.mk_projection(opaque_def_id, substs)
-        } else {
-            tcx.mk_opaque(opaque_def_id, substs)
-        };
-
         let icx = ItemCtxt::new(tcx, opaque_def_id);
         let mut bounds = icx.astconv().compute_bounds(item_ty, ast_bounds);
         // Opaque types are implicitly sized unless a `?Sized` bound is found
@@ -83,7 +76,18 @@ pub(super) fn explicit_item_bounds(
     tcx: TyCtxt<'_>,
     def_id: DefId,
 ) -> &'_ [(ty::Predicate<'_>, Span)] {
-    let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
+    // If the def_id is about an RPITIT, delegate explicit_item_bounds to the opaque_def_id that
+    // generated the synthesized associate type.
+    let rpitit_info = if let Some(ImplTraitInTraitData::Trait { opaque_def_id, .. }) =
+        tcx.opt_rpitit_info(def_id)
+    {
+        Some(opaque_def_id)
+    } else {
+        None
+    };
+
+    let bounds_def_id = rpitit_info.unwrap_or(def_id);
+    let hir_id = tcx.hir().local_def_id_to_hir_id(bounds_def_id.expect_local());
     match tcx.hir().get(hir_id) {
         hir::Node::TraitItem(hir::TraitItem {
             kind: hir::TraitItemKind::Type(bounds, _),
@@ -94,7 +98,15 @@ pub(super) fn explicit_item_bounds(
             kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, in_trait, .. }),
             span,
             ..
-        }) => opaque_type_bounds(tcx, def_id, bounds, *span, *in_trait),
+        }) => {
+            let substs = InternalSubsts::identity_for_item(tcx, def_id);
+            let item_ty = if *in_trait || rpitit_info.is_some() {
+                tcx.mk_projection(def_id, substs)
+            } else {
+                tcx.mk_opaque(def_id, substs)
+            };
+            opaque_type_bounds(tcx, bounds_def_id, bounds, item_ty, *span)
+        }
         _ => bug!("item_bounds called on {:?}", def_id),
     }
 }
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index 7fc0711a155..fe44fabf57d 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -9,7 +9,8 @@ use rustc_middle::ty::print::with_forced_trimmed_paths;
 use rustc_middle::ty::subst::InternalSubsts;
 use rustc_middle::ty::util::IntTypeExt;
 use rustc_middle::ty::{
-    self, IsSuggestable, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
+    self, ImplTraitInTraitData, IsSuggestable, Ty, TyCtxt, TypeFolder, TypeSuperFoldable,
+    TypeVisitableExt,
 };
 use rustc_span::symbol::Ident;
 use rustc_span::{Span, DUMMY_SP};
@@ -244,6 +245,24 @@ fn get_path_containing_arg_in_pat<'hir>(
 }
 
 pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder<Ty<'_>> {
+    // If we are computing `type_of` the synthesized associated type for an RPITIT in the impl
+    // side, use `collect_return_position_impl_trait_in_trait_tys` to infer the value of the
+    // associated type in the impl.
+    if let Some(ImplTraitInTraitData::Impl { fn_def_id, .. }) = tcx.opt_rpitit_info(def_id) {
+        match tcx.collect_return_position_impl_trait_in_trait_tys(fn_def_id) {
+            Ok(map) => {
+                let assoc_item = tcx.associated_item(def_id);
+                return ty::EarlyBinder(map[&assoc_item.trait_item_def_id.unwrap()]);
+            }
+            Err(_) => {
+                return ty::EarlyBinder(tcx.ty_error_with_message(
+                    DUMMY_SP,
+                    "Could not collect return position impl trait in trait tys",
+                ));
+            }
+        }
+    }
+
     let def_id = def_id.expect_local();
     use rustc_hir::*;
 
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 74fec93d91e..f57197edeb7 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -5,7 +5,7 @@ use rustc_errors::{
     error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic,
     MultiSpan,
 };
-use rustc_macros::Diagnostic;
+use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_middle::ty::Ty;
 use rustc_span::{symbol::Ident, Span, Symbol};
 
@@ -129,6 +129,18 @@ pub struct AssocTypeBindingNotAllowed {
     #[primary_span]
     #[label]
     pub span: Span,
+
+    #[subdiagnostic]
+    pub fn_trait_expansion: Option<ParenthesizedFnTraitExpansion>,
+}
+
+#[derive(Subdiagnostic)]
+#[help(hir_analysis_parenthesized_fn_trait_expansion)]
+pub struct ParenthesizedFnTraitExpansion {
+    #[primary_span]
+    pub span: Span,
+
+    pub expanded_type: String,
 }
 
 #[derive(Diagnostic)]
@@ -316,6 +328,14 @@ pub(crate) struct TrackCallerOnMain {
 }
 
 #[derive(Diagnostic)]
+#[diag(hir_analysis_target_feature_on_main)]
+pub(crate) struct TargetFeatureOnMain {
+    #[primary_span]
+    #[label(hir_analysis_target_feature_on_main)]
+    pub main: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(hir_analysis_start_not_track_caller)]
 pub(crate) struct StartTrackCaller {
     #[primary_span]
@@ -325,6 +345,15 @@ pub(crate) struct StartTrackCaller {
 }
 
 #[derive(Diagnostic)]
+#[diag(hir_analysis_start_not_target_feature)]
+pub(crate) struct StartTargetFeature {
+    #[primary_span]
+    pub span: Span,
+    #[label]
+    pub start: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(hir_analysis_start_not_async, code = "E0752")]
 pub(crate) struct StartAsync {
     #[primary_span]
@@ -430,3 +459,23 @@ pub(crate) struct CastThinPointerToFatPointer<'tcx> {
     pub expr_ty: Ty<'tcx>,
     pub cast_ty: String,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_invalid_union_field, code = "E0740")]
+pub(crate) struct InvalidUnionField {
+    #[primary_span]
+    pub field_span: Span,
+    #[subdiagnostic]
+    pub sugg: InvalidUnionFieldSuggestion,
+    #[note]
+    pub note: (),
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(hir_analysis_invalid_union_field_sugg, applicability = "machine-applicable")]
+pub(crate) struct InvalidUnionFieldSuggestion {
+    #[suggestion_part(code = "std::mem::ManuallyDrop<")]
+    pub lo: Span,
+    #[suggestion_part(code = ">")]
+    pub hi: Span,
+}
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
index daa5d15704d..58dd03811f7 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
@@ -174,7 +174,7 @@ fn get_impl_substs(
 
     let errors = ocx.select_all_or_error();
     if !errors.is_empty() {
-        ocx.infcx.err_ctxt().report_fulfillment_errors(&errors, None);
+        ocx.infcx.err_ctxt().report_fulfillment_errors(&errors);
         return None;
     }
 
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index 33c132fd534..62abcbbdc9f 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -120,7 +120,7 @@ use std::ops::Not;
 use astconv::AstConv;
 use bounds::Bounds;
 
-fluent_messages! { "../locales/en-US.ftl" }
+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`";
@@ -176,7 +176,7 @@ fn require_same_types<'tcx>(
     match &errors[..] {
         [] => true,
         errors => {
-            infcx.err_ctxt().report_fulfillment_errors(errors, None);
+            infcx.err_ctxt().report_fulfillment_errors(errors);
             false
         }
     }
@@ -283,6 +283,15 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
         error = true;
     }
 
+    if !tcx.codegen_fn_attrs(main_def_id).target_features.is_empty()
+        // Calling functions with `#[target_feature]` is not unsafe on WASM, see #84988
+        && !tcx.sess.target.is_like_wasm
+        && !tcx.sess.opts.actually_rustdoc
+    {
+        tcx.sess.emit_err(errors::TargetFeatureOnMain { main: main_span });
+        error = true;
+    }
+
     if error {
         return;
     }
@@ -309,7 +318,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
         ocx.register_bound(cause, param_env, norm_return_ty, term_did);
         let errors = ocx.select_all_or_error();
         if !errors.is_empty() {
-            infcx.err_ctxt().report_fulfillment_errors(&errors, None);
+            infcx.err_ctxt().report_fulfillment_errors(&errors);
             error = true;
         }
         // now we can take the return type of the given main function
@@ -373,6 +382,18 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) {
                             });
                             error = true;
                         }
+                        if attr.has_name(sym::target_feature)
+                            // Calling functions with `#[target_feature]` is
+                            // not unsafe on WASM, see #84988
+                            && !tcx.sess.target.is_like_wasm
+                            && !tcx.sess.opts.actually_rustdoc
+                        {
+                            tcx.sess.emit_err(errors::StartTargetFeature {
+                                span: attr.span,
+                                start: start_span,
+                            });
+                            error = true;
+                        }
                     }
 
                     if error {
diff --git a/compiler/rustc_hir_typeck/locales/en-US.ftl b/compiler/rustc_hir_typeck/messages.ftl
index adfcbc36a4d..2c537bf4064 100644
--- a/compiler/rustc_hir_typeck/locales/en-US.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -4,14 +4,14 @@ hir_typeck_field_multiply_specified_in_initializer =
     .previous_use_label = first use of `{$ident}`
 
 hir_typeck_copy_impl_on_type_with_dtor =
-    the trait `Copy` may not be implemented for this type; the type has a destructor
+    the trait `Copy` cannot be implemented for this type; the type has a destructor
     .label = `Copy` not allowed on types with destructors
 
 hir_typeck_multiple_relaxed_default_bounds =
     type parameter has more than one relaxed default bound, only one is supported
 
 hir_typeck_copy_impl_on_non_adt =
-    the trait `Copy` may not be implemented for this type
+    the trait `Copy` cannot be implemented for this type
     .label = type is not a structure or enumeration
 
 hir_typeck_trait_object_declared_with_no_traits =
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index d84fabb7834..773ac0e40c5 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -398,7 +398,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ///
     /// Here:
     /// - E would be `fn(&u32) -> &u32`.
-    /// - S would be `fn(&u32) ->
+    /// - S would be `fn(&u32) -> ?T`
     /// - E' is `&'!0 u32 -> &'!0 u32`
     /// - S' is `&'?0 u32 -> ?T`
     ///
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index a8f8121153f..2075537cad7 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -301,16 +301,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         span: Span,
         def_id: DefId,
         substs: SubstsRef<'tcx>,
-    ) -> (ty::InstantiatedPredicates<'tcx>, Vec<Span>) {
+    ) -> ty::InstantiatedPredicates<'tcx> {
         let bounds = self.tcx.predicates_of(def_id);
-        let spans: Vec<Span> = bounds.predicates.iter().map(|(_, span)| *span).collect();
         let result = bounds.instantiate(self.tcx, substs);
         let result = self.normalize(span, result);
-        debug!(
-            "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}",
-            bounds, substs, result, spans,
-        );
-        (result, spans)
+        debug!("instantiate_bounds(bounds={:?}, substs={:?}) = {:?}", bounds, substs, result);
+        result
     }
 
     pub(in super::super) fn normalize<T>(&self, span: Span, value: T) -> T
@@ -585,7 +581,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         if !errors.is_empty() {
             self.adjust_fulfillment_errors_for_expr_obligation(&mut errors);
-            self.err_ctxt().report_fulfillment_errors(&errors, self.inh.body_id);
+            self.err_ctxt().report_fulfillment_errors(&errors);
         }
     }
 
@@ -598,7 +594,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if !result.is_empty() {
             mutate_fulfillment_errors(&mut result);
             self.adjust_fulfillment_errors_for_expr_obligation(&mut result);
-            self.err_ctxt().report_fulfillment_errors(&result, self.inh.body_id);
+            self.err_ctxt().report_fulfillment_errors(&result);
         }
     }
 
@@ -1389,7 +1385,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
             _ => false,
         };
-        let (bounds, _) = self.instantiate_bounds(span, def_id, &substs);
+        let bounds = self.instantiate_bounds(span, def_id, &substs);
 
         for mut obligation in traits::predicates_for_generics(
             |idx, predicate_span| {
@@ -1415,7 +1411,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         } else {
             let e = self.tainted_by_errors().unwrap_or_else(|| {
                 self.err_ctxt()
-                    .emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true)
+                    .emit_inference_failure_err(self.body_id, sp, ty.into(), E0282, true)
                     .emit()
             });
             let err = self.tcx.ty_error(e);
diff --git a/compiler/rustc_hir_typeck/src/inherited.rs b/compiler/rustc_hir_typeck/src/inherited.rs
index 26020382d81..4110b176b41 100644
--- a/compiler/rustc_hir_typeck/src/inherited.rs
+++ b/compiler/rustc_hir_typeck/src/inherited.rs
@@ -4,7 +4,6 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::HirIdMap;
-use rustc_infer::infer;
 use rustc_infer::infer::{DefiningAnchor, InferCtxt, InferOk, TyCtxtInferExt};
 use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -58,8 +57,6 @@ pub struct Inherited<'tcx> {
     pub(super) deferred_generator_interiors:
         RefCell<Vec<(LocalDefId, hir::BodyId, Ty<'tcx>, hir::GeneratorKind)>>,
 
-    pub(super) body_id: Option<hir::BodyId>,
-
     /// Whenever we introduce an adjustment from `!` into a type variable,
     /// we record that type variable here. This is later used to inform
     /// fallback. See the `fallback` module for details.
@@ -75,48 +72,16 @@ impl<'tcx> Deref for Inherited<'tcx> {
     }
 }
 
-/// A temporary returned by `Inherited::build(...)`. This is necessary
-/// for multiple `InferCtxt` to share the same `typeck_results`
-/// without using `Rc` or something similar.
-pub struct InheritedBuilder<'tcx> {
-    infcx: infer::InferCtxtBuilder<'tcx>,
-    def_id: LocalDefId,
-    typeck_results: RefCell<ty::TypeckResults<'tcx>>,
-}
-
 impl<'tcx> Inherited<'tcx> {
-    pub fn build(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> InheritedBuilder<'tcx> {
+    pub fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
         let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner;
 
-        InheritedBuilder {
-            infcx: tcx
-                .infer_ctxt()
-                .ignoring_regions()
-                .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)),
-            def_id,
-            typeck_results: RefCell::new(ty::TypeckResults::new(hir_owner)),
-        }
-    }
-}
-
-impl<'tcx> InheritedBuilder<'tcx> {
-    pub fn enter<F, R>(mut self, f: F) -> R
-    where
-        F: FnOnce(&Inherited<'tcx>) -> R,
-    {
-        let def_id = self.def_id;
-        f(&Inherited::new(self.infcx.build(), def_id, self.typeck_results))
-    }
-}
-
-impl<'tcx> Inherited<'tcx> {
-    fn new(
-        infcx: InferCtxt<'tcx>,
-        def_id: LocalDefId,
-        typeck_results: RefCell<ty::TypeckResults<'tcx>>,
-    ) -> Self {
-        let tcx = infcx.tcx;
-        let body_id = tcx.hir().maybe_body_owned_by(def_id);
+        let infcx = tcx
+            .infer_ctxt()
+            .ignoring_regions()
+            .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id))
+            .build();
+        let typeck_results = RefCell::new(ty::TypeckResults::new(hir_owner));
 
         Inherited {
             typeck_results,
@@ -130,7 +95,6 @@ impl<'tcx> Inherited<'tcx> {
             deferred_asm_checks: RefCell::new(Vec::new()),
             deferred_generator_interiors: RefCell::new(Vec::new()),
             diverging_type_vars: RefCell::new(Default::default()),
-            body_id,
             infer_var_info: RefCell::new(Default::default()),
         }
     }
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index e397dfd4570..d9c56134b66 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -45,13 +45,14 @@ mod rvalue_scopes;
 mod upvar;
 mod writeback;
 
-pub use diverges::Diverges;
-pub use expectation::Expectation;
-pub use fn_ctxt::*;
-pub use inherited::{Inherited, InheritedBuilder};
+pub use fn_ctxt::FnCtxt;
+pub use inherited::Inherited;
 
 use crate::check::check_fn;
 use crate::coercion::DynamicCoerceMany;
+use crate::diverges::Diverges;
+use crate::expectation::Expectation;
+use crate::fn_ctxt::RawTy;
 use crate::gather_locals::GatherLocalsVisitor;
 use rustc_data_structures::unord::UnordSet;
 use rustc_errors::{
@@ -74,7 +75,7 @@ use rustc_session::Session;
 use rustc_span::def_id::{DefId, LocalDefId};
 use rustc_span::{sym, Span};
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 #[macro_export]
 macro_rules! type_error_struct {
@@ -105,10 +106,9 @@ pub struct LocalTy<'tcx> {
 /// (notably closures), `typeck_results(def_id)` would wind up
 /// redirecting to the owning function.
 fn primary_body_of(
-    tcx: TyCtxt<'_>,
-    id: hir::HirId,
+    node: Node<'_>,
 ) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnSig<'_>>)> {
-    match tcx.hir().get(id) {
+    match node {
         Node::Item(item) => match item.kind {
             hir::ItemKind::Const(ty, body) | hir::ItemKind::Static(ty, _, body) => {
                 Some((body, Some(ty), None))
@@ -142,8 +142,7 @@ fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
     }
 
     if let Some(def_id) = def_id.as_local() {
-        let id = tcx.hir().local_def_id_to_hir_id(def_id);
-        primary_body_of(tcx, id).is_some()
+        primary_body_of(tcx.hir().get_by_def_id(def_id)).is_some()
     } else {
         false
     }
@@ -198,143 +197,140 @@ fn typeck_with_fallback<'tcx>(
     }
 
     let id = tcx.hir().local_def_id_to_hir_id(def_id);
+    let node = tcx.hir().get(id);
     let span = tcx.hir().span(id);
 
     // Figure out what primary body this item has.
-    let (body_id, body_ty, fn_sig) = primary_body_of(tcx, id).unwrap_or_else(|| {
+    let (body_id, body_ty, fn_sig) = primary_body_of(node).unwrap_or_else(|| {
         span_bug!(span, "can't type-check body of {:?}", def_id);
     });
     let body = tcx.hir().body(body_id);
 
-    let typeck_results = Inherited::build(tcx, def_id).enter(|inh| {
-        let param_env = tcx.param_env(def_id);
-        let param_env = if tcx.has_attr(def_id.to_def_id(), sym::rustc_do_not_const_check) {
-            param_env.without_const()
+    let param_env = tcx.param_env(def_id);
+    let param_env = if tcx.has_attr(def_id.to_def_id(), sym::rustc_do_not_const_check) {
+        param_env.without_const()
+    } else {
+        param_env
+    };
+    let inh = Inherited::new(tcx, def_id);
+    let mut fcx = FnCtxt::new(&inh, param_env, def_id);
+
+    if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
+        let fn_sig = if rustc_hir_analysis::collect::get_infer_ret_ty(&decl.output).is_some() {
+            fcx.astconv().ty_of_fn(id, header.unsafety, header.abi, decl, None, None)
         } else {
-            param_env
+            tcx.fn_sig(def_id).subst_identity()
         };
-        let mut fcx = FnCtxt::new(&inh, param_env, def_id);
-
-        if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
-            let fn_sig = if rustc_hir_analysis::collect::get_infer_ret_ty(&decl.output).is_some() {
-                fcx.astconv().ty_of_fn(id, header.unsafety, header.abi, decl, None, None)
-            } else {
-                tcx.fn_sig(def_id).subst_identity()
-            };
 
-            check_abi(tcx, id, span, fn_sig.abi());
+        check_abi(tcx, id, span, fn_sig.abi());
 
-            // Compute the function signature from point of view of inside the fn.
-            let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig);
-            let fn_sig = fcx.normalize(body.value.span, fn_sig);
+        // Compute the function signature from point of view of inside the fn.
+        let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig);
+        let fn_sig = fcx.normalize(body.value.span, fn_sig);
 
-            check_fn(&mut fcx, fn_sig, decl, def_id, body, None);
-        } else {
-            let expected_type = body_ty
-                .and_then(|ty| match ty.kind {
-                    hir::TyKind::Infer => Some(fcx.astconv().ast_ty_to_ty(ty)),
-                    _ => None,
-                })
-                .unwrap_or_else(|| match tcx.hir().get(id) {
-                    Node::AnonConst(_) => match tcx.hir().get(tcx.hir().parent_id(id)) {
-                        Node::Expr(&hir::Expr {
-                            kind: hir::ExprKind::ConstBlock(ref anon_const),
-                            ..
-                        }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin {
-                            kind: TypeVariableOriginKind::TypeInference,
-                            span,
-                        }),
-                        Node::Ty(&hir::Ty {
-                            kind: hir::TyKind::Typeof(ref anon_const), ..
-                        }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin {
-                            kind: TypeVariableOriginKind::TypeInference,
-                            span,
-                        }),
-                        Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. })
-                        | Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => {
-                            let operand_ty =
-                                asm.operands.iter().find_map(|(op, _op_sp)| match op {
-                                    hir::InlineAsmOperand::Const { anon_const }
-                                        if anon_const.hir_id == id =>
-                                    {
-                                        // Inline assembly constants must be integers.
-                                        Some(fcx.next_int_var())
-                                    }
-                                    hir::InlineAsmOperand::SymFn { anon_const }
-                                        if anon_const.hir_id == id =>
-                                    {
-                                        Some(fcx.next_ty_var(TypeVariableOrigin {
-                                            kind: TypeVariableOriginKind::MiscVariable,
-                                            span,
-                                        }))
-                                    }
-                                    _ => None,
-                                });
-                            operand_ty.unwrap_or_else(fallback)
+        check_fn(&mut fcx, fn_sig, decl, def_id, body, None);
+    } else {
+        let expected_type = if let Some(&hir::Ty { kind: hir::TyKind::Infer, span, .. }) = body_ty {
+            Some(fcx.next_ty_var(TypeVariableOrigin {
+                kind: TypeVariableOriginKind::TypeInference,
+                span,
+            }))
+        } else if let Node::AnonConst(_) = node {
+            match tcx.hir().get(tcx.hir().parent_id(id)) {
+                Node::Expr(&hir::Expr {
+                    kind: hir::ExprKind::ConstBlock(ref anon_const), ..
+                }) if anon_const.hir_id == id => Some(fcx.next_ty_var(TypeVariableOrigin {
+                    kind: TypeVariableOriginKind::TypeInference,
+                    span,
+                })),
+                Node::Ty(&hir::Ty { kind: hir::TyKind::Typeof(ref anon_const), .. })
+                    if anon_const.hir_id == id =>
+                {
+                    Some(fcx.next_ty_var(TypeVariableOrigin {
+                        kind: TypeVariableOriginKind::TypeInference,
+                        span,
+                    }))
+                }
+                Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. })
+                | Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => {
+                    asm.operands.iter().find_map(|(op, _op_sp)| match op {
+                        hir::InlineAsmOperand::Const { anon_const } if anon_const.hir_id == id => {
+                            // Inline assembly constants must be integers.
+                            Some(fcx.next_int_var())
+                        }
+                        hir::InlineAsmOperand::SymFn { anon_const } if anon_const.hir_id == id => {
+                            Some(fcx.next_ty_var(TypeVariableOrigin {
+                                kind: TypeVariableOriginKind::MiscVariable,
+                                span,
+                            }))
                         }
-                        _ => fallback(),
-                    },
-                    _ => fallback(),
-                });
+                        _ => None,
+                    })
+                }
+                _ => None,
+            }
+        } else {
+            None
+        };
+        let expected_type = expected_type.unwrap_or_else(fallback);
 
-            let expected_type = fcx.normalize(body.value.span, expected_type);
-            fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized);
+        let expected_type = fcx.normalize(body.value.span, expected_type);
+        fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized);
 
-            // Gather locals in statics (because of block expressions).
-            GatherLocalsVisitor::new(&fcx).visit_body(body);
+        // Gather locals in statics (because of block expressions).
+        GatherLocalsVisitor::new(&fcx).visit_body(body);
 
-            fcx.check_expr_coercable_to_type(&body.value, expected_type, None);
+        fcx.check_expr_coercable_to_type(&body.value, expected_type, None);
 
-            fcx.write_ty(id, expected_type);
-        };
+        fcx.write_ty(id, expected_type);
+    };
 
-        fcx.type_inference_fallback();
-
-        // Even though coercion casts provide type hints, we check casts after fallback for
-        // backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
-        fcx.check_casts();
-        fcx.select_obligations_where_possible(|_| {});
-
-        // Closure and generator analysis may run after fallback
-        // because they don't constrain other type variables.
-        // Closure analysis only runs on closures. Therefore they only need to fulfill non-const predicates (as of now)
-        let prev_constness = fcx.param_env.constness();
-        fcx.param_env = fcx.param_env.without_const();
-        fcx.closure_analyze(body);
-        fcx.param_env = fcx.param_env.with_constness(prev_constness);
-        assert!(fcx.deferred_call_resolutions.borrow().is_empty());
-        // Before the generator analysis, temporary scopes shall be marked to provide more
-        // precise information on types to be captured.
-        fcx.resolve_rvalue_scopes(def_id.to_def_id());
-
-        for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) {
-            let ty = fcx.normalize(span, ty);
-            fcx.require_type_is_sized(ty, span, code);
-        }
+    fcx.type_inference_fallback();
+
+    // Even though coercion casts provide type hints, we check casts after fallback for
+    // backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
+    fcx.check_casts();
+    fcx.select_obligations_where_possible(|_| {});
+
+    // Closure and generator analysis may run after fallback
+    // because they don't constrain other type variables.
+    // Closure analysis only runs on closures. Therefore they only need to fulfill non-const predicates (as of now)
+    let prev_constness = fcx.param_env.constness();
+    fcx.param_env = fcx.param_env.without_const();
+    fcx.closure_analyze(body);
+    fcx.param_env = fcx.param_env.with_constness(prev_constness);
+    assert!(fcx.deferred_call_resolutions.borrow().is_empty());
+    // Before the generator analysis, temporary scopes shall be marked to provide more
+    // precise information on types to be captured.
+    fcx.resolve_rvalue_scopes(def_id.to_def_id());
+
+    for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) {
+        let ty = fcx.normalize(span, ty);
+        fcx.require_type_is_sized(ty, span, code);
+    }
 
-        fcx.select_obligations_where_possible(|_| {});
+    fcx.select_obligations_where_possible(|_| {});
 
-        debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations());
+    debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations());
 
-        // This must be the last thing before `report_ambiguity_errors`.
-        fcx.resolve_generator_interiors(def_id.to_def_id());
+    // This must be the last thing before `report_ambiguity_errors`.
+    fcx.resolve_generator_interiors(def_id.to_def_id());
 
-        debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations());
+    debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations());
 
-        if let None = fcx.infcx.tainted_by_errors() {
-            fcx.report_ambiguity_errors();
-        }
+    if let None = fcx.infcx.tainted_by_errors() {
+        fcx.report_ambiguity_errors();
+    }
 
-        if let None = fcx.infcx.tainted_by_errors() {
-            fcx.check_transmutes();
-        }
+    if let None = fcx.infcx.tainted_by_errors() {
+        fcx.check_transmutes();
+    }
 
-        fcx.check_asms();
+    fcx.check_asms();
 
-        fcx.infcx.skip_region_resolution();
+    fcx.infcx.skip_region_resolution();
 
-        fcx.resolve_type_vars_in_body(body)
-    });
+    let typeck_results = fcx.resolve_type_vars_in_body(body);
 
     // Consistency check our TypeckResults instance can hold all ItemLocalIds
     // it will need to hold.
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 3bef5cfcd78..57805f7c800 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -1031,7 +1031,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
             .collect();
 
         // Sort them by the name so we have a stable result.
-        names.sort_by(|a, b| a.as_str().partial_cmp(b.as_str()).unwrap());
+        names.sort_by(|a, b| a.as_str().cmp(b.as_str()));
         names
     }
 
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index e6d6586d5ee..4b15e48bd27 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -42,7 +42,7 @@ use rustc_trait_selection::traits::{
 use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope};
 use super::{CandidateSource, MethodError, NoMatchData};
 use rustc_hir::intravisit::Visitor;
-use std::cmp::Ordering;
+use std::cmp::{self, Ordering};
 use std::iter;
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -2527,7 +2527,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         if !candidates.is_empty() {
             // Sort from most relevant to least relevant.
-            candidates.sort_by(|a, b| a.cmp(b).reverse());
+            candidates.sort_by_key(|&info| cmp::Reverse(info));
             candidates.dedup();
 
             let param_type = match rcvr_ty.kind() {
diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs
index eecded557a5..37783bc91bb 100644
--- a/compiler/rustc_hir_typeck/src/op.rs
+++ b/compiler/rustc_hir_typeck/src/op.rs
@@ -148,10 +148,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         rhs_ty,
                         op,
                     );
-                    self.demand_suptype(expr.span, builtin_return_ty, return_ty);
+                    self.demand_eqtype(expr.span, builtin_return_ty, return_ty);
+                    builtin_return_ty
+                } else {
+                    return_ty
                 }
-
-                return_ty
             }
         }
     }
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index 00348f3afdc..af588b16d59 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -748,7 +748,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
                 .infcx
                 .err_ctxt()
                 .emit_inference_failure_err(
-                    Some(self.body.id()),
+                    self.tcx.hir().body_owner_def_id(self.body.id()),
                     self.span.to_span(self.tcx),
                     p.into(),
                     E0282,
diff --git a/compiler/rustc_incremental/locales/en-US.ftl b/compiler/rustc_incremental/messages.ftl
index 4852ee0d959..4852ee0d959 100644
--- a/compiler/rustc_incremental/locales/en-US.ftl
+++ b/compiler/rustc_incremental/messages.ftl
diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs
index 511e466c2ae..df958e4a61f 100644
--- a/compiler/rustc_incremental/src/lib.rs
+++ b/compiler/rustc_incremental/src/lib.rs
@@ -35,4 +35,4 @@ pub use persist::{build_dep_graph, load_dep_graph, DepGraphFuture};
 use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
 use rustc_macros::fluent_messages;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs
index 73d7e3becab..4deae9f41c7 100644
--- a/compiler/rustc_incremental/src/persist/fs.rs
+++ b/compiler/rustc_incremental/src/persist/fs.rs
@@ -297,10 +297,12 @@ pub fn prepare_session_directory(
 /// renaming it to `s-{timestamp}-{svh}` and releasing the file lock.
 /// If there have been compilation errors, however, this function will just
 /// delete the presumably invalid session directory.
-pub fn finalize_session_directory(sess: &Session, svh: Svh) {
+pub fn finalize_session_directory(sess: &Session, svh: Option<Svh>) {
     if sess.opts.incremental.is_none() {
         return;
     }
+    // The svh is always produced when incr. comp. is enabled.
+    let svh = svh.unwrap();
 
     let _timer = sess.timer("incr_comp_finalize_session_directory");
 
diff --git a/compiler/rustc_infer/locales/en-US.ftl b/compiler/rustc_infer/messages.ftl
index 15780898dc6..15780898dc6 100644
--- a/compiler/rustc_infer/locales/en-US.ftl
+++ b/compiler/rustc_infer/messages.ftl
diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
index 678c4a0beb6..8ac82653c0e 100644
--- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
+++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
@@ -352,19 +352,17 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
             }
 
             ty::ReVar(vid) => {
-                let resolved_vid = self
+                let resolved = self
                     .infcx
                     .inner
                     .borrow_mut()
                     .unwrap_region_constraints()
-                    .opportunistic_resolve_var(vid);
+                    .opportunistic_resolve_var(self.tcx, vid);
                 debug!(
-                    "canonical: region var found with vid {:?}, \
-                     opportunistically resolved to {:?}",
-                    vid, resolved_vid
+                    "canonical: region var found with vid {vid:?}, \
+                     opportunistically resolved to {resolved:?}",
                 );
-                let r = self.tcx.mk_re_var(resolved_vid);
-                self.canonicalize_mode.canonicalize_free_region(self, r)
+                self.canonicalize_mode.canonicalize_free_region(self, resolved)
             }
 
             ty::ReStatic
@@ -376,9 +374,18 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
         }
     }
 
-    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+    fn fold_ty(&mut self, mut t: Ty<'tcx>) -> Ty<'tcx> {
         match *t.kind() {
-            ty::Infer(ty::TyVar(vid)) => {
+            ty::Infer(ty::TyVar(mut vid)) => {
+                // We need to canonicalize the *root* of our ty var.
+                // This is so that our canonical response correctly reflects
+                // any equated inference vars correctly!
+                let root_vid = self.infcx.root_var(vid);
+                if root_vid != vid {
+                    t = self.infcx.tcx.mk_ty_var(root_vid);
+                    vid = root_vid;
+                }
+
                 debug!("canonical: type var found with vid {:?}", vid);
                 match self.infcx.probe_ty_var(vid) {
                     // `t` could be a float / int variable; canonicalize that instead.
@@ -404,15 +411,28 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
                 }
             }
 
-            ty::Infer(ty::IntVar(_)) => self.canonicalize_ty_var(
-                CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Int) },
-                t,
-            ),
-
-            ty::Infer(ty::FloatVar(_)) => self.canonicalize_ty_var(
-                CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Float) },
-                t,
-            ),
+            ty::Infer(ty::IntVar(vid)) => {
+                let nt = self.infcx.opportunistic_resolve_int_var(vid);
+                if nt != t {
+                    return self.fold_ty(nt);
+                } else {
+                    self.canonicalize_ty_var(
+                        CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Int) },
+                        t,
+                    )
+                }
+            }
+            ty::Infer(ty::FloatVar(vid)) => {
+                let nt = self.infcx.opportunistic_resolve_float_var(vid);
+                if nt != t {
+                    return self.fold_ty(nt);
+                } else {
+                    self.canonicalize_ty_var(
+                        CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Float) },
+                        t,
+                    )
+                }
+            }
 
             ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
                 bug!("encountered a fresh type during canonicalization")
@@ -469,9 +489,18 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
         }
     }
 
-    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+    fn fold_const(&mut self, mut ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
         match ct.kind() {
-            ty::ConstKind::Infer(InferConst::Var(vid)) => {
+            ty::ConstKind::Infer(InferConst::Var(mut vid)) => {
+                // We need to canonicalize the *root* of our const var.
+                // This is so that our canonical response correctly reflects
+                // any equated inference vars correctly!
+                let root_vid = self.infcx.root_const_var(vid);
+                if root_vid != vid {
+                    ct = self.infcx.tcx.mk_const(ty::InferConst::Var(root_vid), ct.ty());
+                    vid = root_vid;
+                }
+
                 debug!("canonical: const var found with vid {:?}", vid);
                 match self.infcx.probe_const_var(vid) {
                     Ok(c) => {
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs
index 33292e871b1..a2332797e86 100644
--- a/compiler/rustc_infer/src/infer/combine.rs
+++ b/compiler/rustc_infer/src/infer/combine.rs
@@ -34,7 +34,6 @@ use rustc_hir::def_id::DefId;
 use rustc_middle::infer::canonical::OriginalQueryValues;
 use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
 use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
-use rustc_middle::traits::query::NoSolution;
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
@@ -119,20 +118,30 @@ impl<'tcx> InferCtxt<'tcx> {
                 self.unify_float_variable(!a_is_expected, v_id, v)
             }
 
-            // All other cases of inference are errors
-            (&ty::Infer(_), _) | (_, &ty::Infer(_)) => {
-                Err(TypeError::Sorts(ty::relate::expected_found(relation, a, b)))
+            // We don't expect `TyVar` or `Fresh*` vars at this point with lazy norm.
+            (
+                ty::Alias(AliasKind::Projection, _),
+                ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)),
+            )
+            | (
+                ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)),
+                ty::Alias(AliasKind::Projection, _),
+            ) if self.tcx.trait_solver_next() => {
+                bug!()
             }
 
-            (ty::Alias(AliasKind::Projection, _), _) if self.tcx.trait_solver_next() => {
+            (_, ty::Alias(AliasKind::Projection, _)) | (ty::Alias(AliasKind::Projection, _), _)
+                if self.tcx.trait_solver_next() =>
+            {
                 relation.register_type_equate_obligation(a, b);
-                Ok(b)
-            }
-            (_, ty::Alias(AliasKind::Projection, _)) if self.tcx.trait_solver_next() => {
-                relation.register_type_equate_obligation(b, a);
                 Ok(a)
             }
 
+            // All other cases of inference are errors
+            (&ty::Infer(_), _) | (_, &ty::Infer(_)) => {
+                Err(TypeError::Sorts(ty::relate::expected_found(relation, a, b)))
+            }
+
             _ => ty::relate::super_relate_tys(relation, a, b),
         }
     }
@@ -161,9 +170,9 @@ impl<'tcx> InferCtxt<'tcx> {
         //
         // This probe is probably not strictly necessary but it seems better to be safe and not accidentally find
         // ourselves with a check to find bugs being required for code to compile because it made inference progress.
-        self.probe(|_| {
+        let compatible_types = self.probe(|_| {
             if a.ty() == b.ty() {
-                return;
+                return Ok(());
             }
 
             // We don't have access to trait solving machinery in `rustc_infer` so the logic for determining if the
@@ -173,15 +182,24 @@ impl<'tcx> InferCtxt<'tcx> {
                 (relation.param_env(), a.ty(), b.ty()),
                 &mut OriginalQueryValues::default(),
             );
-
-            if let Err(NoSolution) = self.tcx.check_tys_might_be_eq(canonical) {
+            self.tcx.check_tys_might_be_eq(canonical).map_err(|_| {
                 self.tcx.sess.delay_span_bug(
                     DUMMY_SP,
                     &format!("cannot relate consts of different types (a={:?}, b={:?})", a, b,),
-                );
-            }
+                )
+            })
         });
 
+        // If the consts have differing types, just bail with a const error with
+        // the expected const's type. Specifically, we don't want const infer vars
+        // to do any type shapeshifting before and after resolution.
+        if let Err(guar) = compatible_types {
+            return Ok(self.tcx.const_error_with_guaranteed(
+                if relation.a_is_expected() { a.ty() } else { b.ty() },
+                guar,
+            ));
+        }
+
         match (a.kind(), b.kind()) {
             (
                 ty::ConstKind::Infer(InferConst::Var(a_vid)),
diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
index a3151d2d365..bde16fad821 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
@@ -10,7 +10,7 @@ use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, IntoDiagnosticArg};
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_hir::def::{CtorOf, DefKind, Namespace};
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, Local, LocalSource};
 use rustc_middle::hir::nested_filter;
@@ -386,7 +386,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     #[instrument(level = "debug", skip(self, error_code))]
     pub fn emit_inference_failure_err(
         &self,
-        body_id: Option<hir::BodyId>,
+        body_def_id: LocalDefId,
         failure_span: Span,
         arg: GenericArg<'tcx>,
         error_code: TypeAnnotationNeeded,
@@ -403,8 +403,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         };
 
         let mut local_visitor = FindInferSourceVisitor::new(&self, typeck_results, arg);
-        if let Some(body_id) = body_id {
-            let expr = self.tcx.hir().expect_expr(body_id.hir_id);
+        if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(
+            self.tcx.typeck_root_def_id(body_def_id.to_def_id()).expect_local(),
+        ) {
+            let expr = self.tcx.hir().body(body_id).value;
             local_visitor.visit_expr(expr);
         }
 
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
index b06ff10d86e..22c1e387117 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
@@ -104,7 +104,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
         let (mention_influencer, influencer_point) =
             if sup_origin.span().overlaps(param.param_ty_span) {
                 // Account for `async fn` like in `async-await/issues/issue-62097.rs`.
-                // The desugaring of `async `fn`s causes `sup_origin` and `param` to point at the same
+                // The desugaring of `async fn`s causes `sup_origin` and `param` to point at the same
                 // place (but with different `ctxt`, hence `overlaps` instead of `==` above).
                 //
                 // This avoids the following:
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index fb067e7ac21..4a834957959 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -1359,6 +1359,32 @@ impl<'tcx> InferCtxt<'tcx> {
         self.inner.borrow_mut().type_variables().root_var(var)
     }
 
+    pub fn root_const_var(&self, var: ty::ConstVid<'tcx>) -> ty::ConstVid<'tcx> {
+        self.inner.borrow_mut().const_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> {
+        let mut inner = self.inner.borrow_mut();
+        if let Some(value) = inner.int_unification_table().probe_value(vid) {
+            value.to_type(self.tcx)
+        } else {
+            self.tcx.mk_int_var(inner.int_unification_table().find(vid))
+        }
+    }
+
+    /// Resolves a float var to a rigid int type, if it was constrained to one,
+    /// or else the root float var in the unification table.
+    pub fn opportunistic_resolve_float_var(&self, vid: ty::FloatVid) -> Ty<'tcx> {
+        let mut inner = self.inner.borrow_mut();
+        if let Some(value) = inner.float_unification_table().probe_value(vid) {
+            value.to_type(self.tcx)
+        } else {
+            self.tcx.mk_float_var(inner.float_unification_table().find(vid))
+        }
+    }
+
     /// Where possible, replaces type/const variables in
     /// `value` with their final value. Note that region variables
     /// are unaffected. If a type/const variable has not been unified, it
diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs
index 872f617474c..0b86d9c1fb8 100644
--- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs
+++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs
@@ -420,7 +420,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
         // `RegionConstraintData` contains the relationship here.
         if *any_unifications {
             *any_unifications = false;
-            self.unification_table().reset_unifications(|_| UnifiedRegion(None));
+            self.unification_table_mut().reset_unifications(|_| UnifiedRegion::new(None));
         }
 
         data
@@ -447,7 +447,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
     ) -> RegionVid {
         let vid = self.var_infos.push(RegionVariableInfo { origin, universe });
 
-        let u_vid = self.unification_table().new_key(UnifiedRegion(None));
+        let u_vid = self.unification_table_mut().new_key(UnifiedRegion::new(None));
         assert_eq!(vid, u_vid.vid);
         self.undo_log.push(AddVar(vid));
         debug!("created new region variable {:?} in {:?} with origin {:?}", vid, universe, origin);
@@ -516,13 +516,13 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
             match (sub, sup) {
                 (Region(Interned(ReVar(sub), _)), Region(Interned(ReVar(sup), _))) => {
                     debug!("make_eqregion: unifying {:?} with {:?}", sub, sup);
-                    self.unification_table().union(*sub, *sup);
+                    self.unification_table_mut().union(*sub, *sup);
                     self.any_unifications = true;
                 }
                 (Region(Interned(ReVar(vid), _)), value)
                 | (value, Region(Interned(ReVar(vid), _))) => {
                     debug!("make_eqregion: unifying {:?} with {:?}", vid, value);
-                    self.unification_table().union_value(*vid, UnifiedRegion(Some(value)));
+                    self.unification_table_mut().union_value(*vid, UnifiedRegion::new(Some(value)));
                     self.any_unifications = true;
                 }
                 (_, _) => {}
@@ -633,28 +633,25 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
         }
     }
 
-    /// Resolves the passed RegionVid to the root RegionVid in the unification table
-    pub(super) fn opportunistic_resolve_var(&mut self, rid: ty::RegionVid) -> ty::RegionVid {
-        self.unification_table().find(rid).vid
-    }
-
-    /// If the Region is a `ReVar`, then resolves it either to the root value in
-    /// the unification table, if it exists, or to the root `ReVar` in the table.
-    /// If the Region is not a `ReVar`, just returns the Region itself.
-    pub fn opportunistic_resolve_region(
+    /// Resolves a region var to its value in the unification table, if it exists.
+    /// Otherwise, it is resolved to the root `ReVar` in the table.
+    pub fn opportunistic_resolve_var(
         &mut self,
         tcx: TyCtxt<'tcx>,
-        region: ty::Region<'tcx>,
+        vid: ty::RegionVid,
     ) -> ty::Region<'tcx> {
-        match *region {
-            ty::ReVar(rid) => {
-                let unified_region = self.unification_table().probe_value(rid);
-                unified_region.0.unwrap_or_else(|| {
-                    let root = self.unification_table().find(rid).vid;
-                    tcx.mk_re_var(root)
-                })
-            }
-            _ => region,
+        let mut ut = self.unification_table_mut(); // FIXME(rust-lang/ena#42): unnecessary mut
+        let root_vid = ut.find(vid).vid;
+        let resolved = ut
+            .probe_value(root_vid)
+            .get_value_ignoring_universes()
+            .unwrap_or_else(|| tcx.mk_re_var(root_vid));
+
+        // Don't resolve a variable to a region that it cannot name.
+        if self.var_universe(vid).can_name(self.universe(resolved)) {
+            resolved
+        } else {
+            tcx.mk_re_var(vid)
         }
     }
 
@@ -733,7 +730,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
     }
 
     #[inline]
-    fn unification_table(&mut self) -> super::UnificationTable<'_, 'tcx, RegionVidKey<'tcx>> {
+    fn unification_table_mut(&mut self) -> super::UnificationTable<'_, 'tcx, RegionVidKey<'tcx>> {
         ut::UnificationTable::with_log(&mut self.storage.unification_table, self.undo_log)
     }
 }
diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs
index 5bb35832930..4f49f416507 100644
--- a/compiler/rustc_infer/src/infer/resolve.rs
+++ b/compiler/rustc_infer/src/infer/resolve.rs
@@ -85,15 +85,12 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for OpportunisticRegionResolver<'a, 'tcx
 
     fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
         match *r {
-            ty::ReVar(rid) => {
-                let resolved = self
-                    .infcx
-                    .inner
-                    .borrow_mut()
-                    .unwrap_region_constraints()
-                    .opportunistic_resolve_var(rid);
-                TypeFolder::interner(self).mk_re_var(resolved)
-            }
+            ty::ReVar(vid) => self
+                .infcx
+                .inner
+                .borrow_mut()
+                .unwrap_region_constraints()
+                .opportunistic_resolve_var(TypeFolder::interner(self), vid),
             _ => r,
         }
     }
diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs
index bdc313c2141..738a1237651 100644
--- a/compiler/rustc_infer/src/lib.rs
+++ b/compiler/rustc_infer/src/lib.rs
@@ -41,4 +41,4 @@ mod errors;
 pub mod infer;
 pub mod traits;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs
index 3a82899660b..77c67c14ecc 100644
--- a/compiler/rustc_infer/src/traits/mod.rs
+++ b/compiler/rustc_infer/src/traits/mod.rs
@@ -53,6 +53,12 @@ pub struct Obligation<'tcx, T> {
     pub recursion_depth: usize,
 }
 
+impl<'tcx, P> From<Obligation<'tcx, P>> for solve::Goal<'tcx, P> {
+    fn from(value: Obligation<'tcx, P>) -> Self {
+        solve::Goal { param_env: value.param_env, predicate: value.predicate }
+    }
+}
+
 pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>;
 pub type TraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>;
 
diff --git a/compiler/rustc_interface/locales/en-US.ftl b/compiler/rustc_interface/messages.ftl
index 37994899a20..37994899a20 100644
--- a/compiler/rustc_interface/locales/en-US.ftl
+++ b/compiler/rustc_interface/messages.ftl
diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs
index 1abbe8d4fab..15e02671075 100644
--- a/compiler/rustc_interface/src/lib.rs
+++ b/compiler/rustc_interface/src/lib.rs
@@ -31,4 +31,4 @@ pub use queries::Queries;
 #[cfg(test)]
 mod tests;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 81c1d665ef0..4a02981f954 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -11,7 +11,7 @@ use rustc_data_structures::parallel;
 use rustc_data_structures::steal::Steal;
 use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal};
 use rustc_errors::PResult;
-use rustc_expand::base::{ExtCtxt, LintStoreExpand, ResolverExpand};
+use rustc_expand::base::{ExtCtxt, LintStoreExpand};
 use rustc_hir::def_id::{StableCrateId, LOCAL_CRATE};
 use rustc_lint::{unerased_lint_store, BufferedEarlyLint, EarlyCheckNode, LintStore};
 use rustc_metadata::creader::CStore;
@@ -178,7 +178,7 @@ fn configure_and_expand(mut krate: ast::Crate, resolver: &mut Resolver<'_, '_>)
     let sess = tcx.sess;
     let lint_store = unerased_lint_store(tcx);
     let crate_name = tcx.crate_name(LOCAL_CRATE);
-    pre_expansion_lint(sess, lint_store, resolver.registered_tools(), &krate, crate_name);
+    pre_expansion_lint(sess, lint_store, tcx.registered_tools(()), &krate, crate_name);
     rustc_builtin_macros::register_builtin_macros(resolver);
 
     krate = sess.time("crate_injection", || {
@@ -302,6 +302,16 @@ fn configure_and_expand(mut krate: ast::Crate, resolver: &mut Resolver<'_, '_>)
 
     // Done with macro expansion!
 
+    resolver.resolve_crate(&krate);
+
+    krate
+}
+
+fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) {
+    let sess = tcx.sess;
+    let (resolver, krate) = &*tcx.resolver_for_lowering(()).borrow();
+    let mut lint_buffer = resolver.lint_buffer.steal();
+
     if sess.opts.unstable_opts.input_stats {
         eprintln!("Post-expansion node count: {}", count_nodes(&krate));
     }
@@ -310,8 +320,6 @@ fn configure_and_expand(mut krate: ast::Crate, resolver: &mut Resolver<'_, '_>)
         hir_stats::print_ast_stats(&krate, "POST EXPANSION AST STATS", "ast-stats-2");
     }
 
-    resolver.resolve_crate(&krate);
-
     // Needs to go *after* expansion to be able to check the results of macro expansion.
     sess.time("complete_gated_feature_checking", || {
         rustc_ast_passes::feature_gate::check_crate(&krate, sess);
@@ -321,7 +329,7 @@ fn configure_and_expand(mut krate: ast::Crate, resolver: &mut Resolver<'_, '_>)
     sess.parse_sess.buffered_lints.with_lock(|buffered_lints| {
         info!("{} parse sess buffered_lints", buffered_lints.len());
         for early_lint in buffered_lints.drain(..) {
-            resolver.lint_buffer().add_early_lint(early_lint);
+            lint_buffer.add_early_lint(early_lint);
         }
     });
 
@@ -340,20 +348,16 @@ fn configure_and_expand(mut krate: ast::Crate, resolver: &mut Resolver<'_, '_>)
         }
     });
 
-    sess.time("early_lint_checks", || {
-        let lint_buffer = Some(std::mem::take(resolver.lint_buffer()));
-        rustc_lint::check_ast_node(
-            sess,
-            false,
-            lint_store,
-            resolver.registered_tools(),
-            lint_buffer,
-            rustc_lint::BuiltinCombinedEarlyLintPass::new(),
-            &krate,
-        )
-    });
-
-    krate
+    let lint_store = unerased_lint_store(tcx);
+    rustc_lint::check_ast_node(
+        sess,
+        false,
+        lint_store,
+        tcx.registered_tools(()),
+        Some(lint_buffer),
+        rustc_lint::BuiltinCombinedEarlyLintPass::new(),
+        &**krate,
+    )
 }
 
 // Returns all the paths that correspond to generated files.
@@ -557,6 +561,7 @@ fn resolver_for_lowering<'tcx>(
     (): (),
 ) -> &'tcx Steal<(ty::ResolverAstLowering, Lrc<ast::Crate>)> {
     let arenas = Resolver::arenas();
+    let _ = tcx.registered_tools(()); // Uses `crate_for_resolver`.
     let krate = tcx.crate_for_resolver(()).steal();
     let mut resolver = Resolver::new(tcx, &krate, &arenas);
     let krate = configure_and_expand(krate, &mut resolver);
@@ -629,6 +634,7 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| {
     providers.hir_crate = rustc_ast_lowering::lower_to_hir;
     providers.output_filenames = output_filenames;
     providers.resolver_for_lowering = resolver_for_lowering;
+    providers.early_lint_checks = early_lint_checks;
     proc_macro_decls::provide(providers);
     rustc_const_eval::provide(providers);
     rustc_middle::hir::provide(providers);
@@ -637,6 +643,7 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| {
     rustc_mir_transform::provide(providers);
     rustc_monomorphize::provide(providers);
     rustc_privacy::provide(providers);
+    rustc_resolve::provide(providers);
     rustc_hir_analysis::provide(providers);
     rustc_hir_typeck::provide(providers);
     ty::provide(providers);
diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs
index a96cc95a384..58ad044b399 100644
--- a/compiler/rustc_interface/src/queries.rs
+++ b/compiler/rustc_interface/src/queries.rs
@@ -284,7 +284,11 @@ impl<'tcx> Queries<'tcx> {
         let codegen_backend = self.codegen_backend().clone();
 
         let (crate_hash, prepare_outputs, dep_graph) = self.global_ctxt()?.enter(|tcx| {
-            (tcx.crate_hash(LOCAL_CRATE), tcx.output_filenames(()).clone(), tcx.dep_graph.clone())
+            (
+                if tcx.sess.needs_crate_hash() { Some(tcx.crate_hash(LOCAL_CRATE)) } else { None },
+                tcx.output_filenames(()).clone(),
+                tcx.dep_graph.clone(),
+            )
         });
         let ongoing_codegen = self.ongoing_codegen()?.steal();
 
@@ -308,7 +312,8 @@ pub struct Linker {
     // compilation outputs
     dep_graph: DepGraph,
     prepare_outputs: Arc<OutputFilenames>,
-    crate_hash: Svh,
+    // Only present when incr. comp. is enabled.
+    crate_hash: Option<Svh>,
     ongoing_codegen: Box<dyn Any>,
 }
 
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index e5d2fb2ea28..043892410ce 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -110,7 +110,7 @@ pub fn create_session(
     add_configuration(&mut cfg, &mut sess, &*codegen_backend);
 
     let mut check_cfg = config::to_crate_check_config(check_cfg);
-    check_cfg.fill_well_known();
+    check_cfg.fill_well_known(&sess.target);
 
     sess.parse_sess.config = cfg;
     sess.parse_sess.check_config = check_cfg;
diff --git a/compiler/rustc_lexer/src/unescape.rs b/compiler/rustc_lexer/src/unescape.rs
index 8507ca9d89e..bb4d91247b8 100644
--- a/compiler/rustc_lexer/src/unescape.rs
+++ b/compiler/rustc_lexer/src/unescape.rs
@@ -298,10 +298,10 @@ where
         }
         let tail = &tail[first_non_space..];
         if let Some(c) = tail.chars().nth(0) {
-            // For error reporting, we would like the span to contain the character that was not
-            // skipped. The +1 is necessary to account for the leading \ that started the escape.
-            let end = start + first_non_space + c.len_utf8() + 1;
             if c.is_whitespace() {
+                // For error reporting, we would like the span to contain the character that was not
+                // skipped. The +1 is necessary to account for the leading \ that started the escape.
+                let end = start + first_non_space + c.len_utf8() + 1;
                 callback(start..end, Err(EscapeError::UnskippedWhitespaceWarning));
             }
         }
diff --git a/compiler/rustc_lint/locales/en-US.ftl b/compiler/rustc_lint/messages.ftl
index 68e62c9789a..68e62c9789a 100644
--- a/compiler/rustc_lint/locales/en-US.ftl
+++ b/compiler/rustc_lint/messages.ftl
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index 2fd0ef3cda8..a14dc20fca3 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -17,6 +17,11 @@ use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::Span;
 
 declare_tool_lint! {
+    /// The `default_hash_type` lint detects use of [`std::collections::HashMap`]/[`std::collections::HashSet`],
+    /// suggesting the use of `FxHashMap`/`FxHashSet`.
+    ///
+    /// This can help as `FxHasher` can perform better than the default hasher. DOS protection is not
+    /// required as input is assumed to be trusted.
     pub rustc::DEFAULT_HASH_TYPES,
     Allow,
     "forbid HashMap and HashSet and suggest the FxHash* variants",
@@ -67,6 +72,12 @@ fn typeck_results_of_method_fn<'tcx>(
 }
 
 declare_tool_lint! {
+    /// The `potential_query_instability` lint detects use of methods which can lead to
+    /// potential query instability, such as iterating over a `HashMap`.
+    ///
+    /// Due to the [incremental compilation](https://rustc-dev-guide.rust-lang.org/queries/incremental-compilation.html) model,
+    /// queries must return deterministic, stable results. `HashMap` iteration order can change between compilations,
+    /// and will introduce instability if query results expose the order.
     pub rustc::POTENTIAL_QUERY_INSTABILITY,
     Allow,
     "require explicit opt-in when using potentially unstable methods or functions",
@@ -92,6 +103,8 @@ impl LateLintPass<'_> for QueryStability {
 }
 
 declare_tool_lint! {
+    /// The `usage_of_ty_tykind` lint detects usages of `ty::TyKind::<kind>`,
+    /// where `ty::<kind>` would suffice.
     pub rustc::USAGE_OF_TY_TYKIND,
     Allow,
     "usage of `ty::TyKind` outside of the `ty::sty` module",
@@ -99,6 +112,8 @@ declare_tool_lint! {
 }
 
 declare_tool_lint! {
+    /// The `usage_of_qualified_ty` lint detects usages of `ty::TyKind`,
+    /// where `Ty` should be used instead.
     pub rustc::USAGE_OF_QUALIFIED_TY,
     Allow,
     "using `ty::{Ty,TyCtxt}` instead of importing it",
@@ -254,6 +269,8 @@ fn gen_args(segment: &PathSegment<'_>) -> String {
 }
 
 declare_tool_lint! {
+    /// The `lint_pass_impl_without_macro` detects manual implementations of a lint
+    /// pass, without using [`declare_lint_pass`] or [`impl_lint_pass`].
     pub rustc::LINT_PASS_IMPL_WITHOUT_MACRO,
     Allow,
     "`impl LintPass` without the `declare_lint_pass!` or `impl_lint_pass!` macros"
@@ -285,6 +302,8 @@ impl EarlyLintPass for LintPassImpl {
 }
 
 declare_tool_lint! {
+    /// The `existing_doc_keyword` lint detects use `#[doc()]` keywords
+    /// that don't exist, e.g. `#[doc(keyword = "..")]`.
     pub rustc::EXISTING_DOC_KEYWORD,
     Allow,
     "Check that documented keywords in std and core actually exist",
@@ -325,6 +344,10 @@ impl<'tcx> LateLintPass<'tcx> for ExistingDocKeyword {
 }
 
 declare_tool_lint! {
+    /// The `untranslatable_diagnostic` lint detects diagnostics created
+    /// without using translatable Fluent strings.
+    ///
+    /// More details on translatable diagnostics can be found [here](https://rustc-dev-guide.rust-lang.org/diagnostics/translation.html).
     pub rustc::UNTRANSLATABLE_DIAGNOSTIC,
     Allow,
     "prevent creation of diagnostics which cannot be translated",
@@ -332,6 +355,11 @@ declare_tool_lint! {
 }
 
 declare_tool_lint! {
+    /// The `diagnostic_outside_of_impl` lint detects diagnostics created manually,
+    /// and inside an `IntoDiagnostic`/`AddToDiagnostic` implementation,
+    /// or a `#[derive(Diagnostic)]`/`#[derive(Subdiagnostic)]` expansion.
+    ///
+    /// More details on diagnostics implementations can be found [here](https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-structs.html).
     pub rustc::DIAGNOSTIC_OUTSIDE_OF_IMPL,
     Allow,
     "prevent creation of diagnostics outside of `IntoDiagnostic`/`AddToDiagnostic` impls",
@@ -396,6 +424,8 @@ impl LateLintPass<'_> for Diagnostics {
 }
 
 declare_tool_lint! {
+    /// The `bad_opt_access` lint detects accessing options by field instad of
+    /// the wrapper function.
     pub rustc::BAD_OPT_ACCESS,
     Deny,
     "prevent using options by field access when there is a wrapper function",
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index bc7488fab4a..a76229dd352 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -128,7 +128,7 @@ fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExp
         },
         warn_about_weird_lints: false,
         store,
-        registered_tools: &tcx.resolutions(()).registered_tools,
+        registered_tools: &tcx.registered_tools(()),
     };
 
     builder.add_command_line();
@@ -156,7 +156,7 @@ fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLe
         },
         warn_about_weird_lints: false,
         store,
-        registered_tools: &tcx.resolutions(()).registered_tools,
+        registered_tools: &tcx.registered_tools(()),
     };
 
     if owner == hir::CRATE_OWNER_ID {
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 35dc533e56c..b3578540516 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -126,7 +126,7 @@ pub use rustc_session::lint::Level::{self, *};
 pub use rustc_session::lint::{BufferedEarlyLint, FutureIncompatibleInfo, Lint, LintId};
 pub use rustc_session::lint::{LintArray, LintPass};
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 pub fn provide(providers: &mut Providers) {
     levels::provide(providers);
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index c43162f6325..2ba365e298f 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -1349,9 +1349,8 @@ declare_lint! {
     /// ### Example
     ///
     /// ```rust
-    /// #![feature(box_syntax)]
     /// fn main() {
-    ///     let a = (box [1, 2, 3]).len();
+    ///     let a = Box::new([1, 2, 3]).len();
     /// }
     /// ```
     ///
@@ -1373,6 +1372,11 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAllocation {
     fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
         match e.kind {
             hir::ExprKind::Box(_) => {}
+            hir::ExprKind::Call(path_expr, [_])
+                if let hir::ExprKind::Path(qpath) = &path_expr.kind
+                && let Some(did) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()
+                && cx.tcx.is_diagnostic_item(sym::box_new, did)
+                => {}
             _ => return,
         }
 
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 534aff7fb62..6f22bdabff4 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -8,7 +8,7 @@ extern crate rustc_macros;
 pub use self::Level::*;
 use rustc_ast::node_id::NodeId;
 use rustc_ast::{AttrId, Attribute};
-use rustc_data_structures::fx::FxIndexMap;
+use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
 use rustc_error_messages::{DiagnosticMessage, MultiSpan};
 use rustc_hir::HashStableContext;
@@ -533,6 +533,7 @@ pub enum BuiltinLintDiagnostics {
 
 /// Lints that are buffered up early on in the `Session` before the
 /// `LintLevels` is calculated.
+#[derive(Debug)]
 pub struct BufferedEarlyLint {
     /// The span of code that we are linting on.
     pub span: MultiSpan,
@@ -551,7 +552,7 @@ pub struct BufferedEarlyLint {
     pub diagnostic: BuiltinLintDiagnostics,
 }
 
-#[derive(Default)]
+#[derive(Default, Debug)]
 pub struct LintBuffer {
     pub map: FxIndexMap<NodeId, Vec<BufferedEarlyLint>>,
 }
@@ -601,6 +602,8 @@ impl LintBuffer {
     }
 }
 
+pub type RegisteredTools = FxIndexSet<Ident>;
+
 /// Declares a static item of type `&'static Lint`.
 ///
 /// See <https://rustc-dev-guide.rust-lang.org/diagnostics.html> for
diff --git a/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp
index 974207e918c..0493d6b05d0 100644
--- a/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp
@@ -9,7 +9,6 @@
 
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/Object/ObjectFile.h"
-#include "llvm/ADT/Optional.h"
 
 using namespace llvm;
 using namespace llvm::sys;
diff --git a/compiler/rustc_metadata/locales/en-US.ftl b/compiler/rustc_metadata/messages.ftl
index 79b8b417257..79b8b417257 100644
--- a/compiler/rustc_metadata/locales/en-US.ftl
+++ b/compiler/rustc_metadata/messages.ftl
diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs
index f6431899731..08de828fbdb 100644
--- a/compiler/rustc_metadata/src/fs.rs
+++ b/compiler/rustc_metadata/src/fs.rs
@@ -6,9 +6,9 @@ use crate::{encode_metadata, EncodedMetadata};
 use rustc_data_structures::temp_dir::MaybeTempDir;
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_middle::ty::TyCtxt;
-use rustc_session::config::{CrateType, OutputType};
+use rustc_session::config::OutputType;
 use rustc_session::output::filename_for_metadata;
-use rustc_session::Session;
+use rustc_session::{MetadataKind, Session};
 use tempfile::Builder as TempFileBuilder;
 
 use std::fs;
@@ -39,27 +39,6 @@ pub fn emit_wrapper_file(
 }
 
 pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
-    #[derive(PartialEq, Eq, PartialOrd, Ord)]
-    enum MetadataKind {
-        None,
-        Uncompressed,
-        Compressed,
-    }
-
-    let metadata_kind = tcx
-        .sess
-        .crate_types()
-        .iter()
-        .map(|ty| match *ty {
-            CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => MetadataKind::None,
-
-            CrateType::Rlib => MetadataKind::Uncompressed,
-
-            CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed,
-        })
-        .max()
-        .unwrap_or(MetadataKind::None);
-
     let crate_name = tcx.crate_name(LOCAL_CRATE);
     let out_filename = filename_for_metadata(tcx.sess, crate_name, tcx.output_filenames(()));
     // To avoid races with another rustc process scanning the output directory,
@@ -76,6 +55,7 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
 
     // Always create a file at `metadata_filename`, even if we have nothing to write to it.
     // This simplifies the creation of the output `out_filename` when requested.
+    let metadata_kind = tcx.sess.metadata_kind();
     match metadata_kind {
         MetadataKind::None => {
             std::fs::File::create(&metadata_filename).unwrap_or_else(|err| {
diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs
index 6f6d3731cea..98d9ad31fe0 100644
--- a/compiler/rustc_metadata/src/lib.rs
+++ b/compiler/rustc_metadata/src/lib.rs
@@ -47,4 +47,4 @@ pub use fs::{emit_wrapper_file, METADATA_FILENAME};
 pub use native_libs::find_native_static_library;
 pub use rmeta::{encode_metadata, EncodedMetadata, METADATA_HEADER};
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index b1e59b0a470..771fb091134 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -311,8 +311,11 @@ impl<T: ParameterizedOverTcx> LazyArray<T> {
 impl<'a, 'tcx> DecodeContext<'a, 'tcx> {
     #[inline]
     fn tcx(&self) -> TyCtxt<'tcx> {
-        debug_assert!(self.tcx.is_some(), "missing TyCtxt in DecodeContext");
-        self.tcx.unwrap()
+        let Some(tcx) = self.tcx else {
+            bug!("No TyCtxt found for decoding. \
+                You need to explicitly pass `(crate_metadata_ref, tcx)` to `decode` instead of just `crate_metadata_ref`.");
+        };
+        tcx
     }
 
     #[inline]
@@ -454,7 +457,12 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for ast::AttrId {
 impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for SyntaxContext {
     fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> SyntaxContext {
         let cdata = decoder.cdata();
-        let sess = decoder.sess.unwrap();
+
+        let Some(sess) = decoder.sess else {
+            bug!("Cannot decode SyntaxContext without Session.\
+                You need to explicitly pass `(crate_metadata_ref, tcx)` to `decode` instead of just `crate_metadata_ref`.");
+        };
+
         let cname = cdata.root.name;
         rustc_span::hygiene::decode_syntax_context(decoder, &cdata.hygiene_context, |_, id| {
             debug!("SpecializedDecoder<SyntaxContext>: decoding {}", id);
@@ -471,7 +479,11 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for SyntaxContext {
 impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for ExpnId {
     fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> ExpnId {
         let local_cdata = decoder.cdata();
-        let sess = decoder.sess.unwrap();
+
+        let Some(sess) = decoder.sess else {
+            bug!("Cannot decode ExpnId without Session. \
+                You need to explicitly pass `(crate_metadata_ref, tcx)` to `decode` instead of just `crate_metadata_ref`.");
+        };
 
         let cnum = CrateNum::decode(decoder);
         let index = u32::decode(decoder);
@@ -520,7 +532,8 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for Span {
         let hi = lo + len;
 
         let Some(sess) = decoder.sess else {
-            bug!("Cannot decode Span without Session.")
+            bug!("Cannot decode Span without Session. \
+                You need to explicitly pass `(crate_metadata_ref, tcx)` to `decode` instead of just `crate_metadata_ref`.")
         };
 
         // Index of the file in the corresponding crate's list of encoded files.
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 3ab01f7809b..16306bef42e 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1104,7 +1104,7 @@ fn should_encode_const(def_kind: DefKind) -> bool {
 // We only encode impl trait in trait when using `lower-impl-trait-in-trait-to-assoc-ty` unstable
 // option.
 fn should_encode_fn_impl_trait_in_trait<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
-    if tcx.sess.opts.unstable_opts.lower_impl_trait_in_trait_to_assoc_ty
+    if tcx.lower_impl_trait_in_trait_to_assoc_ty()
         && let Some(assoc_item) = tcx.opt_associated_item(def_id)
         && assoc_item.container == ty::AssocItemContainer::TraitContainer
         && assoc_item.kind == ty::AssocKind::Fn
@@ -2050,13 +2050,13 @@ fn prefetch_mir(tcx: TyCtxt<'_>) {
         let (encode_const, encode_opt) = should_encode_mir(tcx, def_id);
 
         if encode_const {
-            tcx.ensure().mir_for_ctfe(def_id);
+            tcx.ensure_with_value().mir_for_ctfe(def_id);
         }
         if encode_opt {
-            tcx.ensure().optimized_mir(def_id);
+            tcx.ensure_with_value().optimized_mir(def_id);
         }
         if encode_opt || encode_const {
-            tcx.ensure().promoted_mir(def_id);
+            tcx.ensure_with_value().promoted_mir(def_id);
         }
     })
 }
diff --git a/compiler/rustc_middle/locales/en-US.ftl b/compiler/rustc_middle/messages.ftl
index 4f4e5c6a2c9..4f4e5c6a2c9 100644
--- a/compiler/rustc_middle/locales/en-US.ftl
+++ b/compiler/rustc_middle/messages.ftl
diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs
index 62e44b6298b..72907fba5e6 100644
--- a/compiler/rustc_middle/src/arena.rs
+++ b/compiler/rustc_middle/src/arena.rs
@@ -94,7 +94,8 @@ macro_rules! arena_types {
             [] object_safety_violations: rustc_middle::traits::ObjectSafetyViolation,
             [] codegen_unit: rustc_middle::mir::mono::CodegenUnit<'tcx>,
             [decode] attribute: rustc_ast::Attribute,
-            [] name_set: rustc_data_structures::fx::FxHashSet<rustc_span::symbol::Symbol>,
+            [] name_set: rustc_data_structures::unord::UnordSet<rustc_span::symbol::Symbol>,
+            [] ordered_name_set: rustc_data_structures::fx::FxIndexSet<rustc_span::symbol::Symbol>,
             [] hir_id_set: rustc_hir::HirIdSet,
 
             // Interned types
@@ -107,6 +108,7 @@ macro_rules! arena_types {
             // (during lowering) and the `librustc_middle` arena (for decoding MIR)
             [decode] asm_template: rustc_ast::InlineAsmTemplatePiece,
             [decode] used_trait_imports: rustc_data_structures::unord::UnordSet<rustc_hir::def_id::LocalDefId>,
+            [decode] registered_tools: rustc_middle::ty::RegisteredTools,
             [decode] is_late_bound_map: rustc_data_structures::fx::FxIndexSet<rustc_hir::ItemLocalId>,
             [decode] impl_source: rustc_middle::traits::ImplSource<'tcx, ()>,
 
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index 43eef1c770c..746cf488589 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -1134,7 +1134,7 @@ impl<'hir> intravisit::Map<'hir> for Map<'hir> {
 pub(super) fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh {
     debug_assert_eq!(crate_num, LOCAL_CRATE);
     let krate = tcx.hir_crate(());
-    let hir_body_hash = krate.hir_hash;
+    let hir_body_hash = krate.opt_hir_hash.expect("HIR hash missing while computing crate hash");
 
     let upstream_crates = upstream_crates(tcx);
 
diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs
index 6706b9db3f5..6c0566cd9e8 100644
--- a/compiler/rustc_middle/src/hir/mod.rs
+++ b/compiler/rustc_middle/src/hir/mod.rs
@@ -8,7 +8,6 @@ pub mod place;
 
 use crate::ty::query::Providers;
 use crate::ty::{ImplSubject, TyCtxt};
-use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync::{par_for_each_in, Send, Sync};
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -24,14 +23,15 @@ use rustc_span::{ExpnId, DUMMY_SP};
 #[derive(Copy, Clone, Debug)]
 pub struct Owner<'tcx> {
     node: OwnerNode<'tcx>,
-    hash_without_bodies: Fingerprint,
 }
 
 impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Owner<'tcx> {
     #[inline]
     fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
-        let Owner { node: _, hash_without_bodies } = self;
-        hash_without_bodies.hash_stable(hcx, hasher)
+        // Perform a shallow hash instead using the deep hash saved in `OwnerNodes`. This lets us
+        // differentiate queries that depend on the full HIR tree from those that only depend on
+        // the item signature.
+        hcx.without_hir_bodies(|hcx| self.node.hash_stable(hcx, hasher));
     }
 }
 
@@ -123,7 +123,7 @@ pub fn provide(providers: &mut Providers) {
     providers.hir_owner = |tcx, id| {
         let owner = tcx.hir_crate(()).owners.get(id.def_id)?.as_owner()?;
         let node = owner.node();
-        Some(Owner { node, hash_without_bodies: owner.nodes.hash_without_bodies })
+        Some(Owner { node })
     };
     providers.opt_local_def_id_to_hir_id = |tcx, id| {
         let owner = tcx.hir_crate(()).owners[id].map(|_| ());
diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs
index 41d8c7ffdb9..d83a587a86a 100644
--- a/compiler/rustc_middle/src/infer/unify_key.rs
+++ b/compiler/rustc_middle/src/infer/unify_key.rs
@@ -1,4 +1,4 @@
-use crate::ty::{self, Ty, TyCtxt};
+use crate::ty::{self, Region, Ty, TyCtxt};
 use rustc_data_structures::unify::{NoError, UnifyKey, UnifyValue};
 use rustc_span::def_id::DefId;
 use rustc_span::symbol::Symbol;
@@ -11,7 +11,20 @@ pub trait ToType {
 }
 
 #[derive(PartialEq, Copy, Clone, Debug)]
-pub struct UnifiedRegion<'tcx>(pub Option<ty::Region<'tcx>>);
+pub struct UnifiedRegion<'tcx> {
+    value: Option<ty::Region<'tcx>>,
+}
+
+impl<'tcx> UnifiedRegion<'tcx> {
+    pub fn new(value: Option<Region<'tcx>>) -> Self {
+        Self { value }
+    }
+
+    /// The caller is responsible for checking universe compatibility before using this value.
+    pub fn get_value_ignoring_universes(self) -> Option<Region<'tcx>> {
+        self.value
+    }
+}
 
 #[derive(PartialEq, Copy, Clone, Debug)]
 pub struct RegionVidKey<'tcx> {
@@ -44,11 +57,27 @@ impl<'tcx> UnifyValue for UnifiedRegion<'tcx> {
     type Error = NoError;
 
     fn unify_values(value1: &Self, value2: &Self) -> Result<Self, NoError> {
-        Ok(match (value1.0, value2.0) {
+        // We pick the value of the least universe because it is compatible with more variables.
+        // This is *not* neccessary for soundness, but it allows more region variables to be
+        // resolved to the said value.
+        #[cold]
+        fn min_universe<'tcx>(r1: Region<'tcx>, r2: Region<'tcx>) -> Region<'tcx> {
+            cmp::min_by_key(r1, r2, |r| match r.kind() {
+                ty::ReStatic
+                | ty::ReErased
+                | ty::ReFree(..)
+                | ty::ReEarlyBound(..)
+                | ty::ReError(_) => ty::UniverseIndex::ROOT,
+                ty::RePlaceholder(placeholder) => placeholder.universe,
+                ty::ReVar(..) | ty::ReLateBound(..) => bug!("not a universal region"),
+            })
+        }
+
+        Ok(match (value1.value, value2.value) {
             // Here we can just pick one value, because the full constraints graph
             // will be handled later. Ideally, we might want a `MultipleValues`
             // variant or something. For now though, this is fine.
-            (Some(_), Some(_)) => *value1,
+            (Some(val1), Some(val2)) => Self { value: Some(min_universe(val1, val2)) },
 
             (Some(_), _) => *value1,
             (_, Some(_)) => *value2,
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs
index c33b9d84eb0..45c4a1057d2 100644
--- a/compiler/rustc_middle/src/lib.rs
+++ b/compiler/rustc_middle/src/lib.rs
@@ -109,4 +109,4 @@ pub mod util {
 // Allows macros to refer to this crate as `::rustc_middle`
 extern crate self as rustc_middle;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 6e6bb8ce95e..5215e3db798 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -1453,6 +1453,9 @@ impl Debug for Statement<'_> {
                 write!(fmt, "discriminant({:?}) = {:?}", place, variant_index)
             }
             Deinit(ref place) => write!(fmt, "Deinit({:?})", place),
+            PlaceMention(ref place) => {
+                write!(fmt, "PlaceMention({:?})", place)
+            }
             AscribeUserType(box (ref place, ref c_ty), ref variance) => {
                 write!(fmt, "AscribeUserType({:?}, {:?}, {:?})", place, variance, c_ty)
             }
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index d8829e3e782..7e51953599d 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -123,6 +123,7 @@ fn dump_matched_mir_node<'tcx, F>(
         // see notes on #41697 above
         let def_path =
             ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id()));
+        // ignore-tidy-odd-backticks the literal below is fine
         write!(file, "// MIR for `{}", def_path)?;
         match body.source.promoted {
             None => write!(file, "`")?,
diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs
index b964c1852d2..d85d68870d7 100644
--- a/compiler/rustc_middle/src/mir/query.rs
+++ b/compiler/rustc_middle/src/mir/query.rs
@@ -2,7 +2,7 @@
 
 use crate::mir::{Body, ConstantKind, Promoted};
 use crate::ty::{self, OpaqueHiddenType, Ty, TyCtxt};
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::unord::UnordSet;
 use rustc_data_structures::vec_map::VecMap;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir as hir;
@@ -123,7 +123,7 @@ pub struct UnsafetyCheckResult {
     pub violations: Vec<UnsafetyViolation>,
 
     /// Used `unsafe` blocks in this function. This is used for the "unused_unsafe" lint.
-    pub used_unsafe_blocks: FxHashSet<hir::HirId>,
+    pub used_unsafe_blocks: UnordSet<hir::HirId>,
 
     /// This is `Some` iff the item is not a closure.
     pub unused_unsafes: Option<Vec<(hir::HirId, UnusedUnsafe)>>,
diff --git a/compiler/rustc_middle/src/mir/spanview.rs b/compiler/rustc_middle/src/mir/spanview.rs
index 28a3b51b7fc..1a23f9dadd4 100644
--- a/compiler/rustc_middle/src/mir/spanview.rs
+++ b/compiler/rustc_middle/src/mir/spanview.rs
@@ -247,6 +247,7 @@ pub fn statement_kind_name(statement: &Statement<'_>) -> &'static str {
         StorageLive(..) => "StorageLive",
         StorageDead(..) => "StorageDead",
         Retag(..) => "Retag",
+        PlaceMention(..) => "PlaceMention",
         AscribeUserType(..) => "AscribeUserType",
         Coverage(..) => "Coverage",
         Intrinsic(..) => "Intrinsic",
@@ -265,7 +266,6 @@ pub fn terminator_kind_name(term: &Terminator<'_>) -> &'static str {
         Return => "Return",
         Unreachable => "Unreachable",
         Drop { .. } => "Drop",
-        DropAndReplace { .. } => "DropAndReplace",
         Call { .. } => "Call",
         Assert { .. } => "Assert",
         Yield { .. } => "Yield",
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index ae09562a85e..9312e27df37 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -133,7 +133,6 @@ pub enum AnalysisPhase {
 pub enum RuntimePhase {
     /// In addition to the semantic changes, beginning with this phase, the following variants are
     /// disallowed:
-    /// * [`TerminatorKind::DropAndReplace`]
     /// * [`TerminatorKind::Yield`]
     /// * [`TerminatorKind::GeneratorDrop`]
     /// * [`Rvalue::Aggregate`] for any `AggregateKind` except `Array`
@@ -326,6 +325,15 @@ pub enum StatementKind<'tcx> {
     /// Only `RetagKind::Default` and `RetagKind::FnEntry` are permitted.
     Retag(RetagKind, Box<Place<'tcx>>),
 
+    /// This statement exists to preserve a trace of a scrutinee matched against a wildcard binding.
+    /// This is especially useful for `let _ = PLACE;` bindings that desugar to a single
+    /// `PlaceMention(PLACE)`.
+    ///
+    /// When executed at runtime this is a nop.
+    ///
+    /// Disallowed after drop elaboration.
+    PlaceMention(Box<Place<'tcx>>),
+
     /// Encodes a user's type ascription. These need to be preserved
     /// intact so that NLL can respect them. For example:
     /// ```ignore (illustrative)
@@ -596,43 +604,6 @@ pub enum TerminatorKind<'tcx> {
     /// > consider indirect assignments.
     Drop { place: Place<'tcx>, target: BasicBlock, unwind: Option<BasicBlock> },
 
-    /// Drops the place and assigns a new value to it.
-    ///
-    /// This first performs the exact same operation as the pre drop-elaboration `Drop` terminator;
-    /// it then additionally assigns the `value` to the `place` as if by an assignment statement.
-    /// This assignment occurs both in the unwind and the regular code paths. The semantics are best
-    /// explained by the elaboration:
-    ///
-    /// ```ignore (MIR)
-    /// BB0 {
-    ///   DropAndReplace(P <- V, goto BB1, unwind BB2)
-    /// }
-    /// ```
-    ///
-    /// becomes
-    ///
-    /// ```ignore (MIR)
-    /// BB0 {
-    ///   Drop(P, goto BB1, unwind BB2)
-    /// }
-    /// BB1 {
-    ///   // P is now uninitialized
-    ///   P <- V
-    /// }
-    /// BB2 {
-    ///   // P is now uninitialized -- its dtor panicked
-    ///   P <- V
-    /// }
-    /// ```
-    ///
-    /// Disallowed after drop elaboration.
-    DropAndReplace {
-        place: Place<'tcx>,
-        value: Operand<'tcx>,
-        target: BasicBlock,
-        unwind: Option<BasicBlock>,
-    },
-
     /// Roughly speaking, evaluates the `func` operand and the arguments, and starts execution of
     /// the referred to function. The operand types must match the argument types of the function.
     /// The return place type must match the return type. The type of the `func` operand must be
diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs
index 6e905224c13..cd970270727 100644
--- a/compiler/rustc_middle/src/mir/terminator.rs
+++ b/compiler/rustc_middle/src/mir/terminator.rs
@@ -148,7 +148,6 @@ impl<'tcx> TerminatorKind<'tcx> {
             | Call { target: None, cleanup: Some(t), .. }
             | Call { target: Some(t), cleanup: None, .. }
             | Yield { resume: t, drop: None, .. }
-            | DropAndReplace { target: t, unwind: None, .. }
             | Drop { target: t, unwind: None, .. }
             | Assert { target: t, cleanup: None, .. }
             | FalseUnwind { real_target: t, unwind: None }
@@ -158,7 +157,6 @@ impl<'tcx> TerminatorKind<'tcx> {
             }
             Call { target: Some(t), cleanup: Some(ref u), .. }
             | Yield { resume: t, drop: Some(ref u), .. }
-            | DropAndReplace { target: t, unwind: Some(ref u), .. }
             | Drop { target: t, unwind: Some(ref u), .. }
             | Assert { target: t, cleanup: Some(ref u), .. }
             | FalseUnwind { real_target: t, unwind: Some(ref u) }
@@ -188,7 +186,6 @@ impl<'tcx> TerminatorKind<'tcx> {
             | Call { target: None, cleanup: Some(ref mut t), .. }
             | Call { target: Some(ref mut t), cleanup: None, .. }
             | Yield { resume: ref mut t, drop: None, .. }
-            | DropAndReplace { target: ref mut t, unwind: None, .. }
             | Drop { target: ref mut t, unwind: None, .. }
             | Assert { target: ref mut t, cleanup: None, .. }
             | FalseUnwind { real_target: ref mut t, unwind: None }
@@ -198,7 +195,6 @@ impl<'tcx> TerminatorKind<'tcx> {
             }
             Call { target: Some(ref mut t), cleanup: Some(ref mut u), .. }
             | Yield { resume: ref mut t, drop: Some(ref mut u), .. }
-            | DropAndReplace { target: ref mut t, unwind: Some(ref mut u), .. }
             | Drop { target: ref mut t, unwind: Some(ref mut u), .. }
             | Assert { target: ref mut t, cleanup: Some(ref mut u), .. }
             | FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) }
@@ -225,7 +221,6 @@ impl<'tcx> TerminatorKind<'tcx> {
             | TerminatorKind::FalseEdge { .. } => None,
             TerminatorKind::Call { cleanup: ref unwind, .. }
             | TerminatorKind::Assert { cleanup: ref unwind, .. }
-            | TerminatorKind::DropAndReplace { ref unwind, .. }
             | TerminatorKind::Drop { ref unwind, .. }
             | TerminatorKind::FalseUnwind { ref unwind, .. }
             | TerminatorKind::InlineAsm { cleanup: ref unwind, .. } => Some(unwind),
@@ -245,7 +240,6 @@ impl<'tcx> TerminatorKind<'tcx> {
             | TerminatorKind::FalseEdge { .. } => None,
             TerminatorKind::Call { cleanup: ref mut unwind, .. }
             | TerminatorKind::Assert { cleanup: ref mut unwind, .. }
-            | TerminatorKind::DropAndReplace { ref mut unwind, .. }
             | TerminatorKind::Drop { ref mut unwind, .. }
             | TerminatorKind::FalseUnwind { ref mut unwind, .. }
             | TerminatorKind::InlineAsm { cleanup: ref mut unwind, .. } => Some(unwind),
@@ -309,9 +303,6 @@ impl<'tcx> TerminatorKind<'tcx> {
             Yield { value, resume_arg, .. } => write!(fmt, "{:?} = yield({:?})", resume_arg, value),
             Unreachable => write!(fmt, "unreachable"),
             Drop { place, .. } => write!(fmt, "drop({:?})", place),
-            DropAndReplace { place, value, .. } => {
-                write!(fmt, "replace({:?} <- {:?})", place, value)
-            }
             Call { func, args, destination, .. } => {
                 write!(fmt, "{:?} = ", destination)?;
                 write!(fmt, "{:?}(", func)?;
@@ -403,10 +394,10 @@ impl<'tcx> TerminatorKind<'tcx> {
             Call { target: None, cleanup: None, .. } => vec![],
             Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()],
             Yield { drop: None, .. } => vec!["resume".into()],
-            DropAndReplace { unwind: None, .. } | Drop { unwind: None, .. } => {
+            Drop { unwind: None, .. } => {
                 vec!["return".into()]
             }
-            DropAndReplace { unwind: Some(_), .. } | Drop { unwind: Some(_), .. } => {
+            Drop { unwind: Some(_), .. } => {
                 vec!["return".into(), "unwind".into()]
             }
             Assert { cleanup: None, .. } => vec!["".into()],
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 5c056b29975..cbeacf21c19 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -405,6 +405,13 @@ macro_rules! make_mir_visitor {
                     StatementKind::Retag(kind, place) => {
                         self.visit_retag($(& $mutability)? *kind, place, location);
                     }
+                    StatementKind::PlaceMention(place) => {
+                        self.visit_place(
+                            place,
+                            PlaceContext::NonUse(NonUseContext::PlaceMention),
+                            location
+                        );
+                    }
                     StatementKind::AscribeUserType(
                         box (place, user_ty),
                         variance
@@ -495,20 +502,6 @@ macro_rules! make_mir_visitor {
                         );
                     }
 
-                    TerminatorKind::DropAndReplace {
-                        place,
-                        value,
-                        target: _,
-                        unwind: _,
-                    } => {
-                        self.visit_place(
-                            place,
-                            PlaceContext::MutatingUse(MutatingUseContext::Drop),
-                            location
-                        );
-                        self.visit_operand(value, location);
-                    }
-
                     TerminatorKind::Call {
                         func,
                         args,
@@ -1302,6 +1295,8 @@ pub enum NonUseContext {
     AscribeUserTy,
     /// The data of a user variable, for debug info.
     VarDebugInfo,
+    /// PlaceMention statement.
+    PlaceMention,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 5133da3429a..12dc00c3228 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -26,6 +26,15 @@ rustc_queries! {
         desc { "triggering a delay span bug" }
     }
 
+    query registered_tools(_: ()) -> &'tcx ty::RegisteredTools {
+        arena_cache
+        desc { "compute registered tools for crate" }
+    }
+
+    query early_lint_checks(_: ()) -> () {
+        desc { "perform lints prior to macro expansion" }
+    }
+
     query resolutions(_: ()) -> &'tcx ty::ResolverGlobalCtxt {
         feedable
         no_hash
@@ -764,7 +773,7 @@ rustc_queries! {
     ///
     /// The map returned for `tcx.impl_item_implementor_ids(impl_id)` would be
     ///`{ trait_f: impl_f, trait_g: impl_g }`
-    query impl_item_implementor_ids(impl_id: DefId) -> &'tcx FxHashMap<DefId, DefId> {
+    query impl_item_implementor_ids(impl_id: DefId) -> &'tcx DefIdMap<DefId> {
         arena_cache
         desc { |tcx| "comparing impl items against trait for `{}`", tcx.def_path_str(impl_id) }
     }
@@ -906,8 +915,8 @@ rustc_queries! {
     /// The second return value maps from ADTs to ignored derived traits (e.g. Debug and Clone) and
     /// their respective impl (i.e., part of the derive macro)
     query live_symbols_and_ignored_derived_traits(_: ()) -> &'tcx (
-        FxHashSet<LocalDefId>,
-        FxHashMap<LocalDefId, Vec<(DefId, DefId)>>
+        LocalDefIdSet,
+        LocalDefIdMap<Vec<(DefId, DefId)>>
     ) {
         arena_cache
         desc { "finding live symbols in crate" }
@@ -1120,7 +1129,7 @@ rustc_queries! {
         desc { "checking for private elements in public interfaces" }
     }
 
-    query reachable_set(_: ()) -> &'tcx FxHashSet<LocalDefId> {
+    query reachable_set(_: ()) -> &'tcx LocalDefIdSet {
         arena_cache
         desc { "reachability" }
     }
@@ -1229,7 +1238,7 @@ rustc_queries! {
         separate_provide_extern
     }
 
-    query asm_target_features(def_id: DefId) -> &'tcx FxHashSet<Symbol> {
+    query asm_target_features(def_id: DefId) -> &'tcx FxIndexSet<Symbol> {
         desc { |tcx| "computing target features for inline asm of `{}`", tcx.def_path_str(def_id) }
     }
 
@@ -1324,6 +1333,7 @@ rustc_queries! {
     /// might want to use `reveal_all()` method to change modes.
     query param_env(def_id: DefId) -> ty::ParamEnv<'tcx> {
         desc { |tcx| "computing normalized predicates of `{}`", tcx.def_path_str(def_id) }
+        feedable
     }
 
     /// Like `param_env`, but returns the `ParamEnv` in `Reveal::All` mode.
@@ -1845,7 +1855,7 @@ rustc_queries! {
     query maybe_unused_trait_imports(_: ()) -> &'tcx FxIndexSet<LocalDefId> {
         desc { "fetching potentially unused trait imports" }
     }
-    query names_imported_by_glob_use(def_id: LocalDefId) -> &'tcx FxHashSet<Symbol> {
+    query names_imported_by_glob_use(def_id: LocalDefId) -> &'tcx UnordSet<Symbol> {
         desc { |tcx| "finding names imported by glob use for `{}`", tcx.def_path_str(def_id.to_def_id()) }
     }
 
diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs
index bd43867a3da..92d3e73e683 100644
--- a/compiler/rustc_middle/src/traits/solve.rs
+++ b/compiler/rustc_middle/src/traits/solve.rs
@@ -1,12 +1,104 @@
 use std::ops::ControlFlow;
 
 use rustc_data_structures::intern::Interned;
+use rustc_query_system::cache::Cache;
 
-use crate::infer::canonical::QueryRegionConstraints;
+use crate::infer::canonical::{CanonicalVarValues, QueryRegionConstraints};
+use crate::traits::query::NoSolution;
+use crate::traits::Canonical;
 use crate::ty::{
-    FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor,
+    self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable,
+    TypeVisitor,
 };
 
+pub type EvaluationCache<'tcx> = Cache<CanonicalGoal<'tcx>, QueryResult<'tcx>>;
+
+/// A goal is a statement, i.e. `predicate`, we want to prove
+/// given some assumptions, i.e. `param_env`.
+///
+/// Most of the time the `param_env` contains the `where`-bounds of the function
+/// we're currently typechecking while the `predicate` is some trait bound.
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
+pub struct Goal<'tcx, P> {
+    pub param_env: ty::ParamEnv<'tcx>,
+    pub predicate: P,
+}
+
+impl<'tcx, P> Goal<'tcx, P> {
+    pub fn new(
+        tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        predicate: impl ToPredicate<'tcx, P>,
+    ) -> Goal<'tcx, P> {
+        Goal { param_env, predicate: predicate.to_predicate(tcx) }
+    }
+
+    /// Updates the goal to one with a different `predicate` but the same `param_env`.
+    pub fn with<Q>(self, tcx: TyCtxt<'tcx>, predicate: impl ToPredicate<'tcx, Q>) -> Goal<'tcx, Q> {
+        Goal { param_env: self.param_env, predicate: predicate.to_predicate(tcx) }
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
+pub struct Response<'tcx> {
+    pub var_values: CanonicalVarValues<'tcx>,
+    /// Additional constraints returned by this query.
+    pub external_constraints: ExternalConstraints<'tcx>,
+    pub certainty: Certainty,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
+pub enum Certainty {
+    Yes,
+    Maybe(MaybeCause),
+}
+
+impl Certainty {
+    pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity);
+
+    /// When proving multiple goals using **AND**, e.g. nested obligations for an impl,
+    /// use this function to unify the certainty of these goals
+    pub fn unify_and(self, other: Certainty) -> Certainty {
+        match (self, other) {
+            (Certainty::Yes, Certainty::Yes) => Certainty::Yes,
+            (Certainty::Yes, Certainty::Maybe(_)) => other,
+            (Certainty::Maybe(_), Certainty::Yes) => self,
+            (Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Overflow)) => {
+                Certainty::Maybe(MaybeCause::Overflow)
+            }
+            // If at least one of the goals is ambiguous, hide the overflow as the ambiguous goal
+            // may still result in failure.
+            (Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(_))
+            | (Certainty::Maybe(_), Certainty::Maybe(MaybeCause::Ambiguity)) => {
+                Certainty::Maybe(MaybeCause::Ambiguity)
+            }
+        }
+    }
+}
+
+/// Why we failed to evaluate a goal.
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
+pub enum MaybeCause {
+    /// We failed due to ambiguity. This ambiguity can either
+    /// be a true ambiguity, i.e. there are multiple different answers,
+    /// or we hit a case where we just don't bother, e.g. `?x: Trait` goals.
+    Ambiguity,
+    /// We gave up due to an overflow, most often by hitting the recursion limit.
+    Overflow,
+}
+
+pub type CanonicalGoal<'tcx, T = ty::Predicate<'tcx>> = Canonical<'tcx, Goal<'tcx, T>>;
+
+pub type CanonicalResponse<'tcx> = Canonical<'tcx, Response<'tcx>>;
+
+/// The result of evaluating a canonical query.
+///
+/// FIXME: We use a different type than the existing canonical queries. This is because
+/// we need to add a `Certainty` for `overflow` and may want to restructure this code without
+/// having to worry about changes to currently used code. Once we've made progress on this
+/// solver, merge the two responses again.
+pub type QueryResult<'tcx> = Result<CanonicalResponse<'tcx>, NoSolution>;
+
 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
 pub struct ExternalConstraints<'tcx>(pub(crate) Interned<'tcx, ExternalConstraintsData<'tcx>>);
 
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index 5d0cf23280b..42101f6b931 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -135,6 +135,9 @@ impl<'tcx> Const<'tcx> {
                 _,
                 &hir::Path { res: Res::Def(DefKind::ConstParam, def_id), .. },
             )) => {
+                // Use the type from the param's definition, since we can resolve it,
+                // not the expected parameter type from WithOptConstParam.
+                let param_ty = tcx.type_of(def_id).subst_identity();
                 match tcx.named_bound_var(expr.hir_id) {
                     Some(rbv::ResolvedArg::EarlyBound(_)) => {
                         // Find the name and index of the const parameter by indexing the generics of
@@ -143,14 +146,14 @@ impl<'tcx> Const<'tcx> {
                         let generics = tcx.generics_of(item_def_id);
                         let index = generics.param_def_id_to_index[&def_id];
                         let name = tcx.item_name(def_id);
-                        Some(tcx.mk_const(ty::ParamConst::new(index, name), ty))
+                        Some(tcx.mk_const(ty::ParamConst::new(index, name), param_ty))
                     }
                     Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => Some(tcx.mk_const(
                         ty::ConstKind::Bound(debruijn, ty::BoundVar::from_u32(index)),
-                        ty,
+                        param_ty,
                     )),
                     Some(rbv::ResolvedArg::Error(guar)) => {
-                        Some(tcx.const_error_with_guaranteed(ty, guar))
+                        Some(tcx.const_error_with_guaranteed(param_ty, guar))
                     }
                     arg => bug!("unexpected bound var resolution for {:?}: {arg:?}", expr.hir_id),
                 }
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 70091477e39..8dd8f95fcc7 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -17,6 +17,7 @@ use crate::mir::{
 };
 use crate::thir::Thir;
 use crate::traits;
+use crate::traits::solve;
 use crate::traits::solve::{ExternalConstraints, ExternalConstraintsData};
 use crate::ty::query::{self, TyCtxtAt};
 use crate::ty::{
@@ -36,6 +37,7 @@ 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, Lock, Lrc, MappedReadGuard, ReadGuard, WorkerLocal};
+use rustc_data_structures::unord::UnordSet;
 use rustc_errors::{
     DecorateLint, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed, MultiSpan,
 };
@@ -536,6 +538,9 @@ pub struct GlobalCtxt<'tcx> {
     /// Merge this with `selection_cache`?
     pub evaluation_cache: traits::EvaluationCache<'tcx>,
 
+    /// Caches the results of goal evaluation in the new solver.
+    pub new_solver_evaluation_cache: solve::EvaluationCache<'tcx>,
+
     /// Data layout specification for the current target.
     pub data_layout: TargetDataLayout,
 
@@ -711,6 +716,7 @@ impl<'tcx> TyCtxt<'tcx> {
             pred_rcache: Default::default(),
             selection_cache: Default::default(),
             evaluation_cache: Default::default(),
+            new_solver_evaluation_cache: Default::default(),
             data_layout,
             alloc_map: Lock::new(interpret::AllocMap::new()),
         }
@@ -2439,6 +2445,18 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn trait_solver_next(self) -> bool {
         self.sess.opts.unstable_opts.trait_solver == rustc_session::config::TraitSolver::Next
     }
+
+    pub fn lower_impl_trait_in_trait_to_assoc_ty(self) -> bool {
+        self.sess.opts.unstable_opts.lower_impl_trait_in_trait_to_assoc_ty
+    }
+
+    pub fn is_impl_trait_in_trait(self, def_id: DefId) -> bool {
+        if self.lower_impl_trait_in_trait_to_assoc_ty() {
+            self.def_kind(def_id) == DefKind::AssocTy && self.opt_rpitit_info(def_id).is_some()
+        } else {
+            self.def_kind(def_id) == DefKind::ImplTraitPlaceholder
+        }
+    }
 }
 
 impl<'tcx> TyCtxtAt<'tcx> {
@@ -2486,7 +2504,9 @@ pub fn provide(providers: &mut ty::query::Providers) {
     providers.maybe_unused_trait_imports =
         |tcx, ()| &tcx.resolutions(()).maybe_unused_trait_imports;
     providers.names_imported_by_glob_use = |tcx, id| {
-        tcx.arena.alloc(tcx.resolutions(()).glob_map.get(&id).cloned().unwrap_or_default())
+        tcx.arena.alloc(UnordSet::from(
+            tcx.resolutions(()).glob_map.get(&id).cloned().unwrap_or_default(),
+        ))
     };
 
     providers.extern_mod_stmt_cnum =
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index 3ca17e7273e..ae0bb4949c7 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -117,7 +117,7 @@ pub fn suggest_arbitrary_trait_bound<'tcx>(
     }
 
     let param_name = trait_pred.skip_binder().self_ty().to_string();
-    let mut constraint = trait_pred.print_modifiers_and_trait_path().to_string();
+    let mut constraint = trait_pred.to_string();
 
     if let Some((name, term)) = associated_ty {
         // FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err.
@@ -144,7 +144,7 @@ pub fn suggest_arbitrary_trait_bound<'tcx>(
              this requirement",
             if generics.where_clause_span.is_empty() { "introducing a" } else { "extending the" },
         ),
-        format!("{} {}: {}", generics.add_where_or_trailing_comma(), param_name, constraint),
+        format!("{} {constraint}", generics.add_where_or_trailing_comma()),
         Applicability::MaybeIncorrect,
     );
     true
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index 9c171a69d06..aff6c77e039 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -151,12 +151,8 @@ impl<'tcx> TypeError<'tcx> {
             .into(),
             RegionsPlaceholderMismatch => "one type is more general than the other".into(),
             ArgumentSorts(values, _) | Sorts(values) => {
-                let mut expected = values.expected.sort_string(tcx);
-                let mut found = values.found.sort_string(tcx);
-                if expected == found {
-                    expected = values.expected.sort_string(tcx);
-                    found = values.found.sort_string(tcx);
-                }
+                let expected = values.expected.sort_string(tcx);
+                let found = values.found.sort_string(tcx);
                 report_maybe_different(&expected, &found).into()
             }
             Traits(values) => {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 8cc8a0573bb..487cbf1ec4e 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -34,6 +34,7 @@ use rustc_attr as attr;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_data_structures::intern::Interned;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_data_structures::steal::Steal;
 use rustc_data_structures::tagged_ptr::CopyTaggedPtr;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir as hir;
@@ -44,6 +45,8 @@ use rustc_index::vec::IndexVec;
 use rustc_macros::HashStable;
 use rustc_query_system::ich::StableHashingContext;
 use rustc_serialize::{Decodable, Encodable};
+use rustc_session::lint::LintBuffer;
+pub use rustc_session::lint::RegisteredTools;
 use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{ExpnId, ExpnKind, Span};
@@ -148,8 +151,6 @@ mod typeck_results;
 
 // Data types
 
-pub type RegisteredTools = FxHashSet<Ident>;
-
 pub struct ResolverOutputs {
     pub global_ctxt: ResolverGlobalCtxt,
     pub ast_lowering: ResolverAstLowering,
@@ -175,7 +176,6 @@ pub struct ResolverGlobalCtxt {
     /// Mapping from ident span to path span for paths that don't exist as written, but that
     /// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`.
     pub confused_type_with_std_module: FxHashMap<Span, Span>,
-    pub registered_tools: RegisteredTools,
     pub doc_link_resolutions: FxHashMap<LocalDefId, DocLinkResMap>,
     pub doc_link_traits_in_scope: FxHashMap<LocalDefId, Vec<DefId>>,
     pub all_macro_rules: FxHashMap<Symbol, Res<ast::NodeId>>,
@@ -209,6 +209,9 @@ pub struct ResolverAstLowering {
     pub builtin_macro_kinds: FxHashMap<LocalDefId, MacroKind>,
     /// List functions and methods for which lifetime elision was successful.
     pub lifetime_elision_allowed: FxHashSet<ast::NodeId>,
+
+    /// Lints that were emitted by the resolver and early lints.
+    pub lint_buffer: Steal<LintBuffer>,
 }
 
 #[derive(Clone, Copy, Debug)]
@@ -995,7 +998,7 @@ impl<'tcx> Term<'tcx> {
 
     pub fn is_infer(&self) -> bool {
         match self.unpack() {
-            TermKind::Ty(ty) => ty.is_ty_or_numeric_infer(),
+            TermKind::Ty(ty) => ty.is_ty_var(),
             TermKind::Const(ct) => ct.is_ct_infer(),
         }
     }
diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs
index 2bc51baf879..05219efe5f5 100644
--- a/compiler/rustc_middle/src/ty/query.rs
+++ b/compiler/rustc_middle/src/ty/query.rs
@@ -41,7 +41,7 @@ use rustc_arena::TypedArena;
 use rustc_ast as ast;
 use rustc_ast::expand::allocator::AllocatorKind;
 use rustc_attr as attr;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
 use rustc_data_structures::steal::Steal;
 use rustc_data_structures::svh::Svh;
 use rustc_data_structures::sync::Lrc;
@@ -50,7 +50,9 @@ use rustc_data_structures::unord::UnordSet;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, DocLinkResMap};
-use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId};
+use rustc_hir::def_id::{
+    CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId, LocalDefIdMap, LocalDefIdSet,
+};
 use rustc_hir::hir_id::OwnerId;
 use rustc_hir::lang_items::{LangItem, LanguageItems};
 use rustc_hir::{Crate, ItemLocalId, TraitCandidate};
@@ -97,6 +99,11 @@ pub struct TyCtxtEnsure<'tcx> {
     pub tcx: TyCtxt<'tcx>,
 }
 
+#[derive(Copy, Clone)]
+pub struct TyCtxtEnsureWithValue<'tcx> {
+    pub tcx: TyCtxt<'tcx>,
+}
+
 impl<'tcx> TyCtxt<'tcx> {
     /// Returns a transparent wrapper for `TyCtxt`, which ensures queries
     /// are executed instead of just returning their results.
@@ -105,6 +112,15 @@ impl<'tcx> TyCtxt<'tcx> {
         TyCtxtEnsure { tcx: self }
     }
 
+    /// Returns a transparent wrapper for `TyCtxt`, which ensures queries
+    /// are executed instead of just returning their results.
+    ///
+    /// This version verifies that the computed result exists in the cache before returning.
+    #[inline(always)]
+    pub fn ensure_with_value(self) -> TyCtxtEnsureWithValue<'tcx> {
+        TyCtxtEnsureWithValue { tcx: self }
+    }
+
     /// Returns a transparent wrapper for `TyCtxt` which uses
     /// `span` as the location of queries performed through it.
     #[inline(always)]
@@ -250,6 +266,36 @@ macro_rules! define_callbacks {
             )*
         }
 
+        $(
+            // Ensure that keys grow no larger than 64 bytes
+            #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+            const _: () = {
+                if mem::size_of::<query_keys::$name<'static>>() > 64 {
+                    panic!("{}", concat!(
+                        "the query `",
+                        stringify!($name),
+                        "` has a key type `",
+                        stringify!($($K)*),
+                        "` that is too large"
+                    ));
+                }
+            };
+
+            // Ensure that values grow no larger than 64 bytes
+            #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+            const _: () = {
+                if mem::size_of::<query_values::$name<'static>>() > 64 {
+                    panic!("{}", concat!(
+                        "the query `",
+                        stringify!($name),
+                        "` has a value type `",
+                        stringify!($V),
+                        "` that is too large"
+                    ));
+                }
+            };
+        )*
+
         pub struct QueryArenas<'tcx> {
             $($(#[$attr])* pub $name: query_if_arena!([$($modifiers)*]
                 (WorkerLocal<TypedArena<<$V as Deref>::Target>>)
@@ -282,7 +328,31 @@ macro_rules! define_callbacks {
 
                 match try_get_cached(self.tcx, &self.tcx.query_system.caches.$name, &key) {
                     Some(_) => return,
-                    None => self.tcx.queries.$name(self.tcx, DUMMY_SP, key, QueryMode::Ensure),
+                    None => self.tcx.queries.$name(
+                        self.tcx,
+                        DUMMY_SP,
+                        key,
+                        QueryMode::Ensure { check_cache: false },
+                    ),
+                };
+            })*
+        }
+
+        impl<'tcx> TyCtxtEnsureWithValue<'tcx> {
+            $($(#[$attr])*
+            #[inline(always)]
+            pub fn $name(self, key: query_helper_param_ty!($($K)*)) {
+                let key = key.into_query_param();
+                opt_remap_env_constness!([$($modifiers)*][key]);
+
+                match try_get_cached(self.tcx, &self.tcx.query_system.caches.$name, &key) {
+                    Some(_) => return,
+                    None => self.tcx.queries.$name(
+                        self.tcx,
+                        DUMMY_SP,
+                        key,
+                        QueryMode::Ensure { check_cache: true },
+                    ),
                 };
             })*
         }
diff --git a/compiler/rustc_mir_build/locales/en-US.ftl b/compiler/rustc_mir_build/messages.ftl
index 93e7fb330e0..93e7fb330e0 100644
--- a/compiler/rustc_mir_build/locales/en-US.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
diff --git a/compiler/rustc_mir_build/src/build/cfg.rs b/compiler/rustc_mir_build/src/build/cfg.rs
index d7b4b1f731a..4f1623b4c6a 100644
--- a/compiler/rustc_mir_build/src/build/cfg.rs
+++ b/compiler/rustc_mir_build/src/build/cfg.rs
@@ -90,6 +90,17 @@ impl<'tcx> CFG<'tcx> {
         self.push(block, stmt);
     }
 
+    pub(crate) fn push_place_mention(
+        &mut self,
+        block: BasicBlock,
+        source_info: SourceInfo,
+        place: Place<'tcx>,
+    ) {
+        let kind = StatementKind::PlaceMention(Box::new(place));
+        let stmt = Statement { source_info, kind };
+        self.push(block, stmt);
+    }
+
     pub(crate) fn terminate(
         &mut self,
         block: BasicBlock,
diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
index 09d2eb96d0f..5e77f2dc126 100644
--- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
+++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
@@ -59,14 +59,6 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
                     unwind: None,
                 })
             },
-            @call("mir_drop_and_replace", args) => {
-                Ok(TerminatorKind::DropAndReplace {
-                    place: self.parse_place(args[0])?,
-                    value: self.parse_operand(args[1])?,
-                    target: self.parse_block(args[2])?,
-                    unwind: None,
-                })
-            },
             @call("mir_call", args) => {
                 let destination = self.parse_place(args[0])?;
                 let target = self.parse_block(args[1])?;
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index de2851a1af9..3fb8a6db2d2 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -556,6 +556,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
             _ => {
                 let place_builder = unpack!(block = self.as_place_builder(block, initializer));
+
+                if let Some(place) = place_builder.try_to_place(self) {
+                    let source_info = self.source_info(initializer.span);
+                    self.cfg.push_place_mention(block, source_info, place);
+                }
+
                 self.place_into_pattern(block, &irrefutable_pat, place_builder, true)
             }
         }
@@ -576,13 +582,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             false,
             &mut [&mut candidate],
         );
+
         // For matches and function arguments, the place that is being matched
         // can be set when creating the variables. But the place for
         // let PATTERN = ... might not even exist until we do the assignment.
         // so we set it here instead.
         if set_match_place {
-            let mut candidate_ref = &candidate;
-            while let Some(next) = {
+            let mut next = Some(&candidate);
+            while let Some(candidate_ref) = next.take() {
                 for binding in &candidate_ref.bindings {
                     let local = self.var_local_id(binding.var_id, OutsideGuard);
                     // `try_to_place` may fail if it is unable to resolve the given
@@ -610,9 +617,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 }
                 // All of the subcandidates should bind the same locals, so we
                 // only visit the first one.
-                candidate_ref.subcandidates.get(0)
-            } {
-                candidate_ref = next;
+                next = candidate_ref.subcandidates.get(0)
             }
         }
 
@@ -1881,6 +1886,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         // let place = Foo::new();
         // match place { Foo { .. } if { let tmp1 = &place; inspect(*tmp1) }
         //     => { let tmp2 = place; feed(tmp2) }, ... }
+        // ```
         //
         // And an input like:
         //
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index b3f9d82829f..70d5fc2d958 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -48,17 +48,14 @@ pub(crate) fn mir_built(
 /// Construct the MIR for a given `DefId`.
 fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_> {
     // Ensure unsafeck and abstract const building is ran before we steal the THIR.
-    // We can't use `ensure()` for `thir_abstract_const` as it doesn't compute the query
-    // if inputs are green. This can cause ICEs when calling `thir_abstract_const` after
-    // THIR has been stolen if we haven't computed this query yet.
     match def {
         ty::WithOptConstParam { did, const_param_did: Some(const_param_did) } => {
-            tcx.ensure().thir_check_unsafety_for_const_arg((did, const_param_did));
-            drop(tcx.thir_abstract_const_of_const_arg((did, const_param_did)));
+            tcx.ensure_with_value().thir_check_unsafety_for_const_arg((did, const_param_did));
+            tcx.ensure_with_value().thir_abstract_const_of_const_arg((did, const_param_did));
         }
         ty::WithOptConstParam { did, const_param_did: None } => {
-            tcx.ensure().thir_check_unsafety(did);
-            drop(tcx.thir_abstract_const(did));
+            tcx.ensure_with_value().thir_check_unsafety(did);
+            tcx.ensure_with_value().thir_abstract_const(did);
         }
     }
 
diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs
index 770701b3750..4bc2c0ca791 100644
--- a/compiler/rustc_mir_build/src/build/scope.rs
+++ b/compiler/rustc_mir_build/src/build/scope.rs
@@ -1072,7 +1072,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 TerminatorKind::Assert { .. }
                     | TerminatorKind::Call { .. }
                     | TerminatorKind::Drop { .. }
-                    | TerminatorKind::DropAndReplace { .. }
                     | TerminatorKind::FalseUnwind { .. }
                     | TerminatorKind::InlineAsm { .. }
             ),
@@ -1432,8 +1431,7 @@ impl<'tcx> DropTreeBuilder<'tcx> for Unwind {
                     *unwind = Some(to);
                 }
             }
-            TerminatorKind::DropAndReplace { unwind, .. }
-            | TerminatorKind::FalseUnwind { unwind, .. }
+            TerminatorKind::FalseUnwind { unwind, .. }
             | TerminatorKind::Call { cleanup: unwind, .. }
             | TerminatorKind::Assert { cleanup: unwind, .. }
             | TerminatorKind::InlineAsm { cleanup: unwind, .. } => {
diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs
index e10a264d385..04709197578 100644
--- a/compiler/rustc_mir_build/src/lib.rs
+++ b/compiler/rustc_mir_build/src/lib.rs
@@ -28,7 +28,7 @@ use rustc_middle::ty::query::Providers;
 use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
 use rustc_macros::fluent_messages;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 pub fn provide(providers: &mut Providers) {
     providers.check_match = thir::pattern::check_match;
diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs
index f67f24b43c4..f6db329fd7c 100644
--- a/compiler/rustc_mir_build/src/lints.rs
+++ b/compiler/rustc_mir_build/src/lints.rs
@@ -128,7 +128,6 @@ impl<'mir, 'tcx> TriColorVisitor<BasicBlocks<'tcx>> for Search<'mir, 'tcx> {
             TerminatorKind::Assert { .. }
             | TerminatorKind::Call { .. }
             | TerminatorKind::Drop { .. }
-            | TerminatorKind::DropAndReplace { .. }
             | TerminatorKind::FalseEdge { .. }
             | TerminatorKind::FalseUnwind { .. }
             | TerminatorKind::Goto { .. }
diff --git a/compiler/rustc_mir_dataflow/locales/en-US.ftl b/compiler/rustc_mir_dataflow/messages.ftl
index 98854152508..98854152508 100644
--- a/compiler/rustc_mir_dataflow/locales/en-US.ftl
+++ b/compiler/rustc_mir_dataflow/messages.ftl
diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs
index 2ae3ae02fcc..a40c38aa4c3 100644
--- a/compiler/rustc_mir_dataflow/src/framework/direction.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs
@@ -480,7 +480,6 @@ impl Direction for Forward {
 
             Assert { target, cleanup: unwind, expected: _, msg: _, cond: _ }
             | Drop { target, unwind, place: _ }
-            | DropAndReplace { target, unwind, value: _, place: _ }
             | FalseUnwind { real_target: target, unwind } => {
                 if let Some(unwind) = unwind {
                     propagate(unwind, exit_state);
diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
index 6f4e7fd4682..08fadfe68a1 100644
--- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
@@ -111,8 +111,7 @@ where
         self.super_terminator(terminator, location);
 
         match terminator.kind {
-            mir::TerminatorKind::Drop { place: dropped_place, .. }
-            | mir::TerminatorKind::DropAndReplace { place: dropped_place, .. } => {
+            mir::TerminatorKind::Drop { place: dropped_place, .. } => {
                 // Drop terminators may call custom drop glue (`Drop::drop`), which takes `&mut
                 // self` as a parameter. In the general case, a drop impl could launder that
                 // reference into the surrounding environment through a raw pointer, thus creating
diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
index 633a5674f1f..bc67aa476f1 100644
--- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
@@ -263,6 +263,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
             | StatementKind::StorageDead(_)
             | StatementKind::Retag(..)
             | StatementKind::AscribeUserType(..)
+            | StatementKind::PlaceMention(..)
             | StatementKind::Coverage(..)
             | StatementKind::Intrinsic(..)
             | StatementKind::ConstEvalCounter
diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
index fcf0ce9d821..99988b29e8a 100644
--- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
@@ -139,6 +139,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
             // Nothing to do for these. Match exhaustively so this fails to compile when new
             // variants are added.
             StatementKind::AscribeUserType(..)
+            | StatementKind::PlaceMention(..)
             | StatementKind::Coverage(..)
             | StatementKind::FakeRead(..)
             | StatementKind::ConstEvalCounter
@@ -202,7 +203,6 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
             TerminatorKind::Abort
             | TerminatorKind::Assert { .. }
             | TerminatorKind::Drop { .. }
-            | TerminatorKind::DropAndReplace { .. }
             | TerminatorKind::FalseEdge { .. }
             | TerminatorKind::FalseUnwind { .. }
             | TerminatorKind::GeneratorDrop
@@ -240,7 +240,6 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
             | TerminatorKind::Abort
             | TerminatorKind::Assert { .. }
             | TerminatorKind::Drop { .. }
-            | TerminatorKind::DropAndReplace { .. }
             | TerminatorKind::FalseEdge { .. }
             | TerminatorKind::FalseUnwind { .. }
             | TerminatorKind::GeneratorDrop
diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs
index b1e03faff05..4ed6f7e90ff 100644
--- a/compiler/rustc_mir_dataflow/src/lib.rs
+++ b/compiler/rustc_mir_dataflow/src/lib.rs
@@ -46,7 +46,7 @@ pub mod storage;
 pub mod un_derefer;
 pub mod value_analysis;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 pub(crate) mod indexes {
     pub(crate) use super::move_paths::MovePathIndex;
diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
index 4a163028fcf..d9ceac1154f 100644
--- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
+++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
@@ -329,6 +329,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
             }
             StatementKind::Retag { .. }
             | StatementKind::AscribeUserType(..)
+            | StatementKind::PlaceMention(..)
             | StatementKind::Coverage(..)
             | StatementKind::Intrinsic(..)
             | StatementKind::ConstEvalCounter
@@ -392,11 +393,6 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
                 self.create_move_path(place);
                 self.gather_init(place.as_ref(), InitKind::Deep);
             }
-            TerminatorKind::DropAndReplace { place, ref value, .. } => {
-                self.create_move_path(place);
-                self.gather_operand(value);
-                self.gather_init(place.as_ref(), InitKind::Deep);
-            }
             TerminatorKind::Call {
                 ref func,
                 ref args,
diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs
index 34c60b5ff3c..7f560d61194 100644
--- a/compiler/rustc_mir_dataflow/src/value_analysis.rs
+++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs
@@ -86,6 +86,7 @@ pub trait ValueAnalysis<'tcx> {
             StatementKind::ConstEvalCounter
             | StatementKind::Nop
             | StatementKind::FakeRead(..)
+            | StatementKind::PlaceMention(..)
             | StatementKind::Coverage(..)
             | StatementKind::AscribeUserType(..) => (),
         }
@@ -230,7 +231,7 @@ pub trait ValueAnalysis<'tcx> {
             TerminatorKind::Drop { place, .. } => {
                 state.flood_with(place.as_ref(), self.map(), Self::Value::bottom());
             }
-            TerminatorKind::DropAndReplace { .. } | TerminatorKind::Yield { .. } => {
+            TerminatorKind::Yield { .. } => {
                 // They would have an effect, but are not allowed in this phase.
                 bug!("encountered disallowed terminator");
             }
diff --git a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs
index 9b4b720702b..893018e0d8e 100644
--- a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs
+++ b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs
@@ -74,7 +74,7 @@ impl<'tcx> MirPass<'tcx> for AbortUnwindingCalls {
                     };
                     layout::fn_can_unwind(tcx, fn_def_id, sig.abi())
                 }
-                TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } => {
+                TerminatorKind::Drop { .. } => {
                     tcx.sess.opts.unstable_opts.panic_in_drop == PanicStrategy::Unwind
                         && layout::fn_can_unwind(tcx, None, Abi::Rust)
                 }
diff --git a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs
index 9b2260f6825..896fcd9cdd6 100644
--- a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs
+++ b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs
@@ -64,9 +64,6 @@ fn add_moves_for_packed_drops_patch<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>)
             {
                 add_move_for_packed_drop(tcx, body, &mut patch, terminator, loc, data.is_cleanup);
             }
-            TerminatorKind::DropAndReplace { .. } => {
-                span_bug!(terminator.source_info.span, "replace in AddMovesForPackedDrops");
-            }
             _ => {}
         }
     }
diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs
index 7d2146214c6..916f2904dda 100644
--- a/compiler/rustc_mir_transform/src/add_retag.rs
+++ b/compiler/rustc_mir_transform/src/add_retag.rs
@@ -100,7 +100,7 @@ impl<'tcx> MirPass<'tcx> for AddRetag {
                     }
 
                     // `Drop` is also a call, but it doesn't return anything so we are good.
-                    TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } => None,
+                    TerminatorKind::Drop { .. } => None,
                     // Not a block ending in a Call -> ignore.
                     _ => None,
                 }
diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs
index d00ee1f4bab..a8ec568eb0d 100644
--- a/compiler/rustc_mir_transform/src/check_unsafety.rs
+++ b/compiler/rustc_mir_transform/src/check_unsafety.rs
@@ -1,4 +1,4 @@
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::unord::{UnordItems, UnordSet};
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
@@ -24,7 +24,7 @@ pub struct UnsafetyChecker<'a, 'tcx> {
     param_env: ty::ParamEnv<'tcx>,
 
     /// Used `unsafe` blocks in this function. This is used for the "unused_unsafe" lint.
-    used_unsafe_blocks: FxHashSet<HirId>,
+    used_unsafe_blocks: UnordSet<HirId>,
 }
 
 impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
@@ -55,7 +55,6 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
             | TerminatorKind::Drop { .. }
             | TerminatorKind::Yield { .. }
             | TerminatorKind::Assert { .. }
-            | TerminatorKind::DropAndReplace { .. }
             | TerminatorKind::GeneratorDrop
             | TerminatorKind::Resume
             | TerminatorKind::Abort
@@ -101,13 +100,16 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
             | StatementKind::StorageLive(..)
             | StatementKind::StorageDead(..)
             | StatementKind::Retag { .. }
-            | StatementKind::AscribeUserType(..)
+            | StatementKind::PlaceMention(..)
             | StatementKind::Coverage(..)
             | StatementKind::Intrinsic(..)
             | StatementKind::ConstEvalCounter
             | StatementKind::Nop => {
                 // safe (at least as emitted during MIR construction)
             }
+            // `AscribeUserType` just exists to help MIR borrowck.
+            // It has no semantics, and everything is already reported by `PlaceMention`.
+            StatementKind::AscribeUserType(..) => return,
         }
         self.super_statement(statement, location);
     }
@@ -129,7 +131,7 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
                     let def_id = def_id.expect_local();
                     let UnsafetyCheckResult { violations, used_unsafe_blocks, .. } =
                         self.tcx.unsafety_check_result(def_id);
-                    self.register_violations(violations, used_unsafe_blocks.iter().copied());
+                    self.register_violations(violations, used_unsafe_blocks.items().copied());
                 }
             },
             _ => {}
@@ -151,7 +153,7 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
                         let local_def_id = def_id.expect_local();
                         let UnsafetyCheckResult { violations, used_unsafe_blocks, .. } =
                             self.tcx.unsafety_check_result(local_def_id);
-                        self.register_violations(violations, used_unsafe_blocks.iter().copied());
+                        self.register_violations(violations, used_unsafe_blocks.items().copied());
                     }
                 }
             }
@@ -268,14 +270,14 @@ impl<'tcx> UnsafetyChecker<'_, 'tcx> {
             .lint_root;
         self.register_violations(
             [&UnsafetyViolation { source_info, lint_root, kind, details }],
-            [],
+            UnordItems::empty(),
         );
     }
 
     fn register_violations<'a>(
         &mut self,
         violations: impl IntoIterator<Item = &'a UnsafetyViolation>,
-        new_used_unsafe_blocks: impl IntoIterator<Item = HirId>,
+        new_used_unsafe_blocks: UnordItems<HirId, impl Iterator<Item = HirId>>,
     ) {
         let safety = self.body.source_scopes[self.source_info.scope]
             .local_data
@@ -308,9 +310,7 @@ impl<'tcx> UnsafetyChecker<'_, 'tcx> {
             }),
         };
 
-        new_used_unsafe_blocks.into_iter().for_each(|hir_id| {
-            self.used_unsafe_blocks.insert(hir_id);
-        });
+        self.used_unsafe_blocks.extend_unord(new_used_unsafe_blocks);
     }
     fn check_mut_borrowing_layout_constrained_field(
         &mut self,
@@ -407,7 +407,7 @@ enum Context {
 
 struct UnusedUnsafeVisitor<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
-    used_unsafe_blocks: &'a FxHashSet<HirId>,
+    used_unsafe_blocks: &'a UnordSet<HirId>,
     context: Context,
     unused_unsafes: &'a mut Vec<(HirId, UnusedUnsafe)>,
 }
@@ -458,7 +458,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'_, 'tcx> {
 fn check_unused_unsafe(
     tcx: TyCtxt<'_>,
     def_id: LocalDefId,
-    used_unsafe_blocks: &FxHashSet<HirId>,
+    used_unsafe_blocks: &UnordSet<HirId>,
 ) -> Vec<(HirId, UnusedUnsafe)> {
     let body_id = tcx.hir().maybe_body_owned_by(def_id);
 
@@ -505,7 +505,7 @@ fn unsafety_check_result(
     if body.is_custom_mir() {
         return tcx.arena.alloc(UnsafetyCheckResult {
             violations: Vec::new(),
-            used_unsafe_blocks: FxHashSet::default(),
+            used_unsafe_blocks: Default::default(),
             unused_unsafes: Some(Vec::new()),
         });
     }
diff --git a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs
index d435d3ee69b..0923824db48 100644
--- a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs
+++ b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs
@@ -24,6 +24,7 @@ impl<'tcx> MirPass<'tcx> for CleanupPostBorrowck {
             for statement in basic_block.statements.iter_mut() {
                 match statement.kind {
                     StatementKind::AscribeUserType(..)
+                    | StatementKind::PlaceMention(..)
                     | StatementKind::Assign(box (_, Rvalue::Ref(_, BorrowKind::Shallow, _)))
                     | StatementKind::FakeRead(..) => statement.make_nop(),
                     _ => (),
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index 6c5202549ea..de7b8c63fc8 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -944,7 +944,6 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
             | TerminatorKind::Return
             | TerminatorKind::Unreachable
             | TerminatorKind::Drop { .. }
-            | TerminatorKind::DropAndReplace { .. }
             | TerminatorKind::Yield { .. }
             | TerminatorKind::GeneratorDrop
             | TerminatorKind::FalseEdge { .. }
diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs
index cbf29380ff2..68e50070e56 100644
--- a/compiler/rustc_mir_transform/src/const_prop_lint.rs
+++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs
@@ -675,7 +675,6 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
             | TerminatorKind::Return
             | TerminatorKind::Unreachable
             | TerminatorKind::Drop { .. }
-            | TerminatorKind::DropAndReplace { .. }
             | TerminatorKind::Yield { .. }
             | TerminatorKind::GeneratorDrop
             | TerminatorKind::FalseEdge { .. }
diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs
index 22ea8710e6a..0e7dc171a5d 100644
--- a/compiler/rustc_mir_transform/src/coverage/debug.rs
+++ b/compiler/rustc_mir_transform/src/coverage/debug.rs
@@ -822,7 +822,6 @@ pub(super) fn term_type(kind: &TerminatorKind<'_>) -> &'static str {
         TerminatorKind::Return => "Return",
         TerminatorKind::Unreachable => "Unreachable",
         TerminatorKind::Drop { .. } => "Drop",
-        TerminatorKind::DropAndReplace { .. } => "DropAndReplace",
         TerminatorKind::Call { .. } => "Call",
         TerminatorKind::Assert { .. } => "Assert",
         TerminatorKind::Yield { .. } => "Yield",
diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs
index a2671eef2e9..49028ca4e5e 100644
--- a/compiler/rustc_mir_transform/src/coverage/graph.rs
+++ b/compiler/rustc_mir_transform/src/coverage/graph.rs
@@ -156,7 +156,6 @@ impl CoverageGraph {
                 | TerminatorKind::Resume
                 | TerminatorKind::Unreachable
                 | TerminatorKind::Drop { .. }
-                | TerminatorKind::DropAndReplace { .. }
                 | TerminatorKind::Call { .. }
                 | TerminatorKind::GeneratorDrop
                 | TerminatorKind::Assert { .. }
@@ -538,29 +537,29 @@ impl TraverseCoverageGraphWithLoops {
             "TraverseCoverageGraphWithLoops::next - context_stack: {:?}",
             self.context_stack.iter().rev().collect::<Vec<_>>()
         );
-        while let Some(next_bcb) = {
-            // Strip contexts with empty worklists from the top of the stack
-            while self.context_stack.last().map_or(false, |context| context.worklist.is_empty()) {
+
+        while let Some(context) = self.context_stack.last_mut() {
+            if let Some(next_bcb) = context.worklist.pop() {
+                if !self.visited.insert(next_bcb) {
+                    debug!("Already visited: {:?}", next_bcb);
+                    continue;
+                }
+                debug!("Visiting {:?}", next_bcb);
+                if self.backedges[next_bcb].len() > 0 {
+                    debug!("{:?} is a loop header! Start a new TraversalContext...", next_bcb);
+                    self.context_stack.push(TraversalContext {
+                        loop_backedges: Some((self.backedges[next_bcb].clone(), next_bcb)),
+                        worklist: Vec::new(),
+                    });
+                }
+                self.extend_worklist(basic_coverage_blocks, next_bcb);
+                return Some(next_bcb);
+            } else {
+                // Strip contexts with empty worklists from the top of the stack
                 self.context_stack.pop();
             }
-            // Pop the next bcb off of the current context_stack. If none, all BCBs were visited.
-            self.context_stack.last_mut().map_or(None, |context| context.worklist.pop())
-        } {
-            if !self.visited.insert(next_bcb) {
-                debug!("Already visited: {:?}", next_bcb);
-                continue;
-            }
-            debug!("Visiting {:?}", next_bcb);
-            if self.backedges[next_bcb].len() > 0 {
-                debug!("{:?} is a loop header! Start a new TraversalContext...", next_bcb);
-                self.context_stack.push(TraversalContext {
-                    loop_backedges: Some((self.backedges[next_bcb].clone(), next_bcb)),
-                    worklist: Vec::new(),
-                });
-            }
-            self.extend_worklist(basic_coverage_blocks, next_bcb);
-            return Some(next_bcb);
         }
+
         None
     }
 
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index 9a617159813..5ecb2d6a631 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -577,5 +577,5 @@ fn get_body_span<'tcx>(
 fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx>) -> u64 {
     // FIXME(cjgillot) Stop hashing HIR manually here.
     let owner = hir_body.id().hir_id.owner;
-    tcx.hir_owner_nodes(owner).unwrap().hash_including_bodies.to_smaller_hash()
+    tcx.hir_owner_nodes(owner).unwrap().opt_hash_including_bodies.unwrap().to_smaller_hash()
 }
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs
index 8ee316773ae..2f120258659 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -832,6 +832,7 @@ pub(super) fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span>
         | StatementKind::SetDiscriminant { .. }
         | StatementKind::Deinit(..)
         | StatementKind::Retag(_, _)
+        | StatementKind::PlaceMention(..)
         | StatementKind::AscribeUserType(_, _) => {
             Some(statement.source_info.span)
         }
@@ -850,7 +851,6 @@ pub(super) fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Sp
         TerminatorKind::Unreachable // Unreachable blocks are not connected to the MIR CFG
         | TerminatorKind::Assert { .. }
         | TerminatorKind::Drop { .. }
-        | TerminatorKind::DropAndReplace { .. }
         | TerminatorKind::SwitchInt { .. }
         // For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`.
         | TerminatorKind::FalseEdge { .. }
diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs
index fa7f22303a8..aded8039dc3 100644
--- a/compiler/rustc_mir_transform/src/coverage/tests.rs
+++ b/compiler/rustc_mir_transform/src/coverage/tests.rs
@@ -86,7 +86,6 @@ impl<'tcx> MockBlocks<'tcx> {
             TerminatorKind::Assert { ref mut target, .. }
             | TerminatorKind::Call { target: Some(ref mut target), .. }
             | TerminatorKind::Drop { ref mut target, .. }
-            | TerminatorKind::DropAndReplace { ref mut target, .. }
             | TerminatorKind::FalseEdge { real_target: ref mut target, .. }
             | TerminatorKind::FalseUnwind { real_target: ref mut target, .. }
             | TerminatorKind::Goto { ref mut target }
@@ -184,7 +183,6 @@ fn debug_basic_blocks(mir_body: &Body<'_>) -> String {
                     TerminatorKind::Assert { target, .. }
                     | TerminatorKind::Call { target: Some(target), .. }
                     | TerminatorKind::Drop { target, .. }
-                    | TerminatorKind::DropAndReplace { target, .. }
                     | TerminatorKind::FalseEdge { real_target: target, .. }
                     | TerminatorKind::FalseUnwind { real_target: target, .. }
                     | TerminatorKind::Goto { target }
diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs
index 9dbfb089dc6..18c407b42d3 100644
--- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs
+++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs
@@ -56,7 +56,9 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS
                 | StatementKind::ConstEvalCounter
                 | StatementKind::Nop => (),
 
-                StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => {
+                StatementKind::FakeRead(_)
+                | StatementKind::PlaceMention(_)
+                | StatementKind::AscribeUserType(_, _) => {
                     bug!("{:?} not found in this MIR phase!", &statement.kind)
                 }
             }
diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs
index 2e481b97278..7344ec793ea 100644
--- a/compiler/rustc_mir_transform/src/dest_prop.rs
+++ b/compiler/rustc_mir_transform/src/dest_prop.rs
@@ -583,7 +583,9 @@ impl WriteInfo {
             | StatementKind::Coverage(_)
             | StatementKind::StorageLive(_)
             | StatementKind::StorageDead(_) => (),
-            StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => {
+            StatementKind::FakeRead(_)
+            | StatementKind::AscribeUserType(_, _)
+            | StatementKind::PlaceMention(_) => {
                 bug!("{:?} not found in this MIR phase", statement)
             }
         }
@@ -650,8 +652,7 @@ impl WriteInfo {
             TerminatorKind::Drop { .. } => {
                 // `Drop`s create a `&mut` and so are not considered
             }
-            TerminatorKind::DropAndReplace { .. }
-            | TerminatorKind::Yield { .. }
+            TerminatorKind::Yield { .. }
             | TerminatorKind::GeneratorDrop
             | TerminatorKind::FalseEdge { .. }
             | TerminatorKind::FalseUnwind { .. } => {
diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs
index 29424f09695..3faccca823a 100644
--- a/compiler/rustc_mir_transform/src/elaborate_drops.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs
@@ -18,15 +18,14 @@ use rustc_span::{DesugaringKind, Span};
 use rustc_target::abi::VariantIdx;
 use std::fmt;
 
-/// During MIR building, Drop and DropAndReplace terminators are inserted in every place where a drop may occur.
+/// During MIR building, Drop terminators are inserted in every place where a drop may occur.
 /// However, in this phase, the presence of these terminators does not guarantee that a destructor will run,
 /// as the target of the drop may be uninitialized.
 /// In general, the compiler cannot determine at compile time whether a destructor will run or not.
 ///
-/// At a high level, this pass refines Drop and DropAndReplace to only run the destructor if the
+/// At a high level, this pass refines Drop to only run the destructor if the
 /// target is initialized. The way this is achievied is by inserting drop flags for every variable
 /// that may be dropped, and then using those flags to determine whether a destructor should run.
-/// This pass also removes DropAndReplace, replacing it with a Drop paired with an assign statement.
 /// Once this is complete, Drop terminators in the MIR correspond to a call to the "drop glue" or
 /// "drop shim" for the type of the dropped place.
 ///
@@ -121,8 +120,7 @@ fn remove_dead_unwinds<'tcx>(
         .into_results_cursor(body);
     for (bb, bb_data) in body.basic_blocks.iter_enumerated() {
         let place = match bb_data.terminator().kind {
-            TerminatorKind::Drop { ref place, unwind: Some(_), .. }
-            | TerminatorKind::DropAndReplace { ref place, unwind: Some(_), .. } => {
+            TerminatorKind::Drop { ref place, unwind: Some(_), .. } => {
                 und.derefer(place.as_ref(), body).unwrap_or(*place)
             }
             _ => continue,
@@ -343,8 +341,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
             }
             let terminator = data.terminator();
             let place = match terminator.kind {
-                TerminatorKind::Drop { ref place, .. }
-                | TerminatorKind::DropAndReplace { ref place, .. } => {
+                TerminatorKind::Drop { ref place, .. } => {
                     self.un_derefer.derefer(place.as_ref(), self.body).unwrap_or(*place)
                 }
                 _ => continue,
@@ -441,103 +438,11 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
                         }
                     }
                 }
-                TerminatorKind::DropAndReplace { mut place, ref value, target, unwind } => {
-                    assert!(!data.is_cleanup);
-
-                    if let Some(new_place) = self.un_derefer.derefer(place.as_ref(), self.body) {
-                        place = new_place;
-                    }
-                    self.elaborate_replace(loc, place, value, target, unwind);
-                }
                 _ => continue,
             }
         }
     }
 
-    /// Elaborate a MIR `replace` terminator. This instruction
-    /// is not directly handled by codegen, and therefore
-    /// must be desugared.
-    ///
-    /// The desugaring drops the location if needed, and then writes
-    /// the value (including setting the drop flag) over it in *both* arms.
-    ///
-    /// The `replace` terminator can also be called on places that
-    /// are not tracked by elaboration (for example,
-    /// `replace x[i] <- tmp0`). The borrow checker requires that
-    /// these locations are initialized before the assignment,
-    /// so we just generate an unconditional drop.
-    fn elaborate_replace(
-        &mut self,
-        loc: Location,
-        place: Place<'tcx>,
-        value: &Operand<'tcx>,
-        target: BasicBlock,
-        unwind: Option<BasicBlock>,
-    ) {
-        let bb = loc.block;
-        let data = &self.body[bb];
-        let terminator = data.terminator();
-        assert!(!data.is_cleanup, "DropAndReplace in unwind path not supported");
-
-        let assign = Statement {
-            kind: StatementKind::Assign(Box::new((place, Rvalue::Use(value.clone())))),
-            source_info: terminator.source_info,
-        };
-
-        let unwind = unwind.unwrap_or_else(|| self.patch.resume_block());
-        let unwind = self.patch.new_block(BasicBlockData {
-            statements: vec![assign.clone()],
-            terminator: Some(Terminator {
-                kind: TerminatorKind::Goto { target: unwind },
-                ..*terminator
-            }),
-            is_cleanup: true,
-        });
-
-        let target = self.patch.new_block(BasicBlockData {
-            statements: vec![assign],
-            terminator: Some(Terminator { kind: TerminatorKind::Goto { target }, ..*terminator }),
-            is_cleanup: false,
-        });
-
-        match self.move_data().rev_lookup.find(place.as_ref()) {
-            LookupResult::Exact(path) => {
-                debug!("elaborate_drop_and_replace({:?}) - tracked {:?}", terminator, path);
-                self.init_data.seek_before(loc);
-                elaborate_drop(
-                    &mut Elaborator { ctxt: self },
-                    terminator.source_info,
-                    place,
-                    path,
-                    target,
-                    Unwind::To(unwind),
-                    bb,
-                );
-                on_all_children_bits(self.tcx, self.body, self.move_data(), path, |child| {
-                    self.set_drop_flag(
-                        Location { block: target, statement_index: 0 },
-                        child,
-                        DropFlagState::Present,
-                    );
-                    self.set_drop_flag(
-                        Location { block: unwind, statement_index: 0 },
-                        child,
-                        DropFlagState::Present,
-                    );
-                });
-            }
-            LookupResult::Parent(parent) => {
-                // drop and replace behind a pointer/array/whatever. The location
-                // must be initialized.
-                debug!("elaborate_drop_and_replace({:?}) - untracked {:?}", terminator, parent);
-                self.patch.patch_terminator(
-                    bb,
-                    TerminatorKind::Drop { place, target, unwind: Some(unwind) },
-                );
-            }
-        }
-    }
-
     fn constant_bool(&self, span: Span, val: bool) -> Rvalue<'tcx> {
         Rvalue::Use(Operand::Constant(Box::new(Constant {
             span,
@@ -609,22 +514,12 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
             debug!("drop_flags_for_locs({:?})", data);
             for i in 0..(data.statements.len() + 1) {
                 debug!("drop_flag_for_locs: stmt {}", i);
-                let mut allow_initializations = true;
                 if i == data.statements.len() {
                     match data.terminator().kind {
                         TerminatorKind::Drop { .. } => {
                             // drop elaboration should handle that by itself
                             continue;
                         }
-                        TerminatorKind::DropAndReplace { .. } => {
-                            // this contains the move of the source and
-                            // the initialization of the destination. We
-                            // only want the former - the latter is handled
-                            // by the elaboration code and must be done
-                            // *after* the destination is dropped.
-                            assert!(self.patch.is_patched(bb));
-                            allow_initializations = false;
-                        }
                         TerminatorKind::Resume => {
                             // It is possible for `Resume` to be patched
                             // (in particular it can be patched to be replaced with
@@ -641,11 +536,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
                     self.body,
                     self.env,
                     loc,
-                    |path, ds| {
-                        if ds == DropFlagState::Absent || allow_initializations {
-                            self.set_drop_flag(loc, path, ds)
-                        }
-                    },
+                    |path, ds| self.set_drop_flag(loc, path, ds),
                 )
             }
 
diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs
index 746326f9bde..b7f1cdfc7f2 100644
--- a/compiler/rustc_mir_transform/src/generator.rs
+++ b/compiler/rustc_mir_transform/src/generator.rs
@@ -1199,7 +1199,6 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool {
 
             // These may unwind.
             TerminatorKind::Drop { .. }
-            | TerminatorKind::DropAndReplace { .. }
             | TerminatorKind::Call { .. }
             | TerminatorKind::InlineAsm { .. }
             | TerminatorKind::Assert { .. } => return true,
@@ -1648,6 +1647,7 @@ impl<'tcx> Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> {
             | StatementKind::StorageDead(_)
             | StatementKind::Retag(..)
             | StatementKind::AscribeUserType(..)
+            | StatementKind::PlaceMention(..)
             | StatementKind::Coverage(..)
             | StatementKind::Intrinsic(..)
             | StatementKind::ConstEvalCounter
@@ -1691,7 +1691,6 @@ impl<'tcx> Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> {
             | TerminatorKind::Return
             | TerminatorKind::Unreachable
             | TerminatorKind::Drop { .. }
-            | TerminatorKind::DropAndReplace { .. }
             | TerminatorKind::Assert { .. }
             | TerminatorKind::GeneratorDrop
             | TerminatorKind::FalseEdge { .. }
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 6e6d6566f4b..9cba8870f23 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -453,9 +453,7 @@ impl<'tcx> Inliner<'tcx> {
             checker.visit_basic_block_data(bb, blk);
 
             let term = blk.terminator();
-            if let TerminatorKind::Drop { ref place, target, unwind }
-            | TerminatorKind::DropAndReplace { ref place, target, unwind, .. } = term.kind
-            {
+            if let TerminatorKind::Drop { ref place, target, unwind } = term.kind {
                 work_list.push(target);
 
                 // If the place doesn't actually need dropping, treat it like a regular goto.
@@ -815,8 +813,7 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
     fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
         let tcx = self.tcx;
         match terminator.kind {
-            TerminatorKind::Drop { ref place, unwind, .. }
-            | TerminatorKind::DropAndReplace { ref place, unwind, .. } => {
+            TerminatorKind::Drop { ref place, unwind, .. } => {
                 // If the place doesn't actually need dropping, treat it like a regular goto.
                 let ty = self.instance.subst_mir(tcx, &place.ty(self.callee_body, tcx).ty);
                 if ty.needs_drop(tcx, self.param_env) {
@@ -1120,8 +1117,7 @@ impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> {
                     *tgt = self.map_block(*tgt);
                 }
             }
-            TerminatorKind::Drop { ref mut target, ref mut unwind, .. }
-            | TerminatorKind::DropAndReplace { ref mut target, ref mut unwind, .. } => {
+            TerminatorKind::Drop { ref mut target, ref mut unwind, .. } => {
                 *target = self.map_block(*target);
                 *unwind = self.map_unwind(*unwind);
             }
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 5fd923190ef..4917b045d4c 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -278,14 +278,14 @@ fn mir_const(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> &Steal<
     // Unsafety check uses the raw mir, so make sure it is run.
     if !tcx.sess.opts.unstable_opts.thir_unsafeck {
         if let Some(param_did) = def.const_param_did {
-            tcx.ensure().unsafety_check_result_for_const_arg((def.did, param_did));
+            tcx.ensure_with_value().unsafety_check_result_for_const_arg((def.did, param_did));
         } else {
-            tcx.ensure().unsafety_check_result(def.did);
+            tcx.ensure_with_value().unsafety_check_result(def.did);
         }
     }
 
     // has_ffi_unwind_calls query uses the raw mir, so make sure it is run.
-    tcx.ensure().has_ffi_unwind_calls(def.did);
+    tcx.ensure_with_value().has_ffi_unwind_calls(def.did);
 
     let mut body = tcx.mir_built(def).steal();
 
@@ -433,7 +433,7 @@ fn mir_drops_elaborated_and_const_checked(
     if tcx.sess.opts.unstable_opts.drop_tracking_mir
         && let DefKind::Generator = tcx.def_kind(def.did)
     {
-        tcx.ensure().mir_generator_witnesses(def.did);
+        tcx.ensure_with_value().mir_generator_witnesses(def.did);
     }
     let mir_borrowck = tcx.mir_borrowck_opt_const_arg(def);
 
@@ -444,7 +444,7 @@ fn mir_drops_elaborated_and_const_checked(
 
         // Do not compute the mir call graph without said call graph actually being used.
         if inline::Inline.is_enabled(&tcx.sess) {
-            let _ = tcx.mir_inliner_callees(ty::InstanceDef::Item(def));
+            tcx.ensure_with_value().mir_inliner_callees(ty::InstanceDef::Item(def));
         }
     }
 
@@ -613,7 +613,7 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> {
         // Run the `mir_for_ctfe` query, which depends on `mir_drops_elaborated_and_const_checked`
         // which we are going to steal below. Thus we need to run `mir_for_ctfe` first, so it
         // computes and caches its result.
-        Some(hir::ConstContext::ConstFn) => tcx.ensure().mir_for_ctfe(did),
+        Some(hir::ConstContext::ConstFn) => tcx.ensure_with_value().mir_for_ctfe(did),
         None => {}
         Some(other) => panic!("do not use `optimized_mir` for constants: {:?}", other),
     }
diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs
index e3a03aa08af..e962819b691 100644
--- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs
+++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs
@@ -33,6 +33,7 @@ impl RemoveNoopLandingPads {
                 StatementKind::FakeRead(..)
                 | StatementKind::StorageLive(_)
                 | StatementKind::StorageDead(_)
+                | StatementKind::PlaceMention(..)
                 | StatementKind::AscribeUserType(..)
                 | StatementKind::Coverage(..)
                 | StatementKind::ConstEvalCounter
@@ -75,7 +76,6 @@ impl RemoveNoopLandingPads {
             | TerminatorKind::Unreachable
             | TerminatorKind::Call { .. }
             | TerminatorKind::Assert { .. }
-            | TerminatorKind::DropAndReplace { .. }
             | TerminatorKind::Drop { .. }
             | TerminatorKind::InlineAsm { .. } => false,
         }
diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
index 78b6f714a9b..e72729b152e 100644
--- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
+++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
@@ -1,5 +1,5 @@
 use rustc_index::bit_set::ChunkedBitSet;
-use rustc_middle::mir::{Body, Field, Rvalue, Statement, StatementKind, TerminatorKind};
+use rustc_middle::mir::{Body, Field, TerminatorKind};
 use rustc_middle::ty::subst::SubstsRef;
 use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, VariantDef};
 use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
@@ -8,7 +8,7 @@ use rustc_mir_dataflow::{self, move_path_children_matching, Analysis, MoveDataPa
 
 use crate::MirPass;
 
-/// Removes `Drop` and `DropAndReplace` terminators whose target is known to be uninitialized at
+/// Removes `Drop` terminators whose target is known to be uninitialized at
 /// that point.
 ///
 /// This is redundant with drop elaboration, but we need to do it prior to const-checking, and
@@ -37,8 +37,7 @@ impl<'tcx> MirPass<'tcx> for RemoveUninitDrops {
         let mut to_remove = vec![];
         for (bb, block) in body.basic_blocks.iter_enumerated() {
             let terminator = block.terminator();
-            let (TerminatorKind::Drop { place, .. } | TerminatorKind::DropAndReplace { place, .. })
-                = &terminator.kind
+            let TerminatorKind::Drop { place, .. } = &terminator.kind
             else { continue };
 
             maybe_inits.seek_before_primary_effect(body.terminator_loc(bb));
@@ -64,24 +63,12 @@ impl<'tcx> MirPass<'tcx> for RemoveUninitDrops {
         for bb in to_remove {
             let block = &mut body.basic_blocks_mut()[bb];
 
-            let (TerminatorKind::Drop { target, .. } | TerminatorKind::DropAndReplace { target, .. })
+            let TerminatorKind::Drop { target, .. }
                 = &block.terminator().kind
             else { unreachable!() };
 
             // Replace block terminator with `Goto`.
-            let target = *target;
-            let old_terminator_kind = std::mem::replace(
-                &mut block.terminator_mut().kind,
-                TerminatorKind::Goto { target },
-            );
-
-            // If this is a `DropAndReplace`, we need to emulate the assignment to the return place.
-            if let TerminatorKind::DropAndReplace { place, value, .. } = old_terminator_kind {
-                block.statements.push(Statement {
-                    source_info: block.terminator().source_info,
-                    kind: StatementKind::Assign(Box::new((place, Rvalue::Use(value)))),
-                });
-            }
+            block.terminator_mut().kind = TerminatorKind::Goto { target: *target };
         }
     }
 }
diff --git a/compiler/rustc_mir_transform/src/separate_const_switch.rs b/compiler/rustc_mir_transform/src/separate_const_switch.rs
index a24d2d34d79..d76ab95faba 100644
--- a/compiler/rustc_mir_transform/src/separate_const_switch.rs
+++ b/compiler/rustc_mir_transform/src/separate_const_switch.rs
@@ -108,7 +108,6 @@ pub fn separate_const_switch(body: &mut Body<'_>) -> usize {
                         // The following terminators are not allowed
                         TerminatorKind::Resume
                         | TerminatorKind::Drop { .. }
-                        | TerminatorKind::DropAndReplace { .. }
                         | TerminatorKind::Call { .. }
                         | TerminatorKind::Assert { .. }
                         | TerminatorKind::FalseUnwind { .. }
@@ -170,7 +169,6 @@ pub fn separate_const_switch(body: &mut Body<'_>) -> usize {
             | TerminatorKind::Unreachable
             | TerminatorKind::GeneratorDrop
             | TerminatorKind::Assert { .. }
-            | TerminatorKind::DropAndReplace { .. }
             | TerminatorKind::FalseUnwind { .. }
             | TerminatorKind::Drop { .. }
             | TerminatorKind::Call { .. }
@@ -247,6 +245,7 @@ fn is_likely_const<'tcx>(mut tracked_place: Place<'tcx>, block: &BasicBlockData<
             | StatementKind::StorageLive(_)
             | StatementKind::Retag(_, _)
             | StatementKind::AscribeUserType(_, _)
+            | StatementKind::PlaceMention(..)
             | StatementKind::Coverage(_)
             | StatementKind::StorageDead(_)
             | StatementKind::Intrinsic(_)
@@ -317,6 +316,7 @@ fn find_determining_place<'tcx>(
             | StatementKind::StorageDead(_)
             | StatementKind::Retag(_, _)
             | StatementKind::AscribeUserType(_, _)
+            | StatementKind::PlaceMention(..)
             | StatementKind::Coverage(_)
             | StatementKind::Intrinsic(_)
             | StatementKind::ConstEvalCounter
diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs
index 9ef55c558c6..929d229dcdf 100644
--- a/compiler/rustc_mir_transform/src/simplify.rs
+++ b/compiler/rustc_mir_transform/src/simplify.rs
@@ -525,6 +525,7 @@ impl<'tcx> Visitor<'tcx> for UsedLocals {
             | StatementKind::Retag(..)
             | StatementKind::Coverage(..)
             | StatementKind::FakeRead(..)
+            | StatementKind::PlaceMention(..)
             | StatementKind::AscribeUserType(..) => {
                 self.super_statement(statement, location);
             }
diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs
index c1e7f62dea5..73168652f8f 100644
--- a/compiler/rustc_mir_transform/src/ssa.rs
+++ b/compiler/rustc_mir_transform/src/ssa.rs
@@ -53,7 +53,7 @@ impl SsaLocals {
         body: &Body<'tcx>,
         borrowed_locals: &BitSet<Local>,
     ) -> SsaLocals {
-        let assignment_order = Vec::new();
+        let assignment_order = Vec::with_capacity(body.local_decls.len());
 
         let assignments = IndexVec::from_elem(Set1::Empty, &body.local_decls);
         let dominators =
@@ -179,12 +179,34 @@ struct SsaVisitor {
     assignment_order: Vec<Local>,
 }
 
+impl SsaVisitor {
+    fn check_assignment_dominates(&mut self, local: Local, loc: Location) {
+        let set = &mut self.assignments[local];
+        let assign_dominates = match *set {
+            Set1::Empty | Set1::Many => false,
+            Set1::One(LocationExtended::Arg) => true,
+            Set1::One(LocationExtended::Plain(assign)) => {
+                assign.successor_within_block().dominates(loc, &self.dominators)
+            }
+        };
+        // We are visiting a use that is not dominated by an assignment.
+        // Either there is a cycle involved, or we are reading for uninitialized local.
+        // Bail out.
+        if !assign_dominates {
+            *set = Set1::Many;
+        }
+    }
+}
+
 impl<'tcx> Visitor<'tcx> for SsaVisitor {
     fn visit_local(&mut self, local: Local, ctxt: PlaceContext, loc: Location) {
         match ctxt {
             PlaceContext::MutatingUse(MutatingUseContext::Store) => {
                 self.assignments[local].insert(LocationExtended::Plain(loc));
-                self.assignment_order.push(local);
+                if let Set1::One(_) = self.assignments[local] {
+                    // Only record if SSA-like, to avoid growing the vector needlessly.
+                    self.assignment_order.push(local);
+                }
             }
             // Anything can happen with raw pointers, so remove them.
             PlaceContext::NonMutatingUse(NonMutatingUseContext::AddressOf)
@@ -192,24 +214,26 @@ impl<'tcx> Visitor<'tcx> for SsaVisitor {
             // Immutable borrows are taken into account in `SsaLocals::new` by
             // removing non-freeze locals.
             PlaceContext::NonMutatingUse(_) => {
-                let set = &mut self.assignments[local];
-                let assign_dominates = match *set {
-                    Set1::Empty | Set1::Many => false,
-                    Set1::One(LocationExtended::Arg) => true,
-                    Set1::One(LocationExtended::Plain(assign)) => {
-                        assign.successor_within_block().dominates(loc, &self.dominators)
-                    }
-                };
-                // We are visiting a use that is not dominated by an assignment.
-                // Either there is a cycle involved, or we are reading for uninitialized local.
-                // Bail out.
-                if !assign_dominates {
-                    *set = Set1::Many;
-                }
+                self.check_assignment_dominates(local, loc);
             }
             PlaceContext::NonUse(_) => {}
         }
     }
+
+    fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, loc: Location) {
+        if place.projection.first() == Some(&PlaceElem::Deref) {
+            // Do not do anything for storage statements and debuginfo.
+            if ctxt.is_use() {
+                // A use through a `deref` only reads from the local, and cannot write to it.
+                let new_ctxt = PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection);
+
+                self.visit_projection(place.as_ref(), new_ctxt, loc);
+                self.check_assignment_dominates(place.local, loc);
+            }
+            return;
+        }
+        self.super_place(place, ctxt, loc);
+    }
 }
 
 #[instrument(level = "trace", skip(ssa, body))]
diff --git a/compiler/rustc_monomorphize/locales/en-US.ftl b/compiler/rustc_monomorphize/messages.ftl
index 6cea6a603f3..6cea6a603f3 100644
--- a/compiler/rustc_monomorphize/locales/en-US.ftl
+++ b/compiler/rustc_monomorphize/messages.ftl
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 45e659eab6c..f529944acce 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -808,8 +808,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
                 let callee_ty = self.monomorphize(callee_ty);
                 visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output)
             }
-            mir::TerminatorKind::Drop { ref place, .. }
-            | mir::TerminatorKind::DropAndReplace { ref place, .. } => {
+            mir::TerminatorKind::Drop { ref place, .. } => {
                 let ty = place.ty(self.body, self.tcx).ty;
                 let ty = self.monomorphize(ty);
                 visit_drop_use(self.tcx, ty, true, source, self.output);
diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs
index f6b791f29c1..5000fb71937 100644
--- a/compiler/rustc_monomorphize/src/lib.rs
+++ b/compiler/rustc_monomorphize/src/lib.rs
@@ -23,7 +23,7 @@ mod partitioning;
 mod polymorphize;
 mod util;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 fn custom_coerce_unsize_info<'tcx>(
     tcx: TyCtxtAt<'tcx>,
diff --git a/compiler/rustc_monomorphize/src/partitioning/merging.rs b/compiler/rustc_monomorphize/src/partitioning/merging.rs
index 02bb8dea0c0..5c524a18454 100644
--- a/compiler/rustc_monomorphize/src/partitioning/merging.rs
+++ b/compiler/rustc_monomorphize/src/partitioning/merging.rs
@@ -24,7 +24,7 @@ pub fn merge_codegen_units<'tcx>(
     // smallest into each other) we're sure to start off with a deterministic
     // order (sorted by name). This'll mean that if two cgus have the same size
     // the stable sort below will keep everything nice and deterministic.
-    codegen_units.sort_by(|a, b| a.name().as_str().partial_cmp(b.name().as_str()).unwrap());
+    codegen_units.sort_by(|a, b| a.name().as_str().cmp(b.name().as_str()));
 
     // This map keeps track of what got merged into what.
     let mut cgu_contents: FxHashMap<Symbol, Vec<Symbol>> =
diff --git a/compiler/rustc_monomorphize/src/partitioning/mod.rs b/compiler/rustc_monomorphize/src/partitioning/mod.rs
index 524c51d88d7..7ac1c9e057e 100644
--- a/compiler/rustc_monomorphize/src/partitioning/mod.rs
+++ b/compiler/rustc_monomorphize/src/partitioning/mod.rs
@@ -252,7 +252,7 @@ pub fn partition<'tcx>(
         internalization_candidates: _,
     } = post_inlining;
 
-    result.sort_by(|a, b| a.name().as_str().partial_cmp(b.name().as_str()).unwrap());
+    result.sort_by(|a, b| a.name().as_str().cmp(b.name().as_str()));
 
     result
 }
diff --git a/compiler/rustc_parse/locales/en-US.ftl b/compiler/rustc_parse/messages.ftl
index e76e91fc1b1..ab7931d74d7 100644
--- a/compiler/rustc_parse/locales/en-US.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -412,8 +412,7 @@ parse_fn_ptr_with_generics = function pointer types may not have generic paramet
         *[false] a
     } `for` parameter list
 
-parse_invalid_identifier_with_leading_number = expected identifier, found number literal
-    .label = identifiers cannot start with a number
+parse_invalid_identifier_with_leading_number = identifiers cannot start with a number
 
 parse_maybe_fn_typo_with_impl = you might have meant to write `impl` instead of `fn`
     .suggestion = replace `fn` with `impl` here
@@ -710,7 +709,7 @@ parse_zero_chars = empty character literal
 parse_lone_slash = invalid trailing slash in literal
     .label = {parse_lone_slash}
 
-parse_unskipped_whitespace = non-ASCII whitespace symbol '{$ch}' is not skipped
+parse_unskipped_whitespace = whitespace symbol '{$ch}' is not skipped
     .label = {parse_unskipped_whitespace}
 
 parse_multiple_skipped_lines = multiple lines skipped by escaped newline
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 1662db36d10..63e5bc50513 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -939,6 +939,7 @@ pub(crate) struct ExpectedIdentifier {
     pub token: Token,
     pub suggest_raw: Option<SuggEscapeToUseAsIdentifier>,
     pub suggest_remove_comma: Option<SuggRemoveComma>,
+    pub help_cannot_start_number: Option<HelpIdentifierStartsWithNumber>,
 }
 
 impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for ExpectedIdentifier {
@@ -975,10 +976,18 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for ExpectedIdentifier {
             sugg.add_to_diagnostic(&mut diag);
         }
 
+        if let Some(help) = self.help_cannot_start_number {
+            help.add_to_diagnostic(&mut diag);
+        }
+
         diag
     }
 }
 
+#[derive(Subdiagnostic)]
+#[help(parse_invalid_identifier_with_leading_number)]
+pub(crate) struct HelpIdentifierStartsWithNumber;
+
 pub(crate) struct ExpectedSemi {
     pub span: Span,
     pub token: Token,
@@ -1208,14 +1217,6 @@ pub(crate) struct SelfParamNotFirst {
 }
 
 #[derive(Diagnostic)]
-#[diag(parse_invalid_identifier_with_leading_number)]
-pub(crate) struct InvalidIdentiferStartsWithNumber {
-    #[primary_span]
-    #[label]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(parse_const_generic_without_braces)]
 pub(crate) struct ConstGenericWithoutBraces {
     #[primary_span]
diff --git a/compiler/rustc_parse/src/lexer/diagnostics.rs b/compiler/rustc_parse/src/lexer/diagnostics.rs
index 27f4428d306..c4b9fdc81c5 100644
--- a/compiler/rustc_parse/src/lexer/diagnostics.rs
+++ b/compiler/rustc_parse/src/lexer/diagnostics.rs
@@ -71,7 +71,7 @@ pub fn report_suspicious_mismatch_block(
         .collect();
 
     // sort by `lo`, so the large block spans in the front
-    matched_spans.sort_by(|a, b| a.0.lo().cmp(&b.0.lo()));
+    matched_spans.sort_by_key(|(span, _)| span.lo());
 
     // We use larger block whose identation is well to cover those inner mismatched blocks
     // O(N^2) here, but we are on error reporting path, so it is fine
diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs
index d1c3fd0cd0f..17466cd0e6d 100644
--- a/compiler/rustc_parse/src/lib.rs
+++ b/compiler/rustc_parse/src/lib.rs
@@ -36,7 +36,7 @@ pub mod validate_attr;
 
 mod errors;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 // A bunch of utility functions of the form `parse_<thing>_from_<source>`
 // where <thing> includes crate, expr, item, stmt, tts, and one that
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 0a65c37ea7b..5b12bcc1822 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -8,14 +8,14 @@ use crate::errors::{
     ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg,
     ConstGenericWithoutBraces, ConstGenericWithoutBracesSugg, DocCommentOnParamType,
     DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg,
-    GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg, InInTypo,
-    IncorrectAwait, IncorrectSemicolon, IncorrectUseOfAwait, ParenthesesInForHead,
-    ParenthesesInForHeadSugg, PatternMethodParamWithoutBody, QuestionMarkInType,
-    QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath,
-    StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens, StructLiteralNeedingParensSugg,
-    SuggEscapeToUseAsIdentifier, SuggRemoveComma, UnexpectedConstInGenericParam,
-    UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets,
-    UseEqInstead,
+    GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg,
+    HelpIdentifierStartsWithNumber, InInTypo, IncorrectAwait, IncorrectSemicolon,
+    IncorrectUseOfAwait, ParenthesesInForHead, ParenthesesInForHeadSugg,
+    PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst,
+    StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens,
+    StructLiteralNeedingParensSugg, SuggEscapeToUseAsIdentifier, SuggRemoveComma,
+    UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
+    UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead,
 };
 
 use crate::fluent_generated as fluent;
@@ -280,6 +280,7 @@ impl<'a> Parser<'a> {
             TokenKind::CloseDelim(Delimiter::Brace),
             TokenKind::CloseDelim(Delimiter::Parenthesis),
         ];
+
         let suggest_raw = match self.token.ident() {
             Some((ident, false))
                 if ident.is_raw_guess()
@@ -295,18 +296,19 @@ impl<'a> Parser<'a> {
             _ => None,
         };
 
-        let suggest_remove_comma =
-            if self.token == token::Comma && self.look_ahead(1, |t| t.is_ident()) {
-                Some(SuggRemoveComma { span: self.token.span })
-            } else {
-                None
-            };
+        let suggest_remove_comma = (self.token == token::Comma
+            && self.look_ahead(1, |t| t.is_ident()))
+        .then_some(SuggRemoveComma { span: self.token.span });
+
+        let help_cannot_start_number =
+            self.is_lit_bad_ident().then_some(HelpIdentifierStartsWithNumber);
 
         let err = ExpectedIdentifier {
             span: self.token.span,
             token: self.token.clone(),
             suggest_raw,
             suggest_remove_comma,
+            help_cannot_start_number,
         };
         let mut err = err.into_diagnostic(&self.sess.span_diagnostic);
 
@@ -365,6 +367,17 @@ impl<'a> Parser<'a> {
         err
     }
 
+    /// Checks if the current token is a integer or float literal and looks like
+    /// it could be a invalid identifier with digits at the start.
+    pub(super) fn is_lit_bad_ident(&mut self) -> bool {
+        matches!(self.token.uninterpolate().kind, token::Literal(Lit { kind: token::LitKind::Integer | token::LitKind::Float, .. })
+            // ensure that the integer literal is followed by a *invalid*
+            // suffix: this is how we know that it is a identifier with an
+            // invalid beginning.
+            if rustc_ast::MetaItemLit::from_token(&self.token).is_none()
+        )
+    }
+
     pub(super) fn expected_one_of_not_found(
         &mut self,
         edible: &[TokenKind],
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index e00eda47c66..1d12dd47094 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -2105,7 +2105,7 @@ impl<'a> Parser<'a> {
             ClosureBinder::NotPresent
         };
 
-        let constness = self.parse_closure_constness(Case::Sensitive);
+        let constness = self.parse_closure_constness();
 
         let movability =
             if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable };
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 6e9b447fa61..3251dd6d0c6 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -1196,9 +1196,13 @@ impl<'a> Parser<'a> {
         self.parse_constness_(case, false)
     }
 
-    /// Parses constness for closures
-    fn parse_closure_constness(&mut self, case: Case) -> Const {
-        self.parse_constness_(case, true)
+    /// Parses constness for closures (case sensitive, feature-gated)
+    fn parse_closure_constness(&mut self) -> Const {
+        let constness = self.parse_constness_(Case::Sensitive, true);
+        if let Const::Yes(span) = constness {
+            self.sess.gated_spans.gate(sym::const_closures, span);
+        }
+        constness
     }
 
     fn parse_constness_(&mut self, case: Case, is_closure: bool) -> Const {
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 8e920f1c421..fc9f1d1330a 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -348,6 +348,10 @@ impl<'a> Parser<'a> {
             lo = self.token.span;
         }
 
+        if self.is_lit_bad_ident() {
+            return Err(self.expected_ident_found());
+        }
+
         let pat = if self.check(&token::BinOp(token::And)) || self.token.kind == token::AndAnd {
             self.parse_pat_deref(expected)?
         } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 92a22ffc2b0..fbe5b88c49e 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -273,7 +273,6 @@ impl<'a> Parser<'a> {
             self.bump();
         }
 
-        self.report_invalid_identifier_error()?;
         let (pat, colon) =
             self.parse_pat_before_ty(None, RecoverComma::Yes, PatternLocation::LetBinding)?;
 
@@ -366,17 +365,6 @@ impl<'a> Parser<'a> {
         Ok(P(ast::Local { ty, pat, kind, id: DUMMY_NODE_ID, span: lo.to(hi), attrs, tokens: None }))
     }
 
-    /// report error for `let 1x = 123`
-    pub fn report_invalid_identifier_error(&mut self) -> PResult<'a, ()> {
-        if let token::Literal(lit) = self.token.uninterpolate().kind &&
-            rustc_ast::MetaItemLit::from_token(&self.token).is_none() &&
-            (lit.kind == token::LitKind::Integer || lit.kind == token::LitKind::Float) &&
-            self.look_ahead(1, |t| matches!(t.kind, token::Eq) || matches!(t.kind, token::Colon ) ) {
-                return Err(self.sess.create_err(errors::InvalidIdentiferStartsWithNumber { span: self.token.span }));
-        }
-        Ok(())
-    }
-
     fn check_let_else_init_bool_expr(&self, init: &ast::Expr) {
         if let ast::ExprKind::Binary(op, ..) = init.kind {
             if op.node.lazy() {
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 6fe4da71f6b..3d9d2cc62e3 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -624,10 +624,12 @@ impl<'a> Parser<'a> {
     ///
     /// Note that this does *not* parse bare trait objects.
     fn parse_dyn_ty(&mut self, impl_dyn_multi: &mut bool) -> PResult<'a, TyKind> {
+        let lo = self.token.span;
         self.bump(); // `dyn`
 
         // parse dyn* types
         let syntax = if self.eat(&TokenKind::BinOp(token::Star)) {
+            self.sess.gated_spans.gate(sym::dyn_star, lo.to(self.prev_token.span));
             TraitObjectSyntax::DynStar
         } else {
             TraitObjectSyntax::Dyn
diff --git a/compiler/rustc_passes/locales/en-US.ftl b/compiler/rustc_passes/messages.ftl
index 3fa78efc290..3fa78efc290 100644
--- a/compiler/rustc_passes/locales/en-US.ftl
+++ b/compiler/rustc_passes/messages.ftl
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 28e06e5bb3e..73ee51d5f3a 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -2,8 +2,8 @@
 // closely. The idea is that all reachable symbols are live, codes called
 // from live codes are live, and everything else is dead.
 
+use hir::def_id::{LocalDefIdMap, LocalDefIdSet};
 use itertools::Itertools;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::MultiSpan;
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Res};
@@ -45,17 +45,17 @@ struct MarkSymbolVisitor<'tcx> {
     worklist: Vec<LocalDefId>,
     tcx: TyCtxt<'tcx>,
     maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>,
-    live_symbols: FxHashSet<LocalDefId>,
+    live_symbols: LocalDefIdSet,
     repr_has_repr_c: bool,
     repr_has_repr_simd: bool,
     in_pat: bool,
     ignore_variant_stack: Vec<DefId>,
     // maps from tuple struct constructors to tuple struct items
-    struct_constructors: FxHashMap<LocalDefId, LocalDefId>,
+    struct_constructors: LocalDefIdMap<LocalDefId>,
     // maps from ADTs to ignored derived traits (e.g. Debug and Clone)
     // and the span of their respective impl (i.e., part of the derive
     // macro)
-    ignored_derived_traits: FxHashMap<LocalDefId, Vec<(DefId, DefId)>>,
+    ignored_derived_traits: LocalDefIdMap<Vec<(DefId, DefId)>>,
 }
 
 impl<'tcx> MarkSymbolVisitor<'tcx> {
@@ -237,12 +237,18 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
     }
 
     fn mark_live_symbols(&mut self) {
-        let mut scanned = FxHashSet::default();
+        let mut scanned = LocalDefIdSet::default();
         while let Some(id) = self.worklist.pop() {
             if !scanned.insert(id) {
                 continue;
             }
 
+            // Avoid accessing the HIR for the synthesized associated type generated for RPITITs.
+            if self.tcx.opt_rpitit_info(id).is_some() {
+                self.live_symbols.insert(id);
+                continue;
+            }
+
             // in the case of tuple struct constructors we want to check the item, not the generated
             // tuple struct constructor function
             let id = self.struct_constructors.get(&id).copied().unwrap_or(id);
@@ -506,7 +512,7 @@ fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool
 fn check_item<'tcx>(
     tcx: TyCtxt<'tcx>,
     worklist: &mut Vec<LocalDefId>,
-    struct_constructors: &mut FxHashMap<LocalDefId, LocalDefId>,
+    struct_constructors: &mut LocalDefIdMap<LocalDefId>,
     id: hir::ItemId,
 ) {
     let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id);
@@ -583,9 +589,7 @@ fn check_foreign_item(tcx: TyCtxt<'_>, worklist: &mut Vec<LocalDefId>, id: hir::
     }
 }
 
-fn create_and_seed_worklist(
-    tcx: TyCtxt<'_>,
-) -> (Vec<LocalDefId>, FxHashMap<LocalDefId, LocalDefId>) {
+fn create_and_seed_worklist(tcx: TyCtxt<'_>) -> (Vec<LocalDefId>, LocalDefIdMap<LocalDefId>) {
     let effective_visibilities = &tcx.effective_visibilities(());
     // see `MarkSymbolVisitor::struct_constructors`
     let mut struct_constructors = Default::default();
@@ -617,7 +621,7 @@ fn create_and_seed_worklist(
 fn live_symbols_and_ignored_derived_traits(
     tcx: TyCtxt<'_>,
     (): (),
-) -> (FxHashSet<LocalDefId>, FxHashMap<LocalDefId, Vec<(DefId, DefId)>>) {
+) -> (LocalDefIdSet, LocalDefIdMap<Vec<(DefId, DefId)>>) {
     let (worklist, struct_constructors) = create_and_seed_worklist(tcx);
     let mut symbol_visitor = MarkSymbolVisitor {
         worklist,
@@ -629,7 +633,7 @@ fn live_symbols_and_ignored_derived_traits(
         in_pat: false,
         ignore_variant_stack: vec![],
         struct_constructors,
-        ignored_derived_traits: FxHashMap::default(),
+        ignored_derived_traits: Default::default(),
     };
     symbol_visitor.mark_live_symbols();
     (symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits)
@@ -643,8 +647,8 @@ struct DeadVariant {
 
 struct DeadVisitor<'tcx> {
     tcx: TyCtxt<'tcx>,
-    live_symbols: &'tcx FxHashSet<LocalDefId>,
-    ignored_derived_traits: &'tcx FxHashMap<LocalDefId, Vec<(DefId, DefId)>>,
+    live_symbols: &'tcx LocalDefIdSet,
+    ignored_derived_traits: &'tcx LocalDefIdMap<Vec<(DefId, DefId)>>,
 }
 
 enum ShouldWarnAboutField {
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs
index 0cb8424082c..b7e07aff42b 100644
--- a/compiler/rustc_passes/src/lib.rs
+++ b/compiler/rustc_passes/src/lib.rs
@@ -42,7 +42,7 @@ pub mod stability;
 mod upvars;
 mod weak_lang_items;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 pub fn provide(providers: &mut Providers) {
     check_attr::provide(providers);
diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs
index d6cb68a9c20..a5f7b07fe52 100644
--- a/compiler/rustc_passes/src/reachable.rs
+++ b/compiler/rustc_passes/src/reachable.rs
@@ -5,7 +5,7 @@
 // makes all other generics or inline functions that it references
 // reachable as well.
 
-use rustc_data_structures::fx::FxHashSet;
+use hir::def_id::LocalDefIdSet;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -63,7 +63,7 @@ struct ReachableContext<'tcx> {
     tcx: TyCtxt<'tcx>,
     maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>,
     // The set of items which must be exported in the linkage sense.
-    reachable_symbols: FxHashSet<LocalDefId>,
+    reachable_symbols: LocalDefIdSet,
     // A worklist of item IDs. Each item ID in this worklist will be inlined
     // and will be scanned for further references.
     // FIXME(eddyb) benchmark if this would be faster as a `VecDeque`.
@@ -175,7 +175,7 @@ impl<'tcx> ReachableContext<'tcx> {
 
     // Step 2: Mark all symbols that the symbols on the worklist touch.
     fn propagate(&mut self) {
-        let mut scanned = FxHashSet::default();
+        let mut scanned = LocalDefIdSet::default();
         while let Some(search_item) = self.worklist.pop() {
             if !scanned.insert(search_item) {
                 continue;
@@ -361,7 +361,7 @@ fn has_custom_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
         || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
 }
 
-fn reachable_set(tcx: TyCtxt<'_>, (): ()) -> FxHashSet<LocalDefId> {
+fn reachable_set(tcx: TyCtxt<'_>, (): ()) -> LocalDefIdSet {
     let effective_visibilities = &tcx.effective_visibilities(());
 
     let any_library =
diff --git a/compiler/rustc_plugin_impl/locales/en-US.ftl b/compiler/rustc_plugin_impl/messages.ftl
index 8db32a42c1d..8db32a42c1d 100644
--- a/compiler/rustc_plugin_impl/locales/en-US.ftl
+++ b/compiler/rustc_plugin_impl/messages.ftl
diff --git a/compiler/rustc_plugin_impl/src/lib.rs b/compiler/rustc_plugin_impl/src/lib.rs
index 3f03eef9ee3..672189e22cf 100644
--- a/compiler/rustc_plugin_impl/src/lib.rs
+++ b/compiler/rustc_plugin_impl/src/lib.rs
@@ -18,7 +18,7 @@ use rustc_macros::fluent_messages;
 mod errors;
 pub mod load;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 /// Structure used to register plugins.
 ///
diff --git a/compiler/rustc_privacy/locales/en-US.ftl b/compiler/rustc_privacy/messages.ftl
index a26d1b2b381..a26d1b2b381 100644
--- a/compiler/rustc_privacy/locales/en-US.ftl
+++ b/compiler/rustc_privacy/messages.ftl
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 99a44b0ca4d..ef7c68c1a33 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -46,7 +46,7 @@ use errors::{
     UnnamedItemIsPrivate,
 };
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 ////////////////////////////////////////////////////////////////////////////////
 /// Generic infrastructure used to implement specific visitors below.
diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs
index d7708a3bc3f..035bfe978f2 100644
--- a/compiler/rustc_query_impl/src/lib.rs
+++ b/compiler/rustc_query_impl/src/lib.rs
@@ -31,6 +31,7 @@ use rustc_span::Span;
 #[macro_use]
 mod plumbing;
 pub use plumbing::QueryCtxt;
+use rustc_query_system::dep_graph::SerializedDepNodeIndex;
 use rustc_query_system::query::*;
 #[cfg(parallel_compiler)]
 pub use rustc_query_system::query::{deadlock, QueryContext};
diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs
index 6522b1406be..35b7e5919e4 100644
--- a/compiler/rustc_query_impl/src/on_disk_cache.rs
+++ b/compiler/rustc_query_impl/src/on_disk_cache.rs
@@ -388,6 +388,12 @@ impl<'sess> OnDiskCache<'sess> {
         debug_assert!(prev.is_none());
     }
 
+    /// Return whether the cached query result can be decoded.
+    pub fn loadable_from_disk(&self, dep_node_index: SerializedDepNodeIndex) -> bool {
+        self.query_result_index.contains_key(&dep_node_index)
+        // with_decoder is infallible, so we can stop here
+    }
+
     /// Returns the cached query result if there is something in the cache for
     /// the given `SerializedDepNodeIndex`; otherwise returns `None`.
     pub fn try_load_query_result<'tcx, T>(
@@ -398,7 +404,9 @@ impl<'sess> OnDiskCache<'sess> {
     where
         T: for<'a> Decodable<CacheDecoder<'a, 'tcx>>,
     {
-        self.load_indexed(tcx, dep_node_index, &self.query_result_index)
+        let opt_value = self.load_indexed(tcx, dep_node_index, &self.query_result_index);
+        debug_assert_eq!(opt_value.is_some(), self.loadable_from_disk(dep_node_index));
+        opt_value
     }
 
     /// Stores side effect emitted during computation of an anonymous query.
@@ -428,8 +436,8 @@ impl<'sess> OnDiskCache<'sess> {
         T: for<'a> Decodable<CacheDecoder<'a, 'tcx>>,
     {
         let pos = index.get(&dep_node_index).cloned()?;
-
-        self.with_decoder(tcx, pos, |decoder| Some(decode_tagged(decoder, dep_node_index)))
+        let value = self.with_decoder(tcx, pos, |decoder| decode_tagged(decoder, dep_node_index));
+        Some(value)
     }
 
     fn with_decoder<'a, 'tcx, T, F: for<'s> FnOnce(&mut CacheDecoder<'s, 'tcx>) -> T>(
diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
index 005ce16dbb9..ca3c3997df0 100644
--- a/compiler/rustc_query_impl/src/plumbing.rs
+++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -364,6 +364,14 @@ where
     }
 }
 
+pub(crate) fn loadable_from_disk<'tcx>(tcx: QueryCtxt<'tcx>, id: SerializedDepNodeIndex) -> bool {
+    if let Some(cache) = tcx.on_disk_cache().as_ref() {
+        cache.loadable_from_disk(id)
+    } else {
+        false
+    }
+}
+
 pub(crate) fn try_load_from_disk<'tcx, V>(
     tcx: QueryCtxt<'tcx>,
     id: SerializedDepNodeIndex,
@@ -535,6 +543,21 @@ macro_rules! define_queries {
                 })
             }
 
+            #[inline]
+            fn loadable_from_disk(
+                self,
+                _qcx: QueryCtxt<'tcx>,
+                _key: &Self::Key,
+                _index: SerializedDepNodeIndex,
+            ) -> bool {
+                should_ever_cache_on_disk!([$($modifiers)*] {
+                    self.cache_on_disk(_qcx.tcx, _key) &&
+                        $crate::plumbing::loadable_from_disk(_qcx, _index)
+                } {
+                    false
+                })
+            }
+
             #[inline(always)]
             fn anon(self) -> bool {
                 is_anon!([$($modifiers)*])
diff --git a/compiler/rustc_query_system/locales/en-US.ftl b/compiler/rustc_query_system/messages.ftl
index 870e824039c..870e824039c 100644
--- a/compiler/rustc_query_system/locales/en-US.ftl
+++ b/compiler/rustc_query_system/messages.ftl
diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs
index 59e0c359745..8a9a1238606 100644
--- a/compiler/rustc_query_system/src/dep_graph/graph.rs
+++ b/compiler/rustc_query_system/src/dep_graph/graph.rs
@@ -70,7 +70,7 @@ impl DepNodeColor {
     }
 }
 
-struct DepGraphData<K: DepKind> {
+pub struct DepGraphData<K: DepKind> {
     /// The new encoding of the dependency graph, optimized for red/green
     /// tracking. The `current` field is the dependency graph of only the
     /// current compilation session: We don't merge the previous dep-graph into
@@ -168,6 +168,11 @@ impl<K: DepKind> DepGraph<K> {
         DepGraph { data: None, virtual_dep_node_index: Lrc::new(AtomicU32::new(0)) }
     }
 
+    #[inline]
+    pub fn data(&self) -> Option<&DepGraphData<K>> {
+        self.data.as_deref()
+    }
+
     /// Returns `true` if we are actually building the full dep-graph, and `false` otherwise.
     #[inline]
     pub fn is_fully_enabled(&self) -> bool {
@@ -252,6 +257,38 @@ impl<K: DepKind> DepGraph<K> {
         K::with_deps(TaskDepsRef::Forbid, op)
     }
 
+    #[inline(always)]
+    pub fn with_task<Ctxt: HasDepContext<DepKind = K>, A: Debug, R>(
+        &self,
+        key: DepNode<K>,
+        cx: Ctxt,
+        arg: A,
+        task: fn(Ctxt, A) -> R,
+        hash_result: Option<fn(&mut StableHashingContext<'_>, &R) -> Fingerprint>,
+    ) -> (R, DepNodeIndex) {
+        match self.data() {
+            Some(data) => data.with_task(key, cx, arg, task, hash_result),
+            None => (task(cx, arg), self.next_virtual_depnode_index()),
+        }
+    }
+
+    pub fn with_anon_task<Tcx: DepContext<DepKind = K>, OP, R>(
+        &self,
+        cx: Tcx,
+        dep_kind: K,
+        op: OP,
+    ) -> (R, DepNodeIndex)
+    where
+        OP: FnOnce() -> R,
+    {
+        match self.data() {
+            Some(data) => data.with_anon_task(cx, dep_kind, op),
+            None => (op(), self.next_virtual_depnode_index()),
+        }
+    }
+}
+
+impl<K: DepKind> DepGraphData<K> {
     /// Starts a new dep-graph task. Dep-graph tasks are specified
     /// using a free function (`task`) and **not** a closure -- this
     /// is intentional because we want to exercise tight control over
@@ -288,29 +325,6 @@ impl<K: DepKind> DepGraph<K> {
         task: fn(Ctxt, A) -> R,
         hash_result: Option<fn(&mut StableHashingContext<'_>, &R) -> Fingerprint>,
     ) -> (R, DepNodeIndex) {
-        if self.is_fully_enabled() {
-            self.with_task_impl(key, cx, arg, task, hash_result)
-        } else {
-            // Incremental compilation is turned off. We just execute the task
-            // without tracking. We still provide a dep-node index that uniquely
-            // identifies the task so that we have a cheap way of referring to
-            // the query for self-profiling.
-            (task(cx, arg), self.next_virtual_depnode_index())
-        }
-    }
-
-    #[inline(always)]
-    fn with_task_impl<Ctxt: HasDepContext<DepKind = K>, A: Debug, R>(
-        &self,
-        key: DepNode<K>,
-        cx: Ctxt,
-        arg: A,
-        task: fn(Ctxt, A) -> R,
-        hash_result: Option<fn(&mut StableHashingContext<'_>, &R) -> Fingerprint>,
-    ) -> (R, DepNodeIndex) {
-        // This function is only called when the graph is enabled.
-        let data = self.data.as_ref().unwrap();
-
         // If the following assertion triggers, it can have two reasons:
         // 1. Something is wrong with DepNode creation, either here or
         //    in `DepGraph::try_mark_green()`.
@@ -351,9 +365,9 @@ impl<K: DepKind> DepGraph<K> {
         let print_status = cfg!(debug_assertions) && dcx.sess().opts.unstable_opts.dep_tasks;
 
         // Intern the new `DepNode`.
-        let (dep_node_index, prev_and_color) = data.current.intern_node(
+        let (dep_node_index, prev_and_color) = self.current.intern_node(
             dcx.profiler(),
-            &data.previous,
+            &self.previous,
             key,
             edges,
             current_fingerprint,
@@ -364,12 +378,12 @@ impl<K: DepKind> DepGraph<K> {
 
         if let Some((prev_index, color)) = prev_and_color {
             debug_assert!(
-                data.colors.get(prev_index).is_none(),
+                self.colors.get(prev_index).is_none(),
                 "DepGraph::with_task() - Duplicate DepNodeColor \
                             insertion for {key:?}"
             );
 
-            data.colors.insert(prev_index, color);
+            self.colors.insert(prev_index, color);
         }
 
         (result, dep_node_index)
@@ -388,57 +402,55 @@ impl<K: DepKind> DepGraph<K> {
     {
         debug_assert!(!cx.is_eval_always(dep_kind));
 
-        if let Some(ref data) = self.data {
-            let task_deps = Lock::new(TaskDeps::default());
-            let result = K::with_deps(TaskDepsRef::Allow(&task_deps), op);
-            let task_deps = task_deps.into_inner();
-            let task_deps = task_deps.reads;
-
-            let dep_node_index = match task_deps.len() {
-                0 => {
-                    // Because the dep-node id of anon nodes is computed from the sets of its
-                    // dependencies we already know what the ID of this dependency-less node is
-                    // going to be (i.e. equal to the precomputed
-                    // `SINGLETON_DEPENDENCYLESS_ANON_NODE`). As a consequence we can skip creating
-                    // a `StableHasher` and sending the node through interning.
-                    DepNodeIndex::SINGLETON_DEPENDENCYLESS_ANON_NODE
-                }
-                1 => {
-                    // When there is only one dependency, don't bother creating a node.
-                    task_deps[0]
-                }
-                _ => {
-                    // The dep node indices are hashed here instead of hashing the dep nodes of the
-                    // dependencies. These indices may refer to different nodes per session, but this isn't
-                    // a problem here because we that ensure the final dep node hash is per session only by
-                    // combining it with the per session random number `anon_id_seed`. This hash only need
-                    // to map the dependencies to a single value on a per session basis.
-                    let mut hasher = StableHasher::new();
-                    task_deps.hash(&mut hasher);
-
-                    let target_dep_node = DepNode {
-                        kind: dep_kind,
-                        // Fingerprint::combine() is faster than sending Fingerprint
-                        // through the StableHasher (at least as long as StableHasher
-                        // is so slow).
-                        hash: data.current.anon_id_seed.combine(hasher.finish()).into(),
-                    };
+        let task_deps = Lock::new(TaskDeps::default());
+        let result = K::with_deps(TaskDepsRef::Allow(&task_deps), op);
+        let task_deps = task_deps.into_inner();
+        let task_deps = task_deps.reads;
+
+        let dep_node_index = match task_deps.len() {
+            0 => {
+                // Because the dep-node id of anon nodes is computed from the sets of its
+                // dependencies we already know what the ID of this dependency-less node is
+                // going to be (i.e. equal to the precomputed
+                // `SINGLETON_DEPENDENCYLESS_ANON_NODE`). As a consequence we can skip creating
+                // a `StableHasher` and sending the node through interning.
+                DepNodeIndex::SINGLETON_DEPENDENCYLESS_ANON_NODE
+            }
+            1 => {
+                // When there is only one dependency, don't bother creating a node.
+                task_deps[0]
+            }
+            _ => {
+                // The dep node indices are hashed here instead of hashing the dep nodes of the
+                // dependencies. These indices may refer to different nodes per session, but this isn't
+                // a problem here because we that ensure the final dep node hash is per session only by
+                // combining it with the per session random number `anon_id_seed`. This hash only need
+                // to map the dependencies to a single value on a per session basis.
+                let mut hasher = StableHasher::new();
+                task_deps.hash(&mut hasher);
+
+                let target_dep_node = DepNode {
+                    kind: dep_kind,
+                    // Fingerprint::combine() is faster than sending Fingerprint
+                    // through the StableHasher (at least as long as StableHasher
+                    // is so slow).
+                    hash: self.current.anon_id_seed.combine(hasher.finish()).into(),
+                };
 
-                    data.current.intern_new_node(
-                        cx.profiler(),
-                        target_dep_node,
-                        task_deps,
-                        Fingerprint::ZERO,
-                    )
-                }
-            };
+                self.current.intern_new_node(
+                    cx.profiler(),
+                    target_dep_node,
+                    task_deps,
+                    Fingerprint::ZERO,
+                )
+            }
+        };
 
-            (result, dep_node_index)
-        } else {
-            (op(), self.next_virtual_depnode_index())
-        }
+        (result, dep_node_index)
     }
+}
 
+impl<K: DepKind> DepGraph<K> {
     #[inline]
     pub fn read_index(&self, dep_node_index: DepNodeIndex) {
         if let Some(ref data) = self.data {
@@ -519,9 +531,9 @@ impl<K: DepKind> DepGraph<K> {
             // value to an existing node.
             //
             // For sanity, we still check that the loaded stable hash and the new one match.
-            if let Some(dep_node_index) = self.dep_node_index_of_opt(&node) {
+            if let Some(dep_node_index) = data.dep_node_index_of_opt(&node) {
                 let _current_fingerprint =
-                    crate::query::incremental_verify_ich(cx, result, &node, hash_result);
+                    crate::query::incremental_verify_ich(cx, data, result, &node, hash_result);
 
                 #[cfg(debug_assertions)]
                 if hash_result.is_some() {
@@ -577,32 +589,57 @@ impl<K: DepKind> DepGraph<K> {
             self.next_virtual_depnode_index()
         }
     }
+}
 
+impl<K: DepKind> DepGraphData<K> {
     #[inline]
-    pub fn dep_node_index_of(&self, dep_node: &DepNode<K>) -> DepNodeIndex {
-        self.dep_node_index_of_opt(dep_node).unwrap()
+    pub fn dep_node_index_of_opt(&self, dep_node: &DepNode<K>) -> Option<DepNodeIndex> {
+        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()
+        }
     }
 
     #[inline]
-    pub fn dep_node_index_of_opt(&self, dep_node: &DepNode<K>) -> Option<DepNodeIndex> {
-        let data = self.data.as_ref().unwrap();
-        let current = &data.current;
+    pub fn dep_node_exists(&self, dep_node: &DepNode<K>) -> bool {
+        self.dep_node_index_of_opt(dep_node).is_some()
+    }
 
-        if let Some(prev_index) = data.previous.node_to_index_opt(dep_node) {
-            current.prev_index_to_index.lock()[prev_index]
+    fn node_color(&self, dep_node: &DepNode<K>) -> Option<DepNodeColor> {
+        if let Some(prev_index) = self.previous.node_to_index_opt(dep_node) {
+            self.colors.get(prev_index)
         } else {
-            current.new_node_to_index.get_shard_by_value(dep_node).lock().get(dep_node).copied()
+            // This is a node that did not exist in the previous compilation session.
+            None
         }
     }
 
-    #[inline]
-    pub fn dep_node_exists(&self, dep_node: &DepNode<K>) -> bool {
-        self.data.is_some() && self.dep_node_index_of_opt(dep_node).is_some()
+    /// Returns true if the given node has been marked as green during the
+    /// current compilation session. Used in various assertions
+    pub fn is_green(&self, dep_node: &DepNode<K>) -> bool {
+        self.node_color(dep_node).map_or(false, |c| c.is_green())
     }
 
     #[inline]
     pub fn prev_fingerprint_of(&self, dep_node: &DepNode<K>) -> Option<Fingerprint> {
-        self.data.as_ref().unwrap().previous.fingerprint_of(dep_node)
+        self.previous.fingerprint_of(dep_node)
+    }
+
+    pub fn mark_debug_loaded_from_disk(&self, dep_node: DepNode<K>) {
+        self.debug_loaded_from_disk.lock().insert(dep_node);
+    }
+}
+
+impl<K: DepKind> DepGraph<K> {
+    #[inline]
+    pub fn dep_node_exists(&self, dep_node: &DepNode<K>) -> bool {
+        self.data.as_ref().and_then(|data| data.dep_node_index_of_opt(dep_node)).is_some()
     }
 
     /// Checks whether a previous work product exists for `v` and, if
@@ -617,10 +654,6 @@ impl<K: DepKind> DepGraph<K> {
         &self.data.as_ref().unwrap().previous_work_products
     }
 
-    pub fn mark_debug_loaded_from_disk(&self, dep_node: DepNode<K>) {
-        self.data.as_ref().unwrap().debug_loaded_from_disk.lock().insert(dep_node);
-    }
-
     pub fn debug_was_loaded_from_disk(&self, dep_node: DepNode<K>) -> bool {
         self.data.as_ref().unwrap().debug_loaded_from_disk.lock().contains(&dep_node)
     }
@@ -645,17 +678,22 @@ impl<K: DepKind> DepGraph<K> {
 
     fn node_color(&self, dep_node: &DepNode<K>) -> Option<DepNodeColor> {
         if let Some(ref data) = self.data {
-            if let Some(prev_index) = data.previous.node_to_index_opt(dep_node) {
-                return data.colors.get(prev_index);
-            } else {
-                // This is a node that did not exist in the previous compilation session.
-                return None;
-            }
+            return data.node_color(dep_node);
         }
 
         None
     }
 
+    pub fn try_mark_green<Qcx: QueryContext<DepKind = K>>(
+        &self,
+        qcx: Qcx,
+        dep_node: &DepNode<K>,
+    ) -> Option<(SerializedDepNodeIndex, DepNodeIndex)> {
+        self.data().and_then(|data| data.try_mark_green(qcx, dep_node))
+    }
+}
+
+impl<K: DepKind> DepGraphData<K> {
     /// Try to mark a node index for the node dep_node.
     ///
     /// A node will have an index, when it's already been marked green, or when we can mark it
@@ -668,26 +706,23 @@ impl<K: DepKind> DepGraph<K> {
     ) -> Option<(SerializedDepNodeIndex, DepNodeIndex)> {
         debug_assert!(!qcx.dep_context().is_eval_always(dep_node.kind));
 
-        // Return None if the dep graph is disabled
-        let data = self.data.as_ref()?;
-
         // Return None if the dep node didn't exist in the previous session
-        let prev_index = data.previous.node_to_index_opt(dep_node)?;
+        let prev_index = self.previous.node_to_index_opt(dep_node)?;
 
-        match data.colors.get(prev_index) {
+        match self.colors.get(prev_index) {
             Some(DepNodeColor::Green(dep_node_index)) => return Some((prev_index, dep_node_index)),
             Some(DepNodeColor::Red) => return None,
             None => {}
         }
 
-        let backtrace = backtrace_printer(qcx.dep_context().sess(), data, prev_index);
+        let backtrace = backtrace_printer(qcx.dep_context().sess(), self, prev_index);
 
         // This DepNode and the corresponding query invocation existed
         // in the previous compilation session too, so we can try to
         // mark it as green by recursively marking all of its
         // dependencies green.
         let ret = self
-            .try_mark_previous_green(qcx, data, prev_index, &dep_node)
+            .try_mark_previous_green(qcx, prev_index, &dep_node)
             .map(|dep_node_index| (prev_index, dep_node_index));
 
         // We succeeded, no backtrace.
@@ -695,16 +730,15 @@ impl<K: DepKind> DepGraph<K> {
         return ret;
     }
 
-    #[instrument(skip(self, qcx, data, parent_dep_node_index), level = "debug")]
+    #[instrument(skip(self, qcx, parent_dep_node_index), level = "debug")]
     fn try_mark_parent_green<Qcx: QueryContext<DepKind = K>>(
         &self,
         qcx: Qcx,
-        data: &DepGraphData<K>,
         parent_dep_node_index: SerializedDepNodeIndex,
         dep_node: &DepNode<K>,
     ) -> Option<()> {
-        let dep_dep_node_color = data.colors.get(parent_dep_node_index);
-        let dep_dep_node = &data.previous.index_to_node(parent_dep_node_index);
+        let dep_dep_node_color = self.colors.get(parent_dep_node_index);
+        let dep_dep_node = &self.previous.index_to_node(parent_dep_node_index);
 
         match dep_dep_node_color {
             Some(DepNodeColor::Green(_)) => {
@@ -733,8 +767,7 @@ impl<K: DepKind> DepGraph<K> {
                 dep_dep_node, dep_dep_node.hash,
             );
 
-            let node_index =
-                self.try_mark_previous_green(qcx, data, parent_dep_node_index, dep_dep_node);
+            let node_index = self.try_mark_previous_green(qcx, parent_dep_node_index, dep_dep_node);
 
             if node_index.is_some() {
                 debug!("managed to MARK dependency {dep_dep_node:?} as green",);
@@ -750,7 +783,7 @@ impl<K: DepKind> DepGraph<K> {
             return None;
         }
 
-        let dep_dep_node_color = data.colors.get(parent_dep_node_index);
+        let dep_dep_node_color = self.colors.get(parent_dep_node_index);
 
         match dep_dep_node_color {
             Some(DepNodeColor::Green(_)) => {
@@ -783,30 +816,29 @@ impl<K: DepKind> DepGraph<K> {
     }
 
     /// Try to mark a dep-node which existed in the previous compilation session as green.
-    #[instrument(skip(self, qcx, data, prev_dep_node_index), level = "debug")]
+    #[instrument(skip(self, qcx, prev_dep_node_index), level = "debug")]
     fn try_mark_previous_green<Qcx: QueryContext<DepKind = K>>(
         &self,
         qcx: Qcx,
-        data: &DepGraphData<K>,
         prev_dep_node_index: SerializedDepNodeIndex,
         dep_node: &DepNode<K>,
     ) -> Option<DepNodeIndex> {
         #[cfg(not(parallel_compiler))]
         {
             debug_assert!(!self.dep_node_exists(dep_node));
-            debug_assert!(data.colors.get(prev_dep_node_index).is_none());
+            debug_assert!(self.colors.get(prev_dep_node_index).is_none());
         }
 
         // We never try to mark eval_always nodes as green
         debug_assert!(!qcx.dep_context().is_eval_always(dep_node.kind));
 
-        debug_assert_eq!(data.previous.index_to_node(prev_dep_node_index), *dep_node);
+        debug_assert_eq!(self.previous.index_to_node(prev_dep_node_index), *dep_node);
 
-        let prev_deps = data.previous.edge_targets_from(prev_dep_node_index);
+        let prev_deps = self.previous.edge_targets_from(prev_dep_node_index);
 
         for &dep_dep_node_index in prev_deps {
-            let backtrace = backtrace_printer(qcx.dep_context().sess(), data, dep_dep_node_index);
-            let success = self.try_mark_parent_green(qcx, data, dep_dep_node_index, dep_node);
+            let backtrace = backtrace_printer(qcx.dep_context().sess(), self, dep_dep_node_index);
+            let success = self.try_mark_parent_green(qcx, dep_dep_node_index, dep_node);
             backtrace.disable();
             success?;
         }
@@ -819,9 +851,9 @@ impl<K: DepKind> DepGraph<K> {
 
         // We allocating an entry for the node in the current dependency graph and
         // adding all the appropriate edges imported from the previous graph
-        let dep_node_index = data.current.promote_node_and_deps_to_current(
+        let dep_node_index = self.current.promote_node_and_deps_to_current(
             qcx.dep_context().profiler(),
-            &data.previous,
+            &self.previous,
             prev_dep_node_index,
         );
 
@@ -833,20 +865,20 @@ impl<K: DepKind> DepGraph<K> {
 
         #[cfg(not(parallel_compiler))]
         debug_assert!(
-            data.colors.get(prev_dep_node_index).is_none(),
+            self.colors.get(prev_dep_node_index).is_none(),
             "DepGraph::try_mark_previous_green() - Duplicate DepNodeColor \
                       insertion for {dep_node:?}"
         );
 
         if !side_effects.is_empty() {
-            self.with_query_deserialization(|| {
-                self.emit_side_effects(qcx, data, dep_node_index, side_effects)
+            qcx.dep_context().dep_graph().with_query_deserialization(|| {
+                self.emit_side_effects(qcx, dep_node_index, side_effects)
             });
         }
 
         // ... and finally storing a "Green" entry in the color map.
         // Multiple threads can all write the same color here
-        data.colors.insert(prev_dep_node_index, DepNodeColor::Green(dep_node_index));
+        self.colors.insert(prev_dep_node_index, DepNodeColor::Green(dep_node_index));
 
         debug!("successfully marked {dep_node:?} as green");
         Some(dep_node_index)
@@ -859,11 +891,10 @@ impl<K: DepKind> DepGraph<K> {
     fn emit_side_effects<Qcx: QueryContext<DepKind = K>>(
         &self,
         qcx: Qcx,
-        data: &DepGraphData<K>,
         dep_node_index: DepNodeIndex,
         side_effects: QuerySideEffects,
     ) {
-        let mut processed = data.processed_side_effects.lock();
+        let mut processed = self.processed_side_effects.lock();
 
         if processed.insert(dep_node_index) {
             // We were the first to insert the node in the set so this thread
@@ -879,7 +910,9 @@ impl<K: DepKind> DepGraph<K> {
             }
         }
     }
+}
 
+impl<K: DepKind> DepGraph<K> {
     /// Returns true if the given node has been marked as red during the
     /// current compilation session. Used in various assertions
     pub fn is_red(&self, dep_node: &DepNode<K>) -> bool {
diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs
index ba83b775631..5a7b9ae2ab4 100644
--- a/compiler/rustc_query_system/src/dep_graph/mod.rs
+++ b/compiler/rustc_query_system/src/dep_graph/mod.rs
@@ -6,7 +6,8 @@ mod serialized;
 
 pub use dep_node::{DepKindStruct, DepNode, DepNodeParams, WorkProductId};
 pub use graph::{
-    hash_result, DepGraph, DepNodeColor, DepNodeIndex, TaskDeps, TaskDepsRef, WorkProduct,
+    hash_result, DepGraph, DepGraphData, DepNodeColor, DepNodeIndex, TaskDeps, TaskDepsRef,
+    WorkProduct,
 };
 pub use query::DepGraphQuery;
 pub use serialized::{SerializedDepGraph, SerializedDepNodeIndex};
diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs
index 6cc4c9a7e1e..bb812b006e9 100644
--- a/compiler/rustc_query_system/src/lib.rs
+++ b/compiler/rustc_query_system/src/lib.rs
@@ -30,4 +30,4 @@ pub use error::LayoutOfDepth;
 pub use error::QueryOverflow;
 pub use values::Value;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_query_system/src/query/config.rs b/compiler/rustc_query_system/src/query/config.rs
index e44a00ca6cb..a0aeb812af9 100644
--- a/compiler/rustc_query_system/src/query/config.rs
+++ b/compiler/rustc_query_system/src/query/config.rs
@@ -43,6 +43,8 @@ pub trait QueryConfig<Qcx: QueryContext>: Copy {
 
     fn try_load_from_disk(self, qcx: Qcx, idx: &Self::Key) -> TryLoadFromDisk<Qcx, Self::Value>;
 
+    fn loadable_from_disk(self, qcx: Qcx, key: &Self::Key, idx: SerializedDepNodeIndex) -> bool;
+
     fn anon(self) -> bool;
     fn eval_always(self) -> bool;
     fn depth_limit(self) -> bool;
diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs
index 005fcd8c4cc..005512cf53e 100644
--- a/compiler/rustc_query_system/src/query/plumbing.rs
+++ b/compiler/rustc_query_system/src/query/plumbing.rs
@@ -2,8 +2,8 @@
 //! generate the actual methods on tcx which find and execute the provider,
 //! manage the caches, and so forth.
 
-use crate::dep_graph::HasDepContext;
 use crate::dep_graph::{DepContext, DepKind, DepNode, DepNodeIndex, DepNodeParams};
+use crate::dep_graph::{DepGraphData, HasDepContext};
 use crate::ich::StableHashingContext;
 use crate::query::caches::QueryCache;
 use crate::query::job::{report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo};
@@ -17,7 +17,7 @@ use rustc_data_structures::profiling::TimingGuard;
 #[cfg(parallel_compiler)]
 use rustc_data_structures::sharded::Sharded;
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_data_structures::sync::Lock;
+use rustc_data_structures::sync::{Lock, LockGuard};
 use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, FatalError};
 use rustc_session::Session;
 use rustc_span::{Span, DUMMY_SP};
@@ -178,16 +178,13 @@ where
     fn try_start<'b, Qcx>(
         qcx: &'b Qcx,
         state: &'b QueryState<K, Qcx::DepKind>,
+        mut state_lock: LockGuard<'b, FxHashMap<K, QueryResult<Qcx::DepKind>>>,
         span: Span,
         key: K,
     ) -> TryGetJob<'b, K, D>
     where
         Qcx: QueryContext + HasDepContext<DepKind = D>,
     {
-        #[cfg(parallel_compiler)]
-        let mut state_lock = state.active.get_shard_by_value(&key).lock();
-        #[cfg(not(parallel_compiler))]
-        let mut state_lock = state.active.lock();
         let lock = &mut *state_lock;
         let current_job_id = qcx.current_query_job();
 
@@ -362,7 +359,25 @@ where
     Qcx: QueryContext,
 {
     let state = query.query_state(qcx);
-    match JobOwner::<'_, Q::Key, Qcx::DepKind>::try_start(&qcx, state, span, key) {
+    #[cfg(parallel_compiler)]
+    let state_lock = state.active.get_shard_by_value(&key).lock();
+    #[cfg(not(parallel_compiler))]
+    let state_lock = state.active.lock();
+
+    // 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
+    // query is not still executing. Without checking the query cache here, we can end up
+    // re-executing the query since `try_start` only checks that the query is not currently
+    // executing, but another thread may have already completed the query and stores it result
+    // in the query cache.
+    if cfg!(parallel_compiler) && qcx.dep_context().sess().threads() > 1 {
+        if let Some((value, index)) = query.query_cache(qcx).lookup(&key) {
+            qcx.dep_context().profiler().query_cache_hit(index.into());
+            return (value, Some(index));
+        }
+    }
+
+    match JobOwner::<'_, Q::Key, Qcx::DepKind>::try_start(&qcx, state, state_lock, span, key) {
         TryGetJob::NotYetStarted(job) => {
             let (result, dep_node_index) = execute_job(query, qcx, key.clone(), dep_node, job.id);
             let cache = query.query_cache(qcx);
@@ -411,32 +426,34 @@ where
     Qcx: QueryContext,
 {
     let dep_graph = qcx.dep_context().dep_graph();
+    let dep_graph_data = match dep_graph.data() {
+        // Fast path for when incr. comp. is off.
+        None => {
+            // Fingerprint the key, just to assert that it doesn't
+            // have anything we don't consider hashable
+            if cfg!(debug_assertions) {
+                let _ = key.to_fingerprint(*qcx.dep_context());
+            }
 
-    // Fast path for when incr. comp. is off.
-    if !dep_graph.is_fully_enabled() {
-        // Fingerprint the key, just to assert that it doesn't
-        // have anything we don't consider hashable
-        if cfg!(debug_assertions) {
-            let _ = key.to_fingerprint(*qcx.dep_context());
-        }
-
-        let prof_timer = qcx.dep_context().profiler().query_provider();
-        let result = qcx.start_query(job_id, query.depth_limit(), None, || query.compute(qcx, key));
-        let dep_node_index = dep_graph.next_virtual_depnode_index();
-        prof_timer.finish_with_query_invocation_id(dep_node_index.into());
+            let prof_timer = qcx.dep_context().profiler().query_provider();
+            let result =
+                qcx.start_query(job_id, query.depth_limit(), None, || query.compute(qcx, key));
+            let dep_node_index = dep_graph.next_virtual_depnode_index();
+            prof_timer.finish_with_query_invocation_id(dep_node_index.into());
+
+            // Similarly, fingerprint the result to assert that
+            // it doesn't have anything not considered hashable.
+            if cfg!(debug_assertions) && let Some(hash_result) = query.hash_result()
+            {
+                qcx.dep_context().with_stable_hashing_context(|mut hcx| {
+                    hash_result(&mut hcx, &result);
+                });
+            }
 
-        // Similarly, fingerprint the result to assert that
-        // it doesn't have anything not considered hashable.
-        if cfg!(debug_assertions)
-            && let Some(hash_result) = query.hash_result()
-        {
-            qcx.dep_context().with_stable_hashing_context(|mut hcx| {
-                hash_result(&mut hcx, &result);
-            });
+            return (result, dep_node_index);
         }
-
-        return (result, dep_node_index);
-    }
+        Some(data) => data,
+    };
 
     if !query.anon() && !query.eval_always() {
         // `to_dep_node` is expensive for some `DepKind`s.
@@ -446,7 +463,7 @@ where
         // The diagnostics for this query will be promoted to the current session during
         // `try_mark_green()`, so we can ignore them here.
         if let Some(ret) = qcx.start_query(job_id, false, None, || {
-            try_load_from_disk_and_cache_in_memory(query, qcx, &key, &dep_node)
+            try_load_from_disk_and_cache_in_memory(query, dep_graph_data, qcx, &key, &dep_node)
         }) {
             return ret;
         }
@@ -458,7 +475,7 @@ where
     let (result, dep_node_index) =
         qcx.start_query(job_id, query.depth_limit(), Some(&diagnostics), || {
             if query.anon() {
-                return dep_graph.with_anon_task(*qcx.dep_context(), query.dep_kind(), || {
+                return dep_graph_data.with_anon_task(*qcx.dep_context(), query.dep_kind(), || {
                     query.compute(qcx, key)
                 });
             }
@@ -467,7 +484,7 @@ where
             let dep_node =
                 dep_node_opt.unwrap_or_else(|| query.construct_dep_node(*qcx.dep_context(), &key));
 
-            dep_graph.with_task(
+            dep_graph_data.with_task(
                 dep_node,
                 (qcx, query),
                 key,
@@ -495,6 +512,7 @@ where
 #[inline(always)]
 fn try_load_from_disk_and_cache_in_memory<Q, Qcx>(
     query: Q,
+    dep_graph_data: &DepGraphData<Qcx::DepKind>,
     qcx: Qcx,
     key: &Q::Key,
     dep_node: &DepNode<Qcx::DepKind>,
@@ -506,10 +524,9 @@ where
     // Note this function can be called concurrently from the same query
     // We must ensure that this is handled correctly.
 
-    let dep_graph = qcx.dep_context().dep_graph();
-    let (prev_dep_node_index, dep_node_index) = dep_graph.try_mark_green(qcx, &dep_node)?;
+    let (prev_dep_node_index, dep_node_index) = dep_graph_data.try_mark_green(qcx, &dep_node)?;
 
-    debug_assert!(dep_graph.is_green(dep_node));
+    debug_assert!(dep_graph_data.is_green(dep_node));
 
     // First we try to load the result from the on-disk cache.
     // Some things are never cached on disk.
@@ -519,8 +536,10 @@ where
         // The call to `with_query_deserialization` enforces that no new `DepNodes`
         // are created during deserialization. See the docs of that method for more
         // details.
-        let result =
-            dep_graph.with_query_deserialization(|| try_load_from_disk(qcx, prev_dep_node_index));
+        let result = qcx
+            .dep_context()
+            .dep_graph()
+            .with_query_deserialization(|| try_load_from_disk(qcx, prev_dep_node_index));
 
         prof_timer.finish_with_query_invocation_id(dep_node_index.into());
 
@@ -528,14 +547,11 @@ where
             if std::intrinsics::unlikely(
                 qcx.dep_context().sess().opts.unstable_opts.query_dep_graph,
             ) {
-                dep_graph.mark_debug_loaded_from_disk(*dep_node)
+                dep_graph_data.mark_debug_loaded_from_disk(*dep_node)
             }
 
-            let prev_fingerprint = qcx
-                .dep_context()
-                .dep_graph()
-                .prev_fingerprint_of(dep_node)
-                .unwrap_or(Fingerprint::ZERO);
+            let prev_fingerprint =
+                dep_graph_data.prev_fingerprint_of(dep_node).unwrap_or(Fingerprint::ZERO);
             // If `-Zincremental-verify-ich` is specified, re-hash results from
             // the cache and make sure that they have the expected fingerprint.
             //
@@ -547,7 +563,13 @@ where
             if std::intrinsics::unlikely(
                 try_verify || qcx.dep_context().sess().opts.unstable_opts.incremental_verify_ich,
             ) {
-                incremental_verify_ich(*qcx.dep_context(), &result, dep_node, query.hash_result());
+                incremental_verify_ich(
+                    *qcx.dep_context(),
+                    dep_graph_data,
+                    &result,
+                    dep_node,
+                    query.hash_result(),
+                );
             }
 
             return Some((result, dep_node_index));
@@ -557,16 +579,23 @@ where
         // can be forced from `DepNode`.
         debug_assert!(
             !qcx.dep_context().fingerprint_style(dep_node.kind).reconstructible(),
-            "missing on-disk cache entry for {dep_node:?}"
+            "missing on-disk cache entry for reconstructible {dep_node:?}"
         );
     }
 
+    // Sanity check for the logic in `ensure`: if the node is green and the result loadable,
+    // we should actually be able to load it.
+    debug_assert!(
+        !query.loadable_from_disk(qcx, &key, prev_dep_node_index),
+        "missing on-disk cache entry for loadable {dep_node:?}"
+    );
+
     // We could not load a result from the on-disk cache, so
     // recompute.
     let prof_timer = qcx.dep_context().profiler().query_provider();
 
     // The dep-graph for this computation is already in-place.
-    let result = dep_graph.with_ignore(|| query.compute(qcx, *key));
+    let result = qcx.dep_context().dep_graph().with_ignore(|| query.compute(qcx, *key));
 
     prof_timer.finish_with_query_invocation_id(dep_node_index.into());
 
@@ -579,15 +608,22 @@ where
     //
     // See issue #82920 for an example of a miscompilation that would get turned into
     // an ICE by this check
-    incremental_verify_ich(*qcx.dep_context(), &result, dep_node, query.hash_result());
+    incremental_verify_ich(
+        *qcx.dep_context(),
+        dep_graph_data,
+        &result,
+        dep_node,
+        query.hash_result(),
+    );
 
     Some((result, dep_node_index))
 }
 
 #[inline]
-#[instrument(skip(tcx, result, hash_result), level = "debug")]
+#[instrument(skip(tcx, dep_graph_data, result, hash_result), level = "debug")]
 pub(crate) fn incremental_verify_ich<Tcx, V: Debug>(
     tcx: Tcx,
+    dep_graph_data: &DepGraphData<Tcx::DepKind>,
     result: &V,
     dep_node: &DepNode<Tcx::DepKind>,
     hash_result: Option<fn(&mut StableHashingContext<'_>, &V) -> Fingerprint>,
@@ -596,7 +632,7 @@ where
     Tcx: DepContext,
 {
     assert!(
-        tcx.dep_graph().is_green(dep_node),
+        dep_graph_data.is_green(dep_node),
         "fingerprint for green query instance not loaded from cache: {dep_node:?}",
     );
 
@@ -604,7 +640,7 @@ where
         tcx.with_stable_hashing_context(|mut hcx| f(&mut hcx, result))
     });
 
-    let old_hash = tcx.dep_graph().prev_fingerprint_of(dep_node);
+    let old_hash = dep_graph_data.prev_fingerprint_of(dep_node);
 
     if Some(new_hash) != old_hash {
         incremental_verify_ich_failed(
@@ -704,6 +740,7 @@ fn ensure_must_run<Q, Qcx>(
     query: Q,
     qcx: Qcx,
     key: &Q::Key,
+    check_cache: bool,
 ) -> (bool, Option<DepNode<Qcx::DepKind>>)
 where
     Q: QueryConfig<Qcx>,
@@ -719,7 +756,7 @@ where
     let dep_node = query.construct_dep_node(*qcx.dep_context(), key);
 
     let dep_graph = qcx.dep_context().dep_graph();
-    match dep_graph.try_mark_green(qcx, &dep_node) {
+    let serialized_dep_node_index = match dep_graph.try_mark_green(qcx, &dep_node) {
         None => {
             // A None return from `try_mark_green` means that this is either
             // a new dep node or that the dep node has already been marked red.
@@ -727,20 +764,28 @@ where
             // DepNodeIndex. We must invoke the query itself. The performance cost
             // this introduces should be negligible as we'll immediately hit the
             // in-memory cache, or another query down the line will.
-            (true, Some(dep_node))
+            return (true, Some(dep_node));
         }
-        Some((_, dep_node_index)) => {
+        Some((serialized_dep_node_index, dep_node_index)) => {
             dep_graph.read_index(dep_node_index);
             qcx.dep_context().profiler().query_cache_hit(dep_node_index.into());
-            (false, None)
+            serialized_dep_node_index
         }
+    };
+
+    // We do not need the value at all, so do not check the cache.
+    if !check_cache {
+        return (false, None);
     }
+
+    let loadable = query.loadable_from_disk(qcx, key, serialized_dep_node_index);
+    (!loadable, Some(dep_node))
 }
 
 #[derive(Debug)]
 pub enum QueryMode {
     Get,
-    Ensure,
+    Ensure { check_cache: bool },
 }
 
 #[inline(always)]
@@ -755,8 +800,8 @@ where
     Q: QueryConfig<Qcx>,
     Qcx: QueryContext,
 {
-    let dep_node = if let QueryMode::Ensure = mode {
-        let (must_run, dep_node) = ensure_must_run(query, qcx, &key);
+    let dep_node = if let QueryMode::Ensure { check_cache } = mode {
+        let (must_run, dep_node) = ensure_must_run(query, qcx, &key, check_cache);
         if !must_run {
             return None;
         }
diff --git a/compiler/rustc_resolve/locales/en-US.ftl b/compiler/rustc_resolve/messages.ftl
index 817bb83ed78..817bb83ed78 100644
--- a/compiler/rustc_resolve/locales/en-US.ftl
+++ b/compiler/rustc_resolve/messages.ftl
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 206c43f6902..6133e75a78f 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -1736,7 +1736,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
 
         let name = path[path.len() - 1].ident.name;
         // Make sure error reporting is deterministic.
-        names.sort_by(|a, b| a.candidate.as_str().partial_cmp(b.candidate.as_str()).unwrap());
+        names.sort_by(|a, b| a.candidate.as_str().cmp(b.candidate.as_str()));
 
         match find_best_match_for_name(
             &names.iter().map(|suggestion| suggestion.candidate).collect::<Vec<Symbol>>(),
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 5eba208e3ed..ce4834decfd 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -27,6 +27,7 @@ use rustc_ast::{self as ast, NodeId, CRATE_NODE_ID};
 use rustc_ast::{AngleBracketedArg, Crate, Expr, ExprKind, GenericArg, GenericArgs, LitKind, Path};
 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_errors::{
     Applicability, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed, SubdiagnosticMessage,
@@ -80,7 +81,7 @@ mod late;
 mod macros;
 pub mod rustdoc;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 enum Weak {
     Yes,
@@ -965,7 +966,7 @@ pub struct Resolver<'a, 'tcx> {
     /// A small map keeping true kinds of built-in macros that appear to be fn-like on
     /// the surface (`macro` items in libcore), but are actually attributes or derives.
     builtin_macro_kinds: FxHashMap<LocalDefId, MacroKind>,
-    registered_tools: RegisteredTools,
+    registered_tools: &'tcx RegisteredTools,
     macro_use_prelude: FxHashMap<Symbol, &'a NameBinding<'a>>,
     macro_map: FxHashMap<DefId, MacroData>,
     dummy_ext_bang: Lrc<SyntaxExtension>,
@@ -1233,7 +1234,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             }
         }
 
-        let registered_tools = macros::registered_tools(tcx.sess, &krate.attrs);
+        let registered_tools = tcx.registered_tools(());
 
         let features = tcx.sess.features_untracked();
 
@@ -1408,7 +1409,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             trait_impls: self.trait_impls,
             proc_macros,
             confused_type_with_std_module,
-            registered_tools: self.registered_tools,
             doc_link_resolutions: self.doc_link_resolutions,
             doc_link_traits_in_scope: self.doc_link_traits_in_scope,
             all_macro_rules: self.all_macro_rules,
@@ -1426,6 +1426,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             trait_map: self.trait_map,
             builtin_macro_kinds: self.builtin_macro_kinds,
             lifetime_elision_allowed: self.lifetime_elision_allowed,
+            lint_buffer: Steal::new(self.lint_buffer),
         };
         ResolverOutputs { global_ctxt, ast_lowering }
     }
@@ -1849,20 +1850,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         &mut self,
         path_str: &str,
         ns: Namespace,
-        mut parent_scope: ParentScope<'a>,
+        parent_scope: ParentScope<'a>,
     ) -> Option<Res> {
         let mut segments =
             Vec::from_iter(path_str.split("::").map(Ident::from_str).map(Segment::from_ident));
         if let Some(segment) = segments.first_mut() {
-            if segment.ident.name == kw::Crate {
-                // FIXME: `resolve_path` always resolves `crate` to the current crate root, but
-                // rustdoc wants it to resolve to the `parent_scope`'s crate root. This trick of
-                // replacing `crate` with `self` and changing the current module should achieve
-                // the same effect.
-                segment.ident.name = kw::SelfLower;
-                parent_scope.module =
-                    self.expect_module(parent_scope.module.def_id().krate.as_def_id());
-            } else if segment.ident.name == kw::Empty {
+            if segment.ident.name == kw::Empty {
                 segment.ident.name = kw::PathRoot;
             }
         }
@@ -2040,3 +2033,7 @@ impl Finalize {
         Finalize { node_id, path_span, root_span, report_private: true }
     }
 }
+
+pub fn provide(providers: &mut ty::query::Providers) {
+    providers.registered_tools = macros::registered_tools;
+}
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index b38c11e8bb8..37153854f7e 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -8,7 +8,6 @@ use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment};
 use rustc_ast::{self as ast, Inline, ItemKind, ModKind, NodeId};
 use rustc_ast_pretty::pprust;
 use rustc_attr::StabilityLevel;
-use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::intern::Interned;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{struct_span_err, Applicability};
@@ -20,11 +19,11 @@ use rustc_hir::def::{self, DefKind, NonMacroAttrKind};
 use rustc_hir::def_id::{CrateNum, LocalDefId};
 use rustc_middle::middle::stability;
 use rustc_middle::ty::RegisteredTools;
+use rustc_middle::ty::TyCtxt;
 use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE};
 use rustc_session::lint::builtin::{UNUSED_MACROS, UNUSED_MACRO_RULES};
 use rustc_session::lint::BuiltinLintDiagnostics;
 use rustc_session::parse::feature_err;
-use rustc_session::Session;
 use rustc_span::edition::Edition;
 use rustc_span::hygiene::{self, ExpnData, ExpnKind, LocalExpnId};
 use rustc_span::hygiene::{AstPass, MacroKind};
@@ -111,15 +110,17 @@ fn fast_print_path(path: &ast::Path) -> Symbol {
     }
 }
 
-pub(crate) fn registered_tools(sess: &Session, attrs: &[ast::Attribute]) -> FxHashSet<Ident> {
-    let mut registered_tools = FxHashSet::default();
-    for attr in sess.filter_by_name(attrs, sym::register_tool) {
+pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools {
+    let mut registered_tools = RegisteredTools::default();
+    let krate = tcx.crate_for_resolver(()).borrow();
+    for attr in tcx.sess.filter_by_name(&krate.attrs, sym::register_tool) {
         for nested_meta in attr.meta_item_list().unwrap_or_default() {
             match nested_meta.ident() {
                 Some(ident) => {
                     if let Some(old_ident) = registered_tools.replace(ident) {
                         let msg = format!("{} `{}` was already registered", "tool", ident);
-                        sess.struct_span_err(ident.span, &msg)
+                        tcx.sess
+                            .struct_span_err(ident.span, &msg)
                             .span_label(old_ident.span, "already registered here")
                             .emit();
                     }
@@ -127,7 +128,10 @@ pub(crate) fn registered_tools(sess: &Session, attrs: &[ast::Attribute]) -> FxHa
                 None => {
                     let msg = format!("`{}` only accepts identifiers", sym::register_tool);
                     let span = nested_meta.span();
-                    sess.struct_span_err(span, &msg).span_label(span, "not an identifier").emit();
+                    tcx.sess
+                        .struct_span_err(span, &msg)
+                        .span_label(span, "not an identifier")
+                        .emit();
                 }
             }
         }
diff --git a/compiler/rustc_session/locales/en-US.ftl b/compiler/rustc_session/messages.ftl
index ff53f22d43f..ff53f22d43f 100644
--- a/compiler/rustc_session/locales/en-US.ftl
+++ b/compiler/rustc_session/messages.ftl
diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs
index 55178250472..0dfee92f404 100644
--- a/compiler/rustc_session/src/code_stats.rs
+++ b/compiler/rustc_session/src/code_stats.rs
@@ -2,7 +2,7 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::sync::Lock;
 use rustc_span::Symbol;
 use rustc_target::abi::{Align, Size};
-use std::cmp::{self, Ordering};
+use std::cmp;
 
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub struct VariantInfo {
@@ -87,7 +87,7 @@ impl CodeStats {
         // Except for Generators, whose variants are already sorted according to
         // their yield points in `variant_info_for_generator`.
         if kind != DataTypeKind::Generator {
-            variants.sort_by(|info1, info2| info2.size.cmp(&info1.size));
+            variants.sort_by_key(|info| cmp::Reverse(info.size));
         }
         let info = TypeSizeInfo {
             kind,
@@ -107,13 +107,7 @@ impl CodeStats {
 
         // Primary sort: large-to-small.
         // Secondary sort: description (dictionary order)
-        sorted.sort_by(|info1, info2| {
-            // (reversing cmp order to get large-to-small ordering)
-            match info2.overall_size.cmp(&info1.overall_size) {
-                Ordering::Equal => info1.type_description.cmp(&info2.type_description),
-                other => other,
-            }
-        });
+        sorted.sort_by_key(|info| (cmp::Reverse(info.overall_size), &info.type_description));
 
         for info in sorted {
             let TypeSizeInfo { type_description, overall_size, align, kind, variants, .. } = info;
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index d4e4ace889b..485c3f55462 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -1137,7 +1137,7 @@ impl CrateCheckConfig {
     }
 
     /// Fills a `CrateCheckConfig` with well-known configuration values.
-    fn fill_well_known_values(&mut self) {
+    fn fill_well_known_values(&mut self, current_target: &Target) {
         if !self.well_known_values {
             return;
         }
@@ -1229,6 +1229,7 @@ impl CrateCheckConfig {
             for target in TARGETS
                 .iter()
                 .map(|target| Target::expect_builtin(&TargetTriple::from_triple(target)))
+                .chain(iter::once(current_target.clone()))
             {
                 values_target_os.insert(Symbol::intern(&target.options.os));
                 values_target_family
@@ -1243,9 +1244,9 @@ impl CrateCheckConfig {
         }
     }
 
-    pub fn fill_well_known(&mut self) {
+    pub fn fill_well_known(&mut self, current_target: &Target) {
         self.fill_well_known_names();
-        self.fill_well_known_values();
+        self.fill_well_known_values(current_target);
     }
 }
 
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index e1f1a5f6d2e..74aef785163 100644
--- a/compiler/rustc_session/src/lib.rs
+++ b/compiler/rustc_session/src/lib.rs
@@ -42,7 +42,7 @@ pub mod output;
 
 pub use getopts;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 /// Requirements for a `StableHashingContext` to be used in this crate.
 /// This is a hack to allow using the `HashStable_Generic` derive macro
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 12634f67185..fdacf814dd6 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -11,7 +11,7 @@ use crate::{filesearch, lint};
 pub use rustc_ast::attr::MarkedAttrs;
 pub use rustc_ast::Attribute;
 use rustc_data_structures::flock;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
 use rustc_data_structures::jobserver::{self, Client};
 use rustc_data_structures::profiling::{duration_to_secs_str, SelfProfiler, SelfProfilerRef};
 use rustc_data_structures::sync::{
@@ -207,10 +207,10 @@ pub struct Session {
     pub asm_arch: Option<InlineAsmArch>,
 
     /// Set of enabled features for the current target.
-    pub target_features: FxHashSet<Symbol>,
+    pub target_features: FxIndexSet<Symbol>,
 
     /// Set of enabled features for the current target, including unstable ones.
-    pub unstable_target_features: FxHashSet<Symbol>,
+    pub unstable_target_features: FxIndexSet<Symbol>,
 }
 
 pub struct PerfStats {
@@ -224,6 +224,13 @@ pub struct PerfStats {
     pub normalize_projection_ty: AtomicUsize,
 }
 
+#[derive(PartialEq, Eq, PartialOrd, Ord)]
+pub enum MetadataKind {
+    None,
+    Uncompressed,
+    Compressed,
+}
+
 impl Session {
     pub fn miri_unleashed_feature(&self, span: Span, feature_gate: Option<Symbol>) {
         self.miri_unleashed_features.lock().push((span, feature_gate));
@@ -287,6 +294,38 @@ impl Session {
         self.crate_types.get().unwrap().as_slice()
     }
 
+    pub fn needs_crate_hash(&self) -> bool {
+        // Why is the crate hash needed for these configurations?
+        // - debug_assertions: for the "fingerprint the result" check in
+        //   `rustc_query_system::query::plumbing::execute_job`.
+        // - incremental: for query lookups.
+        // - needs_metadata: for putting into crate metadata.
+        // - instrument_coverage: for putting into coverage data (see
+        //   `hash_mir_source`).
+        cfg!(debug_assertions)
+            || self.opts.incremental.is_some()
+            || self.needs_metadata()
+            || self.instrument_coverage()
+    }
+
+    pub fn metadata_kind(&self) -> MetadataKind {
+        self.crate_types()
+            .iter()
+            .map(|ty| match *ty {
+                CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => {
+                    MetadataKind::None
+                }
+                CrateType::Rlib => MetadataKind::Uncompressed,
+                CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed,
+            })
+            .max()
+            .unwrap_or(MetadataKind::None)
+    }
+
+    pub fn needs_metadata(&self) -> bool {
+        self.metadata_kind() != MetadataKind::None
+    }
+
     pub fn init_crate_types(&self, crate_types: Vec<CrateType>) {
         self.crate_types.set(crate_types).expect("`crate_types` was initialized twice")
     }
@@ -1488,8 +1527,8 @@ pub fn build_session(
         ctfe_backtrace,
         miri_unleashed_features: Lock::new(Default::default()),
         asm_arch,
-        target_features: FxHashSet::default(),
-        unstable_target_features: FxHashSet::default(),
+        target_features: Default::default(),
+        unstable_target_features: Default::default(),
     };
 
     validate_commandline_args_with_session_available(&sess);
diff --git a/compiler/rustc_smir/Cargo.toml b/compiler/rustc_smir/Cargo.toml
index 5e0d1f369a6..fb97ee5bebe 100644
--- a/compiler/rustc_smir/Cargo.toml
+++ b/compiler/rustc_smir/Cargo.toml
@@ -4,25 +4,12 @@ version = "0.0.0"
 edition = "2021"
 
 [dependencies]
-rustc_borrowck = { path = "../rustc_borrowck", optional = true }
-rustc_driver = { path = "../rustc_driver", optional = true }
-rustc_hir = { path = "../rustc_hir", optional = true }
-rustc_interface = { path = "../rustc_interface", optional = true }
 rustc_middle = { path = "../rustc_middle", optional = true }
-rustc_mir_dataflow = { path = "../rustc_mir_dataflow", optional = true }
-rustc_mir_transform = { path = "../rustc_mir_transform", optional = true }
-rustc_serialize = { path = "../rustc_serialize", optional = true }
-rustc_trait_selection = { path = "../rustc_trait_selection", optional = true }
+rustc_span = { path = "../rustc_span", optional = true }
+tracing = "0.1"
 
 [features]
 default = [
-    "rustc_borrowck",
-    "rustc_driver",
-    "rustc_hir",
-    "rustc_interface",
     "rustc_middle",
-    "rustc_mir_dataflow",
-    "rustc_mir_transform",
-    "rustc_serialize",
-    "rustc_trait_selection",
+    "rustc_span",
 ]
diff --git a/compiler/rustc_smir/README.md b/compiler/rustc_smir/README.md
index ae49098dd0c..31dee955f49 100644
--- a/compiler/rustc_smir/README.md
+++ b/compiler/rustc_smir/README.md
@@ -73,3 +73,40 @@ git subtree pull --prefix=compiler/rustc_smir https://github.com/rust-lang/proje
 Note: only ever sync to rustc from the project-stable-mir's `smir` branch. Do not sync with your own forks.
 
 Then open a PR against rustc just like a regular PR.
+
+## Stable MIR Design
+
+The stable-mir will follow a similar approach to proc-macro2. It’s
+implementation will eventually be broken down into two main crates:
+
+- `stable_mir`: Public crate, to be published on crates.io, which will contain
+the stable data structure as well as proxy APIs to make calls to the
+compiler.
+- `rustc_smir`: The compiler crate that will translate from internal MIR to
+SMIR. This crate will also implement APIs that will be invoked by
+stable-mir to query the compiler for more information.
+
+This will help tools to communicate with the rust compiler via stable APIs. Tools will depend on
+`stable_mir` crate, which will invoke the compiler using APIs defined in `rustc_smir`. I.e.:
+
+```
+    ┌──────────────────────────────────┐           ┌──────────────────────────────────┐
+    │   External Tool     ┌──────────┐ │           │ ┌──────────┐   Rust Compiler     │
+    │                     │          │ │           │ │          │                     │
+    │                     │stable_mir| │           │ │rustc_smir│                     │
+    │                     │          │ ├──────────►| │          │                     │
+    │                     │          │ │◄──────────┤ │          │                     │
+    │                     │          │ │           │ │          │                     │
+    │                     │          │ │           │ │          │                     │
+    │                     └──────────┘ │           │ └──────────┘                     │
+    └──────────────────────────────────┘           └──────────────────────────────────┘
+```
+
+More details can be found here:
+https://hackmd.io/XhnYHKKuR6-LChhobvlT-g?view
+
+For now, the code for these two crates are in separate modules of this crate.
+The modules have the same name for simplicity. We also have a third module,
+`rustc_internal` which will expose APIs and definitions that allow users to
+gather information from internal MIR constructs that haven't been exposed in
+the `stable_mir` module.
diff --git a/compiler/rustc_smir/rust-toolchain.toml b/compiler/rustc_smir/rust-toolchain.toml
index 7b696fc1f5c..157dfd620ee 100644
--- a/compiler/rustc_smir/rust-toolchain.toml
+++ b/compiler/rustc_smir/rust-toolchain.toml
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2022-06-01"
+channel = "nightly-2023-02-28"
 components = [ "rustfmt", "rustc-dev" ]
diff --git a/compiler/rustc_smir/src/lib.rs b/compiler/rustc_smir/src/lib.rs
index 3e93c6bba97..54d474db038 100644
--- a/compiler/rustc_smir/src/lib.rs
+++ b/compiler/rustc_smir/src/lib.rs
@@ -11,9 +11,9 @@
     test(attr(allow(unused_variables), deny(warnings)))
 )]
 #![cfg_attr(not(feature = "default"), feature(rustc_private))]
-#![deny(rustc::untranslatable_diagnostic)]
-#![deny(rustc::diagnostic_outside_of_impl)]
 
-pub mod mir;
+pub mod rustc_internal;
+pub mod stable_mir;
 
-pub mod very_unstable;
+// Make this module private for now since external users should not call these directly.
+mod rustc_smir;
diff --git a/compiler/rustc_smir/src/mir.rs b/compiler/rustc_smir/src/mir.rs
deleted file mode 100644
index 887e6572930..00000000000
--- a/compiler/rustc_smir/src/mir.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-pub use crate::very_unstable::hir::ImplicitSelfKind;
-pub use crate::very_unstable::middle::mir::{
-    visit::MutVisitor, AggregateKind, AssertKind, BasicBlock, BasicBlockData, BinOp, BindingForm,
-    BlockTailInfo, Body, BorrowKind, CastKind, ClearCrossCrate, Constant, ConstantKind,
-    CopyNonOverlapping, Coverage, FakeReadCause, Field, GeneratorInfo, InlineAsmOperand, Local,
-    LocalDecl, LocalInfo, LocalKind, Location, MirPhase, MirSource, NullOp, Operand, Place,
-    PlaceRef, ProjectionElem, ProjectionKind, Promoted, RetagKind, Rvalue, Safety, SourceInfo,
-    SourceScope, SourceScopeData, SourceScopeLocalData, Statement, StatementKind, UnOp,
-    UserTypeProjection, UserTypeProjections, VarBindingForm, VarDebugInfo, VarDebugInfoContents,
-};
diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs
new file mode 100644
index 00000000000..3eaff9c051f
--- /dev/null
+++ b/compiler/rustc_smir/src/rustc_internal/mod.rs
@@ -0,0 +1,15 @@
+//! Module that implements the bridge between Stable MIR and internal compiler MIR.
+//!
+//! For that, we define APIs that will temporarily be public to 3P that exposes rustc internal APIs
+//! until stable MIR is complete.
+
+use crate::stable_mir;
+pub use rustc_span::def_id::{CrateNum, DefId};
+
+pub fn item_def_id(item: &stable_mir::CrateItem) -> DefId {
+    item.0
+}
+
+pub fn crate_num(item: &stable_mir::Crate) -> CrateNum {
+    item.id.into()
+}
diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs
new file mode 100644
index 00000000000..d956f0ac802
--- /dev/null
+++ b/compiler/rustc_smir/src/rustc_smir/mod.rs
@@ -0,0 +1,48 @@
+//! Module that implements what will become the rustc side of Stable MIR.
+//!
+//! This module is responsible for building Stable MIR components from internal components.
+//!
+//! This module is not intended to be invoked directly by users. It will eventually
+//! become the public API of rustc that will be invoked by the `stable_mir` crate.
+//!
+//! For now, we are developing everything inside `rustc`, thus, we keep this module private.
+
+use crate::stable_mir::{self};
+use rustc_middle::ty::{tls::with, TyCtxt};
+use rustc_span::def_id::{CrateNum, LOCAL_CRATE};
+use tracing::debug;
+
+/// Get information about the local crate.
+pub fn local_crate() -> stable_mir::Crate {
+    with(|tcx| smir_crate(tcx, LOCAL_CRATE))
+}
+
+/// Retrieve a list of all external crates.
+pub fn external_crates() -> Vec<stable_mir::Crate> {
+    with(|tcx| tcx.crates(()).iter().map(|crate_num| smir_crate(tcx, *crate_num)).collect())
+}
+
+/// Find a crate with the given name.
+pub fn find_crate(name: &str) -> Option<stable_mir::Crate> {
+    with(|tcx| {
+        [LOCAL_CRATE].iter().chain(tcx.crates(()).iter()).find_map(|crate_num| {
+            let crate_name = tcx.crate_name(*crate_num).to_string();
+            (name == crate_name).then(|| smir_crate(tcx, *crate_num))
+        })
+    })
+}
+
+/// Retrieve all items of the local crate that have a MIR associated with them.
+pub fn all_local_items() -> stable_mir::CrateItems {
+    with(|tcx| {
+        tcx.mir_keys(()).iter().map(|item| stable_mir::CrateItem(item.to_def_id())).collect()
+    })
+}
+
+/// Build a stable mir crate from a given crate number.
+fn smir_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> stable_mir::Crate {
+    let crate_name = tcx.crate_name(crate_num).to_string();
+    let is_local = crate_num == LOCAL_CRATE;
+    debug!(?crate_name, ?crate_num, "smir_crate");
+    stable_mir::Crate { id: crate_num.into(), name: crate_name, is_local }
+}
diff --git a/compiler/rustc_smir/src/stable_mir/mod.rs b/compiler/rustc_smir/src/stable_mir/mod.rs
new file mode 100644
index 00000000000..cbf52e691fb
--- /dev/null
+++ b/compiler/rustc_smir/src/stable_mir/mod.rs
@@ -0,0 +1,60 @@
+//! Module that implements the public interface to the Stable MIR.
+//!
+//! This module shall contain all type definitions and APIs that we expect 3P tools to invoke to
+//! interact with the compiler.
+//!
+//! The goal is to eventually move this module to its own crate which shall be published on
+//! [crates.io](https://crates.io).
+//!
+//! ## Note:
+//!
+//! There shouldn't be any direct references to internal compiler constructs in this module.
+//! If you need an internal construct, consider using `rustc_internal` or `rustc_smir`.
+
+use crate::rustc_internal;
+
+/// Use String for now but we should replace it.
+pub type Symbol = String;
+
+/// The number that identifies a crate.
+pub type CrateNum = usize;
+
+/// A unique identification number for each item accessible for the current compilation unit.
+pub type DefId = usize;
+
+/// A list of crate items.
+pub type CrateItems = Vec<CrateItem>;
+
+/// Holds information about a crate.
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct Crate {
+    pub(crate) id: CrateNum,
+    pub name: Symbol,
+    pub is_local: bool,
+}
+
+/// Holds information about an item in the crate.
+/// For now, it only stores the item DefId. Use functions inside `rustc_internal` module to
+/// use this item.
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct CrateItem(pub(crate) rustc_internal::DefId);
+
+/// Access to the local crate.
+pub fn local_crate() -> Crate {
+    crate::rustc_smir::local_crate()
+}
+
+/// Try to find a crate with the given name.
+pub fn find_crate(name: &str) -> Option<Crate> {
+    crate::rustc_smir::find_crate(name)
+}
+
+/// Try to find a crate with the given name.
+pub fn external_crates() -> Vec<Crate> {
+    crate::rustc_smir::external_crates()
+}
+
+/// Retrieve all items in the local crate that have a MIR associated with them.
+pub fn all_local_items() -> CrateItems {
+    crate::rustc_smir::all_local_items()
+}
diff --git a/compiler/rustc_smir/src/very_unstable.rs b/compiler/rustc_smir/src/very_unstable.rs
deleted file mode 100644
index 12ba133dbb1..00000000000
--- a/compiler/rustc_smir/src/very_unstable.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-//! This module reexports various crates and modules from unstable rustc APIs.
-//! Add anything you need here and it will get slowly transferred to a stable API.
-//! Only use rustc_smir in your dependencies and use the reexports here instead of
-//! directly referring to the unstable crates.
-
-macro_rules! crates {
-    ($($rustc_name:ident -> $name:ident,)*) => {
-        $(
-            #[cfg(not(feature = "default"))]
-            pub extern crate $rustc_name as $name;
-            #[cfg(feature = "default")]
-            pub use $rustc_name as $name;
-        )*
-    }
-}
-
-crates! {
-    rustc_borrowck -> borrowck,
-    rustc_driver -> driver,
-    rustc_hir -> hir,
-    rustc_interface -> interface,
-    rustc_middle -> middle,
-    rustc_mir_dataflow -> dataflow,
-    rustc_mir_transform -> transform,
-    rustc_serialize -> serialize,
-    rustc_trait_selection -> trait_selection,
-}
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index 2e339a9d2d2..a1cb810a429 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -100,6 +100,9 @@ pub trait FileLoader {
 
     /// Read the contents of a UTF-8 file into memory.
     fn read_file(&self, path: &Path) -> io::Result<String>;
+
+    /// Read the contents of a potentially non-UTF-8 file into memory.
+    fn read_binary_file(&self, path: &Path) -> io::Result<Vec<u8>>;
 }
 
 /// A FileLoader that uses std::fs to load real files.
@@ -113,6 +116,10 @@ impl FileLoader for RealFileLoader {
     fn read_file(&self, path: &Path) -> io::Result<String> {
         fs::read_to_string(path)
     }
+
+    fn read_binary_file(&self, path: &Path) -> io::Result<Vec<u8>> {
+        fs::read(path)
+    }
 }
 
 /// This is a [SourceFile] identifier that is used to correlate source files between
@@ -220,9 +227,7 @@ impl SourceMap {
     /// Unlike `load_file`, guarantees that no normalization like BOM-removal
     /// takes place.
     pub fn load_binary_file(&self, path: &Path) -> io::Result<Vec<u8>> {
-        // Ideally, this should use `self.file_loader`, but it can't
-        // deal with binary files yet.
-        let bytes = fs::read(path)?;
+        let bytes = self.file_loader.read_binary_file(path)?;
 
         // We need to add file to the `SourceMap`, so that it is present
         // in dep-info. There's also an edge case that file might be both
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 4e626fd9f30..bf27bd6c5ad 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -429,6 +429,7 @@ symbols! {
         borrowck_graphviz_format,
         borrowck_graphviz_postflow,
         box_free,
+        box_new,
         box_patterns,
         box_syntax,
         bpf_target_feature,
diff --git a/compiler/rustc_symbol_mangling/locales/en-US.ftl b/compiler/rustc_symbol_mangling/messages.ftl
index b7d48280f46..b7d48280f46 100644
--- a/compiler/rustc_symbol_mangling/locales/en-US.ftl
+++ b/compiler/rustc_symbol_mangling/messages.ftl
diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs
index d9ce7373483..c2fd3304f2b 100644
--- a/compiler/rustc_symbol_mangling/src/lib.rs
+++ b/compiler/rustc_symbol_mangling/src/lib.rs
@@ -119,7 +119,7 @@ pub mod errors;
 pub mod test;
 pub mod typeid;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 /// This function computes the symbol name for the given `instance` and the
 /// given instantiating crate. That is, if you know that instance X is
diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs
index 28493c7700f..97132311a5c 100644
--- a/compiler/rustc_target/src/asm/aarch64.rs
+++ b/compiler/rustc_target/src/asm/aarch64.rs
@@ -1,6 +1,6 @@
 use super::{InlineAsmArch, InlineAsmType};
 use crate::spec::{RelocModel, Target};
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::FxIndexSet;
 use rustc_macros::HashStable_Generic;
 use rustc_span::Symbol;
 use std::fmt;
@@ -80,7 +80,7 @@ pub fn target_reserves_x18(target: &Target) -> bool {
 fn reserved_x18(
     _arch: InlineAsmArch,
     _reloc_model: RelocModel,
-    _target_features: &FxHashSet<Symbol>,
+    _target_features: &FxIndexSet<Symbol>,
     target: &Target,
     _is_clobber: bool,
 ) -> Result<(), &'static str> {
diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs
index ec7429a3065..514e30ae020 100644
--- a/compiler/rustc_target/src/asm/arm.rs
+++ b/compiler/rustc_target/src/asm/arm.rs
@@ -1,6 +1,6 @@
 use super::{InlineAsmArch, InlineAsmType};
 use crate::spec::{RelocModel, Target};
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::FxIndexSet;
 use rustc_macros::HashStable_Generic;
 use rustc_span::{sym, Symbol};
 use std::fmt;
@@ -64,14 +64,14 @@ impl ArmInlineAsmRegClass {
 }
 
 // This uses the same logic as useR7AsFramePointer in LLVM
-fn frame_pointer_is_r7(target_features: &FxHashSet<Symbol>, target: &Target) -> bool {
+fn frame_pointer_is_r7(target_features: &FxIndexSet<Symbol>, target: &Target) -> bool {
     target.is_like_osx || (!target.is_like_windows && target_features.contains(&sym::thumb_mode))
 }
 
 fn frame_pointer_r11(
     arch: InlineAsmArch,
     reloc_model: RelocModel,
-    target_features: &FxHashSet<Symbol>,
+    target_features: &FxIndexSet<Symbol>,
     target: &Target,
     is_clobber: bool,
 ) -> Result<(), &'static str> {
@@ -87,7 +87,7 @@ fn frame_pointer_r11(
 fn frame_pointer_r7(
     _arch: InlineAsmArch,
     _reloc_model: RelocModel,
-    target_features: &FxHashSet<Symbol>,
+    target_features: &FxIndexSet<Symbol>,
     target: &Target,
     _is_clobber: bool,
 ) -> Result<(), &'static str> {
@@ -101,7 +101,7 @@ fn frame_pointer_r7(
 fn not_thumb1(
     _arch: InlineAsmArch,
     _reloc_model: RelocModel,
-    target_features: &FxHashSet<Symbol>,
+    target_features: &FxIndexSet<Symbol>,
     _target: &Target,
     is_clobber: bool,
 ) -> Result<(), &'static str> {
@@ -118,7 +118,7 @@ fn not_thumb1(
 fn reserved_r9(
     arch: InlineAsmArch,
     reloc_model: RelocModel,
-    target_features: &FxHashSet<Symbol>,
+    target_features: &FxIndexSet<Symbol>,
     target: &Target,
     is_clobber: bool,
 ) -> Result<(), &'static str> {
diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs
index 70cd883be09..0dbfd426781 100644
--- a/compiler/rustc_target/src/asm/mod.rs
+++ b/compiler/rustc_target/src/asm/mod.rs
@@ -1,6 +1,6 @@
 use crate::spec::Target;
 use crate::{abi::Size, spec::RelocModel};
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
 use rustc_macros::HashStable_Generic;
 use rustc_span::Symbol;
 use std::fmt;
@@ -37,13 +37,14 @@ macro_rules! def_reg_class {
 
         pub(super) fn regclass_map() -> rustc_data_structures::fx::FxHashMap<
             super::InlineAsmRegClass,
-            rustc_data_structures::fx::FxHashSet<super::InlineAsmReg>,
+            rustc_data_structures::fx::FxIndexSet<super::InlineAsmReg>,
         > {
-            use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+            use rustc_data_structures::fx::FxHashMap;
+            use rustc_data_structures::fx::FxIndexSet;
             use super::InlineAsmRegClass;
             let mut map = FxHashMap::default();
             $(
-                map.insert(InlineAsmRegClass::$arch($arch_regclass::$class), FxHashSet::default());
+                map.insert(InlineAsmRegClass::$arch($arch_regclass::$class), FxIndexSet::default());
             )*
             map
         }
@@ -94,7 +95,7 @@ macro_rules! def_regs {
             pub fn validate(self,
                 _arch: super::InlineAsmArch,
                 _reloc_model: crate::spec::RelocModel,
-                _target_features: &rustc_data_structures::fx::FxHashSet<Symbol>,
+                _target_features: &rustc_data_structures::fx::FxIndexSet<Symbol>,
                 _target: &crate::spec::Target,
                 _is_clobber: bool,
             ) -> Result<(), &'static str> {
@@ -118,11 +119,11 @@ macro_rules! def_regs {
         pub(super) fn fill_reg_map(
             _arch: super::InlineAsmArch,
             _reloc_model: crate::spec::RelocModel,
-            _target_features: &rustc_data_structures::fx::FxHashSet<Symbol>,
+            _target_features: &rustc_data_structures::fx::FxIndexSet<Symbol>,
             _target: &crate::spec::Target,
             _map: &mut rustc_data_structures::fx::FxHashMap<
                 super::InlineAsmRegClass,
-                rustc_data_structures::fx::FxHashSet<super::InlineAsmReg>,
+                rustc_data_structures::fx::FxIndexSet<super::InlineAsmReg>,
             >,
         ) {
             #[allow(unused_imports)]
@@ -334,7 +335,7 @@ impl InlineAsmReg {
         self,
         arch: InlineAsmArch,
         reloc_model: RelocModel,
-        target_features: &FxHashSet<Symbol>,
+        target_features: &FxIndexSet<Symbol>,
         target: &Target,
         is_clobber: bool,
     ) -> Result<(), &'static str> {
@@ -701,9 +702,9 @@ impl fmt::Display for InlineAsmType {
 pub fn allocatable_registers(
     arch: InlineAsmArch,
     reloc_model: RelocModel,
-    target_features: &FxHashSet<Symbol>,
+    target_features: &FxIndexSet<Symbol>,
     target: &crate::spec::Target,
-) -> FxHashMap<InlineAsmRegClass, FxHashSet<InlineAsmReg>> {
+) -> FxHashMap<InlineAsmRegClass, FxIndexSet<InlineAsmReg>> {
     match arch {
         InlineAsmArch::X86 | InlineAsmArch::X86_64 => {
             let mut map = x86::regclass_map();
diff --git a/compiler/rustc_target/src/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs
index e41bdc9a58c..dea6d50fe2b 100644
--- a/compiler/rustc_target/src/asm/riscv.rs
+++ b/compiler/rustc_target/src/asm/riscv.rs
@@ -1,6 +1,6 @@
 use super::{InlineAsmArch, InlineAsmType};
 use crate::spec::{RelocModel, Target};
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::FxIndexSet;
 use rustc_macros::HashStable_Generic;
 use rustc_span::{sym, Symbol};
 use std::fmt;
@@ -55,7 +55,7 @@ impl RiscVInlineAsmRegClass {
 fn not_e(
     _arch: InlineAsmArch,
     _reloc_model: RelocModel,
-    target_features: &FxHashSet<Symbol>,
+    target_features: &FxIndexSet<Symbol>,
     _target: &Target,
     _is_clobber: bool,
 ) -> Result<(), &'static str> {
diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs
index 5eae07f141f..3902dac7ff6 100644
--- a/compiler/rustc_target/src/asm/x86.rs
+++ b/compiler/rustc_target/src/asm/x86.rs
@@ -1,6 +1,6 @@
 use super::{InlineAsmArch, InlineAsmType};
 use crate::spec::{RelocModel, Target};
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::FxIndexSet;
 use rustc_macros::HashStable_Generic;
 use rustc_span::Symbol;
 use std::fmt;
@@ -147,7 +147,7 @@ impl X86InlineAsmRegClass {
 fn x86_64_only(
     arch: InlineAsmArch,
     _reloc_model: RelocModel,
-    _target_features: &FxHashSet<Symbol>,
+    _target_features: &FxIndexSet<Symbol>,
     _target: &Target,
     _is_clobber: bool,
 ) -> Result<(), &'static str> {
@@ -161,7 +161,7 @@ fn x86_64_only(
 fn high_byte(
     arch: InlineAsmArch,
     _reloc_model: RelocModel,
-    _target_features: &FxHashSet<Symbol>,
+    _target_features: &FxIndexSet<Symbol>,
     _target: &Target,
     _is_clobber: bool,
 ) -> Result<(), &'static str> {
@@ -174,7 +174,7 @@ fn high_byte(
 fn rbx_reserved(
     arch: InlineAsmArch,
     _reloc_model: RelocModel,
-    _target_features: &FxHashSet<Symbol>,
+    _target_features: &FxIndexSet<Symbol>,
     _target: &Target,
     _is_clobber: bool,
 ) -> Result<(), &'static str> {
@@ -190,7 +190,7 @@ fn rbx_reserved(
 fn esi_reserved(
     arch: InlineAsmArch,
     _reloc_model: RelocModel,
-    _target_features: &FxHashSet<Symbol>,
+    _target_features: &FxIndexSet<Symbol>,
     _target: &Target,
     _is_clobber: bool,
 ) -> Result<(), &'static str> {
diff --git a/compiler/rustc_trait_selection/locales/en-US.ftl b/compiler/rustc_trait_selection/messages.ftl
index 14eb4a5502d..14eb4a5502d 100644
--- a/compiler/rustc_trait_selection/locales/en-US.ftl
+++ b/compiler/rustc_trait_selection/messages.ftl
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index 548b42cef43..f866cb016e2 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -44,4 +44,4 @@ pub mod infer;
 pub mod solve;
 pub mod traits;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs
index dec9f8016b0..72b1b35e79b 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly.rs
@@ -2,11 +2,12 @@
 
 #[cfg(doc)]
 use super::trait_goals::structural_traits::*;
-use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, MaybeCause, QueryResult};
+use super::EvalCtxt;
 use itertools::Itertools;
 use rustc_hir::def_id::DefId;
 use rustc_infer::traits::query::NoSolution;
 use rustc_infer::traits::util::elaborate_predicates;
+use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult};
 use rustc_middle::ty::TypeFoldable;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use std::fmt::Debug;
@@ -247,7 +248,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
     ///
     /// To deal with this, we first try to normalize the self type and add the candidates for the normalized
     /// self type to the list of candidates in case that succeeds. Note that we can't just eagerly return in
-    /// this case as projections as self types add `
+    /// this case as projections as self types add
+    // FIXME complete the unfinished sentence above
     fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>(
         &mut self,
         goal: Goal<'tcx, G>,
diff --git a/compiler/rustc_trait_selection/src/solve/canonical/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonical/canonicalize.rs
index c048d4a2aad..7ee4f332306 100644
--- a/compiler/rustc_trait_selection/src/solve/canonical/canonicalize.rs
+++ b/compiler/rustc_trait_selection/src/solve/canonical/canonicalize.rs
@@ -261,22 +261,33 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
         self.interner().mk_re_late_bound(self.binder_index, br)
     }
 
-    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+    fn fold_ty(&mut self, mut t: Ty<'tcx>) -> Ty<'tcx> {
         let kind = match *t.kind() {
-            ty::Infer(ty::TyVar(vid)) => match self.infcx.probe_ty_var(vid) {
-                Ok(t) => return self.fold_ty(t),
-                Err(ui) => CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)),
-            },
-            ty::Infer(ty::IntVar(_)) => {
-                let nt = self.infcx.shallow_resolve(t);
+            ty::Infer(ty::TyVar(mut vid)) => {
+                // We need to canonicalize the *root* of our ty var.
+                // This is so that our canonical response correctly reflects
+                // any equated inference vars correctly!
+                let root_vid = self.infcx.root_var(vid);
+                if root_vid != vid {
+                    t = self.infcx.tcx.mk_ty_var(root_vid);
+                    vid = root_vid;
+                }
+
+                match self.infcx.probe_ty_var(vid) {
+                    Ok(t) => return self.fold_ty(t),
+                    Err(ui) => CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)),
+                }
+            }
+            ty::Infer(ty::IntVar(vid)) => {
+                let nt = self.infcx.opportunistic_resolve_int_var(vid);
                 if nt != t {
                     return self.fold_ty(nt);
                 } else {
                     CanonicalVarKind::Ty(CanonicalTyVarKind::Int)
                 }
             }
-            ty::Infer(ty::FloatVar(_)) => {
-                let nt = self.infcx.shallow_resolve(t);
+            ty::Infer(ty::FloatVar(vid)) => {
+                let nt = self.infcx.opportunistic_resolve_float_var(vid);
                 if nt != t {
                     return self.fold_ty(nt);
                 } else {
@@ -338,13 +349,23 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
         self.interner().mk_bound(self.binder_index, bt)
     }
 
-    fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
+    fn fold_const(&mut self, mut c: ty::Const<'tcx>) -> ty::Const<'tcx> {
         let kind = match c.kind() {
-            ty::ConstKind::Infer(ty::InferConst::Var(vid)) => match self.infcx.probe_const_var(vid)
-            {
-                Ok(c) => return self.fold_const(c),
-                Err(universe) => CanonicalVarKind::Const(universe, c.ty()),
-            },
+            ty::ConstKind::Infer(ty::InferConst::Var(mut vid)) => {
+                // We need to canonicalize the *root* of our const var.
+                // This is so that our canonical response correctly reflects
+                // any equated inference vars correctly!
+                let root_vid = self.infcx.root_const_var(vid);
+                if root_vid != vid {
+                    c = self.infcx.tcx.mk_const(ty::InferConst::Var(root_vid), c.ty());
+                    vid = root_vid;
+                }
+
+                match self.infcx.probe_const_var(vid) {
+                    Ok(c) => return self.fold_const(c),
+                    Err(universe) => CanonicalVarKind::Const(universe, c.ty()),
+                }
+            }
             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 95612674eb9..ca438a103cf 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
@@ -93,37 +93,42 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         };
 
         // Guard against `<T as Trait<?0>>::Assoc = ?0>`.
-        struct ContainsTerm<'tcx> {
+        struct ContainsTerm<'a, 'tcx> {
             term: ty::Term<'tcx>,
+            infcx: &'a InferCtxt<'tcx>,
         }
-        impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsTerm<'tcx> {
+        impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsTerm<'_, 'tcx> {
             type BreakTy = ();
             fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-                if t.needs_infer() {
-                    if ty::Term::from(t) == self.term {
-                        ControlFlow::Break(())
-                    } else {
-                        t.super_visit_with(self)
-                    }
+                if let Some(vid) = t.ty_vid()
+                    && let ty::TermKind::Ty(term) = self.term.unpack()
+                    && let Some(term_vid) = term.ty_vid()
+                    && self.infcx.root_var(vid) == self.infcx.root_var(term_vid)
+                {
+                    ControlFlow::Break(())
+                } else if t.has_non_region_infer() {
+                    t.super_visit_with(self)
                 } else {
                     ControlFlow::Continue(())
                 }
             }
 
             fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
-                if c.needs_infer() {
-                    if ty::Term::from(c) == self.term {
-                        ControlFlow::Break(())
-                    } else {
-                        c.super_visit_with(self)
-                    }
+                if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = c.kind()
+                    && let ty::TermKind::Const(term) = self.term.unpack()
+                    && let ty::ConstKind::Infer(ty::InferConst::Var(term_vid)) = term.kind()
+                    && self.infcx.root_const_var(vid) == self.infcx.root_const_var(term_vid)
+                {
+                    ControlFlow::Break(())
+                } else if c.has_non_region_infer() {
+                    c.super_visit_with(self)
                 } else {
                     ControlFlow::Continue(())
                 }
             }
         }
 
-        let mut visitor = ContainsTerm { term: goal.predicate.term };
+        let mut visitor = ContainsTerm { infcx: self.infcx, term: goal.predicate.term };
 
         term_is_infer
             && goal.predicate.projection_ty.visit_with(&mut visitor).is_continue()
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index 57b6a452737..55d361b1204 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -21,11 +21,13 @@ use rustc_hir::def_id::DefId;
 use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues};
 use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt};
 use rustc_infer::traits::query::NoSolution;
-use rustc_infer::traits::Obligation;
-use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData};
+use rustc_middle::traits::solve::{
+    CanonicalGoal, CanonicalResponse, Certainty, ExternalConstraints, ExternalConstraintsData,
+    Goal, MaybeCause, QueryResult, Response,
+};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::ty::{
-    CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, ToPredicate, TypeOutlivesPredicate,
+    CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate,
 };
 use rustc_span::DUMMY_SP;
 
@@ -43,45 +45,6 @@ mod trait_goals;
 pub use eval_ctxt::EvalCtxt;
 pub use fulfill::FulfillmentCtxt;
 
-/// A goal is a statement, i.e. `predicate`, we want to prove
-/// given some assumptions, i.e. `param_env`.
-///
-/// Most of the time the `param_env` contains the `where`-bounds of the function
-/// we're currently typechecking while the `predicate` is some trait bound.
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
-pub struct Goal<'tcx, P> {
-    param_env: ty::ParamEnv<'tcx>,
-    predicate: P,
-}
-
-impl<'tcx, P> Goal<'tcx, P> {
-    pub fn new(
-        tcx: TyCtxt<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        predicate: impl ToPredicate<'tcx, P>,
-    ) -> Goal<'tcx, P> {
-        Goal { param_env, predicate: predicate.to_predicate(tcx) }
-    }
-
-    /// Updates the goal to one with a different `predicate` but the same `param_env`.
-    fn with<Q>(self, tcx: TyCtxt<'tcx>, predicate: impl ToPredicate<'tcx, Q>) -> Goal<'tcx, Q> {
-        Goal { param_env: self.param_env, predicate: predicate.to_predicate(tcx) }
-    }
-}
-
-impl<'tcx, P> From<Obligation<'tcx, P>> for Goal<'tcx, P> {
-    fn from(obligation: Obligation<'tcx, P>) -> Goal<'tcx, P> {
-        Goal { param_env: obligation.param_env, predicate: obligation.predicate }
-    }
-}
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
-pub struct Response<'tcx> {
-    pub var_values: CanonicalVarValues<'tcx>,
-    /// Additional constraints returned by this query.
-    pub external_constraints: ExternalConstraints<'tcx>,
-    pub certainty: Certainty,
-}
-
 trait CanonicalResponseExt {
     fn has_no_inference_or_external_constraints(&self) -> bool;
 }
@@ -94,56 +57,6 @@ impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> {
     }
 }
 
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
-pub enum Certainty {
-    Yes,
-    Maybe(MaybeCause),
-}
-
-impl Certainty {
-    pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity);
-
-    /// When proving multiple goals using **AND**, e.g. nested obligations for an impl,
-    /// use this function to unify the certainty of these goals
-    pub fn unify_and(self, other: Certainty) -> Certainty {
-        match (self, other) {
-            (Certainty::Yes, Certainty::Yes) => Certainty::Yes,
-            (Certainty::Yes, Certainty::Maybe(_)) => other,
-            (Certainty::Maybe(_), Certainty::Yes) => self,
-            (Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Overflow)) => {
-                Certainty::Maybe(MaybeCause::Overflow)
-            }
-            // If at least one of the goals is ambiguous, hide the overflow as the ambiguous goal
-            // may still result in failure.
-            (Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(_))
-            | (Certainty::Maybe(_), Certainty::Maybe(MaybeCause::Ambiguity)) => {
-                Certainty::Maybe(MaybeCause::Ambiguity)
-            }
-        }
-    }
-}
-
-/// Why we failed to evaluate a goal.
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
-pub enum MaybeCause {
-    /// We failed due to ambiguity. This ambiguity can either
-    /// be a true ambiguity, i.e. there are multiple different answers,
-    /// or we hit a case where we just don't bother, e.g. `?x: Trait` goals.
-    Ambiguity,
-    /// We gave up due to an overflow, most often by hitting the recursion limit.
-    Overflow,
-}
-
-type CanonicalGoal<'tcx, T = ty::Predicate<'tcx>> = Canonical<'tcx, Goal<'tcx, T>>;
-type CanonicalResponse<'tcx> = Canonical<'tcx, Response<'tcx>>;
-/// The result of evaluating a canonical query.
-///
-/// FIXME: We use a different type than the existing canonical queries. This is because
-/// we need to add a `Certainty` for `overflow` and may want to restructure this code without
-/// having to worry about changes to currently used code. Once we've made progress on this
-/// solver, merge the two responses again.
-pub type QueryResult<'tcx> = Result<CanonicalResponse<'tcx>, NoSolution>;
-
 pub trait InferCtxtEvalExt<'tcx> {
     /// Evaluates a goal from **outside** of the trait solver.
     ///
@@ -238,6 +151,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
             && has_changed
             && !self.in_projection_eq_hack
             && !self.search_graph.in_cycle()
+            && false
         {
             let (_orig_values, canonical_goal) = self.canonicalize_goal(goal);
             let canonical_response =
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs
index 33c66d072e9..e206658b4b9 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs
@@ -2,7 +2,7 @@ use crate::traits::{specialization_graph, translate_substs};
 
 use super::assembly;
 use super::trait_goals::structural_traits;
-use super::{Certainty, EvalCtxt, Goal, QueryResult};
+use super::EvalCtxt;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
@@ -11,6 +11,7 @@ use rustc_infer::infer::InferCtxt;
 use rustc_infer::traits::query::NoSolution;
 use rustc_infer::traits::specialization_graph::LeafDef;
 use rustc_infer::traits::Reveal;
+use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_middle::ty::ProjectionPredicate;
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -512,7 +513,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
     fn consider_builtin_dyn_upcast_candidates(
         _ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
-    ) -> Vec<super::CanonicalResponse<'tcx>> {
+    ) -> Vec<CanonicalResponse<'tcx>> {
         bug!("`Unsize` does not have an associated type: {:?}", goal);
     }
 
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs b/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs
index 86b13c05f76..d1b4fa554c5 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs
@@ -8,12 +8,10 @@
 //!
 //! FIXME(@lcnr): Write that section, feel free to ping me if you need help here
 //! before then or if I still haven't done that before January 2023.
-use super::overflow::OverflowData;
 use super::StackDepth;
-use crate::solve::{CanonicalGoal, QueryResult};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_index::vec::IndexVec;
-use rustc_middle::ty::TyCtxt;
+use rustc_middle::traits::solve::{CanonicalGoal, QueryResult};
 
 rustc_index::newtype_index! {
     pub struct EntryIndex {}
@@ -98,26 +96,3 @@ impl<'tcx> ProvisionalCache<'tcx> {
         self.entries[entry_index].response
     }
 }
-
-pub(super) fn try_move_finished_goal_to_global_cache<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    overflow_data: &mut OverflowData,
-    stack: &IndexVec<super::StackDepth, super::StackElem<'tcx>>,
-    goal: CanonicalGoal<'tcx>,
-    response: QueryResult<'tcx>,
-) {
-    // We move goals to the global cache if we either did not hit an overflow or if it's
-    // the root goal as that will now always hit the same overflow limit.
-    //
-    // NOTE: We cannot move any non-root goals to the global cache even if their final result
-    // isn't impacted by the overflow as that goal still has unstable query dependencies
-    // because it didn't go its full depth.
-    //
-    // FIXME(@lcnr): We could still cache subtrees which are not impacted by overflow though.
-    // Tracking that info correctly isn't trivial, so I haven't implemented it for now.
-    let should_cache_globally = !overflow_data.did_overflow() || stack.is_empty();
-    if should_cache_globally {
-        // FIXME: move the provisional entry to the global cache.
-        let _ = (tcx, goal, response);
-    }
-}
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 c7eb8de6521..f1b840aac55 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
@@ -2,11 +2,12 @@ mod cache;
 mod overflow;
 
 use self::cache::ProvisionalEntry;
-use super::{CanonicalGoal, Certainty, MaybeCause, QueryResult};
 pub(super) use crate::solve::search_graph::overflow::OverflowHandler;
 use cache::ProvisionalCache;
 use overflow::OverflowData;
 use rustc_index::vec::IndexVec;
+use rustc_middle::dep_graph::DepKind;
+use rustc_middle::traits::solve::{CanonicalGoal, Certainty, MaybeCause, QueryResult};
 use rustc_middle::ty::TyCtxt;
 use std::{collections::hash_map::Entry, mem};
 
@@ -139,10 +140,9 @@ impl<'tcx> SearchGraph<'tcx> {
     /// updated the provisional cache and we have to recompute the current goal.
     ///
     /// FIXME: Refer to the rustc-dev-guide entry once it exists.
-    #[instrument(level = "debug", skip(self, tcx, actual_goal), ret)]
+    #[instrument(level = "debug", skip(self, actual_goal), ret)]
     fn try_finalize_goal(
         &mut self,
-        tcx: TyCtxt<'tcx>,
         actual_goal: CanonicalGoal<'tcx>,
         response: QueryResult<'tcx>,
     ) -> bool {
@@ -176,72 +176,87 @@ impl<'tcx> SearchGraph<'tcx> {
             self.stack.push(StackElem { goal, has_been_used: false });
             false
         } else {
-            self.try_move_finished_goal_to_global_cache(tcx, stack_elem);
             true
         }
     }
 
-    fn try_move_finished_goal_to_global_cache(
+    pub(super) fn with_new_goal(
         &mut self,
         tcx: TyCtxt<'tcx>,
-        stack_elem: StackElem<'tcx>,
-    ) {
-        let StackElem { goal, .. } = stack_elem;
+        canonical_goal: CanonicalGoal<'tcx>,
+        mut loop_body: impl FnMut(&mut Self) -> QueryResult<'tcx>,
+    ) -> QueryResult<'tcx> {
+        if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_goal, tcx) {
+            return result;
+        }
+
+        match self.try_push_stack(tcx, canonical_goal) {
+            Ok(()) => {}
+            // Our goal is already on the stack, eager return.
+            Err(response) => return response,
+        }
+
+        // This is for global caching, so we properly track query dependencies.
+        // Everything that affects the `Result` should be performed within this
+        // `with_anon_task` closure.
+        let (result, dep_node) = tcx.dep_graph.with_anon_task(tcx, DepKind::TraitSelect, || {
+            self.repeat_while_none(
+                |this| {
+                    let result = this.deal_with_overflow(tcx, canonical_goal);
+                    let _ = this.stack.pop().unwrap();
+                    result
+                },
+                |this| {
+                    let result = loop_body(this);
+                    this.try_finalize_goal(canonical_goal, result).then(|| result)
+                },
+            )
+        });
+
         let cache = &mut self.provisional_cache;
-        let provisional_entry_index = *cache.lookup_table.get(&goal).unwrap();
+        let provisional_entry_index = *cache.lookup_table.get(&canonical_goal).unwrap();
         let provisional_entry = &mut cache.entries[provisional_entry_index];
         let depth = provisional_entry.depth;
 
         // If not, we're done with this goal.
         //
         // Check whether that this goal doesn't depend on a goal deeper on the stack
-        // and if so, move it and all nested goals to the global cache.
+        // and if so, move it to the global cache.
         //
         // Note that if any nested goal were to depend on something deeper on the stack,
         // this would have also updated the depth of the current goal.
         if depth == self.stack.next_index() {
-            for (i, entry) in cache.entries.drain_enumerated(provisional_entry_index.index()..) {
+            // If the current goal is the head of a cycle, we drop all other
+            // cycle participants without moving them to the global cache.
+            let other_cycle_participants = provisional_entry_index.index() + 1;
+            for (i, entry) in cache.entries.drain_enumerated(other_cycle_participants..) {
                 let actual_index = cache.lookup_table.remove(&entry.goal);
                 debug_assert_eq!(Some(i), actual_index);
                 debug_assert!(entry.depth == depth);
-                cache::try_move_finished_goal_to_global_cache(
-                    tcx,
-                    &mut self.overflow_data,
-                    &self.stack,
-                    entry.goal,
-                    entry.response,
-                );
             }
-        }
-    }
 
-    pub(super) fn with_new_goal(
-        &mut self,
-        tcx: TyCtxt<'tcx>,
-        canonical_goal: CanonicalGoal<'tcx>,
-        mut loop_body: impl FnMut(&mut Self) -> QueryResult<'tcx>,
-    ) -> QueryResult<'tcx> {
-        match self.try_push_stack(tcx, canonical_goal) {
-            Ok(()) => {}
-            // Our goal is already on the stack, eager return.
-            Err(response) => return response,
+            let current_goal = cache.entries.pop().unwrap();
+            let actual_index = cache.lookup_table.remove(&current_goal.goal);
+            debug_assert_eq!(Some(provisional_entry_index), actual_index);
+            debug_assert!(current_goal.depth == depth);
+
+            // We move the root goal to the global cache if we either did not hit an overflow or if it's
+            // the root goal as that will now always hit the same overflow limit.
+            //
+            // NOTE: We cannot move any non-root goals to the global cache. When replaying the root goal's
+            // dependencies, our non-root goal may no longer appear as child of the root goal.
+            //
+            // See https://github.com/rust-lang/rust/pull/108071 for some additional context.
+            let should_cache_globally = !self.overflow_data.did_overflow() || self.stack.is_empty();
+            if should_cache_globally {
+                tcx.new_solver_evaluation_cache.insert(
+                    current_goal.goal,
+                    dep_node,
+                    current_goal.response,
+                );
+            }
         }
 
-        self.repeat_while_none(
-            |this| {
-                let result = this.deal_with_overflow(tcx, canonical_goal);
-                let stack_elem = this.stack.pop().unwrap();
-                this.try_move_finished_goal_to_global_cache(tcx, stack_elem);
-                result
-            },
-            |this| {
-                let result = loop_body(this);
-                if this.try_finalize_goal(tcx, canonical_goal, result) {
-                    Some(result)
-                } else {
-                    None
-                }
-            },
-        )
+        result
     }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs
index 56409b0602b..7c9e63f529b 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs
@@ -1,10 +1,11 @@
 use rustc_infer::infer::canonical::Canonical;
 use rustc_infer::traits::query::NoSolution;
+use rustc_middle::traits::solve::{Certainty, MaybeCause, QueryResult};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Limit;
 
 use super::SearchGraph;
-use crate::solve::{response_no_constraints, Certainty, EvalCtxt, MaybeCause, QueryResult};
+use crate::solve::{response_no_constraints, EvalCtxt};
 
 /// When detecting a solver overflow, we return ambiguity. Overflow can be
 /// *hidden* by either a fatal error in an **AND** or a trivial success in an **OR**.
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index 5c499c36e9b..0669975d638 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -2,12 +2,12 @@
 
 use std::iter;
 
-use super::assembly;
-use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult};
+use super::{assembly, EvalCtxt};
 use rustc_hir::def_id::DefId;
 use rustc_hir::LangItem;
 use rustc_infer::traits::query::NoSolution;
 use rustc_infer::traits::util::supertraits;
+use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
 use rustc_middle::ty::{TraitPredicate, TypeVisitableExt};
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 a844a1494e2..704b0d0bd1c 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -24,11 +24,9 @@ use rustc_errors::{
 };
 use rustc_hir as hir;
 use rustc_hir::def::Namespace;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::Visitor;
-use rustc_hir::GenericParam;
-use rustc_hir::Item;
-use rustc_hir::Node;
+use rustc_hir::{GenericParam, Item, Node};
 use rustc_infer::infer::error_reporting::TypeErrCtxt;
 use rustc_infer::infer::{InferOk, TypeTrace};
 use rustc_middle::traits::select::OverflowError;
@@ -126,11 +124,7 @@ pub trait TypeErrCtxtExt<'tcx> {
             + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
         <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug;
 
-    fn report_fulfillment_errors(
-        &self,
-        errors: &[FulfillmentError<'tcx>],
-        body_id: Option<hir::BodyId>,
-    ) -> ErrorGuaranteed;
+    fn report_fulfillment_errors(&self, errors: &[FulfillmentError<'tcx>]) -> ErrorGuaranteed;
 
     fn report_overflow_obligation<T>(
         &self,
@@ -388,11 +382,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
 }
 
 impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
-    fn report_fulfillment_errors(
-        &self,
-        errors: &[FulfillmentError<'tcx>],
-        body_id: Option<hir::BodyId>,
-    ) -> ErrorGuaranteed {
+    fn report_fulfillment_errors(&self, errors: &[FulfillmentError<'tcx>]) -> ErrorGuaranteed {
         #[derive(Debug)]
         struct ErrorDescriptor<'tcx> {
             predicate: ty::Predicate<'tcx>,
@@ -469,7 +459,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         for from_expansion in [false, true] {
             for (error, suppressed) in iter::zip(errors, &is_suppressed) {
                 if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion {
-                    self.report_fulfillment_error(error, body_id);
+                    self.report_fulfillment_error(error);
                 }
             }
         }
@@ -955,8 +945,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                             );
                         }
 
-                        let body_hir_id =
-                            self.tcx.hir().local_def_id_to_hir_id(obligation.cause.body_id);
+                        let body_def_id = obligation.cause.body_id;
                         // Try to report a help message
                         if is_fn_trait
                             && let Ok((implemented_kind, params)) = self.type_implements_fn_trait(
@@ -1035,9 +1024,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                             // Can't show anything else useful, try to find similar impls.
                             let impl_candidates = self.find_similar_impl_candidates(trait_predicate);
                             if !self.report_similar_impl_candidates(
-                                impl_candidates,
+                                &impl_candidates,
                                 trait_ref,
-                                body_hir_id,
+                                body_def_id,
                                 &mut err,
                                 true,
                             ) {
@@ -1071,14 +1060,21 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                                     let impl_candidates =
                                         self.find_similar_impl_candidates(trait_pred);
                                     self.report_similar_impl_candidates(
-                                        impl_candidates,
+                                        &impl_candidates,
                                         trait_ref,
-                                        body_hir_id,
+                                        body_def_id,
                                         &mut err,
                                         true,
                                     );
                                 }
                             }
+
+                            self.maybe_suggest_convert_to_slice(
+                                &mut err,
+                                trait_ref,
+                                impl_candidates.as_slice(),
+                                span,
+                            );
                         }
 
                         // Changing mutability doesn't make a difference to whether we have
@@ -1494,11 +1490,7 @@ trait InferCtxtPrivExt<'tcx> {
     // `error` occurring implies that `cond` occurs.
     fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool;
 
-    fn report_fulfillment_error(
-        &self,
-        error: &FulfillmentError<'tcx>,
-        body_id: Option<hir::BodyId>,
-    );
+    fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>);
 
     fn report_projection_error(
         &self,
@@ -1529,9 +1521,9 @@ trait InferCtxtPrivExt<'tcx> {
 
     fn report_similar_impl_candidates(
         &self,
-        impl_candidates: Vec<ImplCandidate<'tcx>>,
+        impl_candidates: &[ImplCandidate<'tcx>],
         trait_ref: ty::PolyTraitRef<'tcx>,
-        body_id: hir::HirId,
+        body_def_id: LocalDefId,
         err: &mut Diagnostic,
         other: bool,
     ) -> bool;
@@ -1561,11 +1553,7 @@ trait InferCtxtPrivExt<'tcx> {
         trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>,
     ) -> PredicateObligation<'tcx>;
 
-    fn maybe_report_ambiguity(
-        &self,
-        obligation: &PredicateObligation<'tcx>,
-        body_id: Option<hir::BodyId>,
-    );
+    fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>);
 
     fn predicate_can_apply(
         &self,
@@ -1647,11 +1635,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
     }
 
     #[instrument(skip(self), level = "debug")]
-    fn report_fulfillment_error(
-        &self,
-        error: &FulfillmentError<'tcx>,
-        body_id: Option<hir::BodyId>,
-    ) {
+    fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) {
         match error.code {
             FulfillmentErrorCode::CodeSelectionError(ref selection_error) => {
                 self.report_selection_error(
@@ -1664,7 +1648,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 self.report_projection_error(&error.obligation, e);
             }
             FulfillmentErrorCode::CodeAmbiguity => {
-                self.maybe_report_ambiguity(&error.obligation, body_id);
+                self.maybe_report_ambiguity(&error.obligation);
             }
             FulfillmentErrorCode::CodeSubtypeError(ref expected_found, ref err) => {
                 self.report_mismatched_types(
@@ -2027,9 +2011,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
 
     fn report_similar_impl_candidates(
         &self,
-        impl_candidates: Vec<ImplCandidate<'tcx>>,
+        impl_candidates: &[ImplCandidate<'tcx>],
         trait_ref: ty::PolyTraitRef<'tcx>,
-        body_id: hir::HirId,
+        body_def_id: LocalDefId,
         err: &mut Diagnostic,
         other: bool,
     ) -> bool {
@@ -2120,9 +2104,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                         // FIXME(compiler-errors): This could be generalized, both to
                         // be more granular, and probably look past other `#[fundamental]`
                         // types, too.
-                        self.tcx
-                            .visibility(def.did())
-                            .is_accessible_from(body_id.owner.def_id, self.tcx)
+                        self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx)
                     } else {
                         true
                     }
@@ -2138,7 +2120,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         // Prefer more similar candidates first, then sort lexicographically
         // by their normalized string representation.
         let mut normalized_impl_candidates_and_similarities = impl_candidates
-            .into_iter()
+            .iter()
+            .copied()
             .map(|ImplCandidate { trait_ref, similarity }| {
                 // FIXME(compiler-errors): This should be using `NormalizeExt::normalize`
                 let normalized = self
@@ -2231,11 +2214,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
     }
 
     #[instrument(skip(self), level = "debug")]
-    fn maybe_report_ambiguity(
-        &self,
-        obligation: &PredicateObligation<'tcx>,
-        body_id: Option<hir::BodyId>,
-    ) {
+    fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) {
         // Unable to successfully determine, probably means
         // insufficient type information, but could mean
         // ambiguous impls. The latter *ought* to be a
@@ -2277,7 +2256,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 if self.tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) {
                     if let None = self.tainted_by_errors() {
                         self.emit_inference_failure_err(
-                            body_id,
+                            obligation.cause.body_id,
                             span,
                             trait_ref.self_ty().skip_binder().into(),
                             ErrorCode::E0282,
@@ -2304,7 +2283,13 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 let subst = data.trait_ref.substs.iter().find(|s| s.has_non_region_infer());
 
                 let mut err = if let Some(subst) = subst {
-                    self.emit_inference_failure_err(body_id, span, subst, ErrorCode::E0283, true)
+                    self.emit_inference_failure_err(
+                        obligation.cause.body_id,
+                        span,
+                        subst,
+                        ErrorCode::E0283,
+                        true,
+                    )
                 } else {
                     struct_span_err!(
                         self.tcx.sess,
@@ -2348,12 +2333,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                                 predicate.to_opt_poly_trait_pred().unwrap(),
                             );
                             if impl_candidates.len() < 10 {
-                                let hir =
-                                    self.tcx.hir().local_def_id_to_hir_id(obligation.cause.body_id);
                                 self.report_similar_impl_candidates(
-                                    impl_candidates,
+                                    impl_candidates.as_slice(),
                                     trait_ref,
-                                    body_id.map(|id| id.hir_id).unwrap_or(hir),
+                                    obligation.cause.body_id,
                                     &mut err,
                                     false,
                                 );
@@ -2375,9 +2358,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                     self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id());
                 }
 
-                if let (Some(body_id), Some(ty::subst::GenericArgKind::Type(_))) =
-                    (body_id, subst.map(|subst| subst.unpack()))
+                if let Some(ty::subst::GenericArgKind::Type(_)) = subst.map(|subst| subst.unpack())
                 {
+                    let body_id = self.tcx.hir().body_owned_by(obligation.cause.body_id);
                     let mut expr_finder = FindExprBySpan::new(span);
                     expr_finder.visit_expr(&self.tcx.hir().body(body_id).value);
 
@@ -2473,7 +2456,13 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                     return;
                 }
 
-                self.emit_inference_failure_err(body_id, span, arg, ErrorCode::E0282, false)
+                self.emit_inference_failure_err(
+                    obligation.cause.body_id,
+                    span,
+                    arg,
+                    ErrorCode::E0282,
+                    false,
+                )
             }
 
             ty::PredicateKind::Subtype(data) => {
@@ -2487,7 +2476,13 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 let SubtypePredicate { a_is_expected: _, a, b } = data;
                 // both must be type variables, or the other would've been instantiated
                 assert!(a.is_ty_var() && b.is_ty_var());
-                self.emit_inference_failure_err(body_id, span, a.into(), ErrorCode::E0282, true)
+                self.emit_inference_failure_err(
+                    obligation.cause.body_id,
+                    span,
+                    a.into(),
+                    ErrorCode::E0282,
+                    true,
+                )
             }
             ty::PredicateKind::Clause(ty::Clause::Projection(data)) => {
                 if predicate.references_error() || self.tainted_by_errors().is_some() {
@@ -2501,7 +2496,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                     .find(|g| g.has_non_region_infer());
                 if let Some(subst) = subst {
                     let mut err = self.emit_inference_failure_err(
-                        body_id,
+                        obligation.cause.body_id,
                         span,
                         subst,
                         ErrorCode::E0284,
@@ -2530,7 +2525,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 let subst = data.walk().find(|g| g.is_non_region_infer());
                 if let Some(subst) = subst {
                     let err = self.emit_inference_failure_err(
-                        body_id,
+                        obligation.cause.body_id,
                         span,
                         subst,
                         ErrorCode::E0284,
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 9ab753c5a48..11567ff39dd 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -1,7 +1,7 @@
 // ignore-tidy-filelength
 
 use super::{
-    DefIdOrName, FindExprBySpan, Obligation, ObligationCause, ObligationCauseCode,
+    DefIdOrName, FindExprBySpan, ImplCandidate, Obligation, ObligationCause, ObligationCauseCode,
     PredicateObligation,
 };
 
@@ -382,6 +382,14 @@ pub trait TypeErrCtxtExt<'tcx> {
         body_id: hir::HirId,
         param_env: ty::ParamEnv<'tcx>,
     ) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>>;
+
+    fn maybe_suggest_convert_to_slice(
+        &self,
+        err: &mut Diagnostic,
+        trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+        candidate_impls: &[ImplCandidate<'tcx>],
+        span: Span,
+    );
 }
 
 fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) {
@@ -2220,7 +2228,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         // - `BuiltinDerivedObligation` with a generator witness (A)
         // - `BuiltinDerivedObligation` with a generator (A)
         // - `BuiltinDerivedObligation` with `impl std::future::Future` (A)
-        // - `BindingObligation` with `impl_send (Send requirement)
+        // - `BindingObligation` with `impl_send` (Send requirement)
         //
         // The first obligation in the chain is the most useful and has the generator that captured
         // the type. The last generator (`outer_generator` below) has information about where the
@@ -3826,6 +3834,73 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         }
         assocs_in_this_method
     }
+
+    /// If the type that failed selection is an array or a reference to an array,
+    /// but the trait is implemented for slices, suggest that the user converts
+    /// the array into a slice.
+    fn maybe_suggest_convert_to_slice(
+        &self,
+        err: &mut Diagnostic,
+        trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+        candidate_impls: &[ImplCandidate<'tcx>],
+        span: Span,
+    ) {
+        // Three cases where we can make a suggestion:
+        // 1. `[T; _]` (array of T)
+        // 2. `&[T; _]` (reference to array of T)
+        // 3. `&mut [T; _]` (mutable reference to array of T)
+        let (element_ty, mut mutability) = match *trait_ref.skip_binder().self_ty().kind() {
+            ty::Array(element_ty, _) => (element_ty, None),
+
+            ty::Ref(_, pointee_ty, mutability) => match *pointee_ty.kind() {
+                ty::Array(element_ty, _) => (element_ty, Some(mutability)),
+                _ => return,
+            },
+
+            _ => return,
+        };
+
+        // Go through all the candidate impls to see if any of them is for
+        // slices of `element_ty` with `mutability`.
+        let mut is_slice = |candidate: Ty<'tcx>| match *candidate.kind() {
+            ty::RawPtr(ty::TypeAndMut { ty: t, mutbl: m }) | ty::Ref(_, t, m) => {
+                if matches!(*t.kind(), ty::Slice(e) if e == element_ty)
+                    && m == mutability.unwrap_or(m)
+                {
+                    // Use the candidate's mutability going forward.
+                    mutability = Some(m);
+                    true
+                } else {
+                    false
+                }
+            }
+            _ => false,
+        };
+
+        // Grab the first candidate that matches, if any, and make a suggestion.
+        if let Some(slice_ty) = candidate_impls
+            .iter()
+            .map(|trait_ref| trait_ref.trait_ref.self_ty())
+            .filter(|t| is_slice(*t))
+            .next()
+        {
+            let msg = &format!("convert the array to a `{}` slice instead", slice_ty);
+
+            if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+                let mut suggestions = vec![];
+                if snippet.starts_with('&') {
+                } else if let Some(hir::Mutability::Mut) = mutability {
+                    suggestions.push((span.shrink_to_lo(), "&mut ".into()));
+                } else {
+                    suggestions.push((span.shrink_to_lo(), "&".into()));
+                }
+                suggestions.push((span.shrink_to_hi(), "[..]".into()));
+                err.multipart_suggestion_verbose(msg, suggestions, Applicability::MaybeIncorrect);
+            } else {
+                err.span_help(span, msg);
+            }
+        }
+    }
 }
 
 /// Add a hint to add a missing borrow or remove an unnecessary one.
diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs
index b94346b0956..336db4fee6c 100644
--- a/compiler/rustc_trait_selection/src/traits/misc.rs
+++ b/compiler/rustc_trait_selection/src/traits/misc.rs
@@ -87,7 +87,12 @@ pub fn type_allowed_to_implement_copy<'tcx>(
             };
             let ty = ocx.normalize(&normalization_cause, param_env, unnormalized_ty);
             let normalization_errors = ocx.select_where_possible();
-            if !normalization_errors.is_empty() {
+
+            // NOTE: The post-normalization type may also reference errors,
+            // such as when we project to a missing type or we have a mismatch
+            // between expected and found const-generic types. Don't report an
+            // additional copy error here, since it's not typically useful.
+            if !normalization_errors.is_empty() || ty.references_error() {
                 tcx.sess.delay_span_bug(field_span, format!("couldn't normalize struct field `{unnormalized_ty}` when checking Copy implementation"));
                 continue;
             }
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 0e047977caa..bfeda88a6d4 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -155,10 +155,12 @@ fn pred_known_to_hold_modulo_regions<'tcx>(
         predicate: pred.to_predicate(infcx.tcx),
     };
 
-    let result = infcx.predicate_must_hold_modulo_regions(&obligation);
+    let result = infcx.evaluate_obligation_no_overflow(&obligation);
     debug!(?result);
 
-    if result && has_non_region_infer {
+    if result.must_apply_modulo_regions() && !has_non_region_infer {
+        true
+    } else if result.may_apply() {
         // Because of inference "guessing", selection can sometimes claim
         // to succeed while the success requires a guess. To ensure
         // this function's result remains infallible, we must confirm
@@ -179,7 +181,7 @@ fn pred_known_to_hold_modulo_regions<'tcx>(
             }
         }
     } else {
-        result
+        false
     }
 }
 
@@ -208,7 +210,7 @@ fn do_normalize_predicates<'tcx>(
     let predicates = match fully_normalize(&infcx, cause, elaborated_env, predicates) {
         Ok(predicates) => predicates,
         Err(errors) => {
-            let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
+            let reported = infcx.err_ctxt().report_fulfillment_errors(&errors);
             return Err(reported);
         }
     };
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index f7559a3f10a..01075d7c55a 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -870,12 +870,12 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> {
 
     fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> {
         let r1 = match *r0 {
-            ty::ReVar(_) => self
+            ty::ReVar(vid) => self
                 .infcx
                 .inner
                 .borrow_mut()
                 .unwrap_region_constraints()
-                .opportunistic_resolve_region(self.infcx.tcx, r0),
+                .opportunistic_resolve_var(self.infcx.tcx, vid),
             _ => r0,
         };
 
diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
index f183248f2d0..f84b2f4428d 100644
--- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
@@ -1,9 +1,9 @@
+use rustc_middle::traits::solve::{Certainty, Goal, MaybeCause};
 use rustc_middle::ty;
-use rustc_session::config::TraitSolver;
 
 use crate::infer::canonical::OriginalQueryValues;
 use crate::infer::InferCtxt;
-use crate::solve::{Certainty, Goal, InferCtxtEvalExt, MaybeCause};
+use crate::solve::InferCtxtEvalExt;
 use crate::traits::{EvaluationResult, OverflowError, PredicateObligation, SelectionContext};
 
 pub trait InferCtxtExt<'tcx> {
@@ -79,13 +79,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
             _ => obligation.param_env.without_const(),
         };
 
-        if self.tcx.sess.opts.unstable_opts.trait_solver != TraitSolver::Next {
-            let c_pred = self.canonicalize_query_keep_static(
-                param_env.and(obligation.predicate),
-                &mut _orig_values,
-            );
-            self.tcx.at(obligation.cause.span()).evaluate_obligation(c_pred)
-        } else {
+        if self.tcx.trait_solver_next() {
             self.probe(|snapshot| {
                 if let Ok((_, certainty)) =
                     self.evaluate_root_goal(Goal::new(self.tcx, param_env, obligation.predicate))
@@ -110,6 +104,12 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
                     Ok(EvaluationResult::EvaluatedToErr)
                 }
             })
+        } else {
+            let c_pred = self.canonicalize_query_keep_static(
+                param_env.and(obligation.predicate),
+                &mut _orig_values,
+            );
+            self.tcx.at(obligation.cause.span()).evaluate_obligation(c_pred)
         }
     }
 
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 21c158fd0fd..ee41d840bae 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -601,10 +601,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         debug!(?obligation, "confirm_fn_pointer_candidate");
 
         let tcx = self.tcx();
-        let self_ty = self
+
+        let Some(self_ty) = self
             .infcx
-            .shallow_resolve(obligation.self_ty().no_bound_vars())
-            .expect("fn pointer should not capture bound vars from predicate");
+            .shallow_resolve(obligation.self_ty().no_bound_vars()) else
+        {
+            // FIXME: Ideally we'd support `for<'a> fn(&'a ()): Fn(&'a ())`,
+            // but we do not currently. Luckily, such a bound is not
+            // particularly useful, so we don't expect users to write
+            // them often.
+            return Err(SelectionError::Unimplemented);
+        };
+
         let sig = self_ty.fn_sig(tcx);
         let trait_ref = closure_trait_ref_and_return_type(
             tcx,
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index cd3f3c114ba..d7ce0078124 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -50,7 +50,6 @@ use rustc_middle::ty::relate::TypeRelation;
 use rustc_middle::ty::SubstsRef;
 use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate};
 use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
-use rustc_session::config::TraitSolver;
 use rustc_span::symbol::sym;
 
 use std::cell::{Cell, RefCell};
@@ -465,14 +464,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         if candidates.len() > 1 {
             let mut i = 0;
             while i < candidates.len() {
-                let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| {
+                let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| {
                     self.candidate_should_be_dropped_in_favor_of(
                         &candidates[i],
                         &candidates[j],
                         needs_infer,
-                    )
+                    ) == DropVictim::Yes
                 });
-                if is_dup {
+                if should_drop_i {
                     debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
                     candidates.swap_remove(i);
                 } else {
@@ -545,13 +544,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         obligation: &PredicateObligation<'tcx>,
     ) -> Result<EvaluationResult, OverflowError> {
         self.evaluation_probe(|this| {
-            if this.tcx().sess.opts.unstable_opts.trait_solver != TraitSolver::Next {
+            if this.tcx().trait_solver_next() {
+                this.evaluate_predicates_recursively_in_new_solver([obligation.clone()])
+            } else {
                 this.evaluate_predicate_recursively(
                     TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()),
                     obligation.clone(),
                 )
-            } else {
-                this.evaluate_predicates_recursively_in_new_solver([obligation.clone()])
             }
         })
     }
@@ -591,7 +590,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     where
         I: IntoIterator<Item = PredicateObligation<'tcx>> + std::fmt::Debug,
     {
-        if self.tcx().sess.opts.unstable_opts.trait_solver != TraitSolver::Next {
+        if self.tcx().trait_solver_next() {
+            self.evaluate_predicates_recursively_in_new_solver(predicates)
+        } else {
             let mut result = EvaluatedToOk;
             for obligation in predicates {
                 let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?;
@@ -604,8 +605,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 }
             }
             Ok(result)
-        } else {
-            self.evaluate_predicates_recursively_in_new_solver(predicates)
         }
     }
 
@@ -1083,7 +1082,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     let mut nested_result = EvaluationResult::EvaluatedToOk;
                     for obligation in nested_obligations {
                         nested_result = cmp::max(
-                            this.evaluate_predicate_recursively(stack.list(), obligation)?,
+                            this.evaluate_predicate_recursively(previous_stack, obligation)?,
                             nested_result,
                         );
                     }
@@ -1092,7 +1091,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                         let obligation = obligation.with(this.tcx(), predicate);
                         result = cmp::max(
                             nested_result,
-                            this.evaluate_trait_predicate_recursively(stack.list(), obligation)?,
+                            this.evaluate_trait_predicate_recursively(previous_stack, obligation)?,
                         );
                     }
                 }
@@ -1842,16 +1841,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             ProjectionMatchesProjection::No
         }
     }
+}
 
-    ///////////////////////////////////////////////////////////////////////////
-    // WINNOW
-    //
-    // Winnowing is the process of attempting to resolve ambiguity by
-    // probing further. During the winnowing process, we unify all
-    // type variables and then we also attempt to evaluate recursive
-    // bounds to see if they are satisfied.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+enum DropVictim {
+    Yes,
+    No,
+}
 
-    /// Returns `true` if `victim` should be dropped in favor of
+/// ## Winnowing
+///
+/// Winnowing is the process of attempting to resolve ambiguity by
+/// probing further. During the winnowing process, we unify all
+/// type variables and then we also attempt to evaluate recursive
+/// bounds to see if they are satisfied.
+impl<'tcx> SelectionContext<'_, 'tcx> {
+    /// Returns `DropVictim::Yes` if `victim` should be dropped in favor of
     /// `other`. Generally speaking we will drop duplicate
     /// candidates and prefer where-clause candidates.
     ///
@@ -1861,9 +1866,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         victim: &EvaluatedCandidate<'tcx>,
         other: &EvaluatedCandidate<'tcx>,
         needs_infer: bool,
-    ) -> bool {
+    ) -> DropVictim {
         if victim.candidate == other.candidate {
-            return true;
+            return DropVictim::Yes;
         }
 
         // Check if a bound would previously have been removed when normalizing
@@ -1887,11 +1892,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             }
 
             // FIXME(@jswrenn): this should probably be more sophisticated
-            (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => false,
+            (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => DropVictim::No,
 
             // (*)
-            (BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_), _) => true,
-            (_, BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_)) => false,
+            (BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_), _) => {
+                DropVictim::Yes
+            }
+            (_, BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_)) => {
+                DropVictim::No
+            }
 
             (ParamCandidate(other), ParamCandidate(victim)) => {
                 let same_except_bound_vars = other.skip_binder().trait_ref
@@ -1905,28 +1914,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     // or the current one if tied (they should both evaluate to the same answer). This is
                     // probably best characterized as a "hack", since we might prefer to just do our
                     // best to *not* create essentially duplicate candidates in the first place.
-                    other.bound_vars().len() <= victim.bound_vars().len()
+                    if other.bound_vars().len() <= victim.bound_vars().len() {
+                        DropVictim::Yes
+                    } else {
+                        DropVictim::No
+                    }
                 } else if other.skip_binder().trait_ref == victim.skip_binder().trait_ref
                     && victim.skip_binder().constness == ty::BoundConstness::NotConst
                     && other.skip_binder().polarity == victim.skip_binder().polarity
                 {
                     // Drop otherwise equivalent non-const candidates in favor of const candidates.
-                    true
+                    DropVictim::Yes
                 } else {
-                    false
+                    DropVictim::No
                 }
             }
 
             // Drop otherwise equivalent non-const fn pointer candidates
-            (FnPointerCandidate { .. }, FnPointerCandidate { is_const: false }) => true,
+            (FnPointerCandidate { .. }, FnPointerCandidate { is_const: false }) => DropVictim::Yes,
 
-            // Global bounds from the where clause should be ignored
-            // here (see issue #50825). Otherwise, we have a where
-            // clause so don't go around looking for impls.
-            // Arbitrarily give param candidates priority
-            // over projection and object candidates.
             (
-                ParamCandidate(ref cand),
+                ParamCandidate(ref other_cand),
                 ImplCandidate(..)
                 | ClosureCandidate { .. }
                 | GeneratorCandidate
@@ -1939,11 +1947,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 | TraitAliasCandidate
                 | ObjectCandidate(_)
                 | ProjectionCandidate(..),
-            ) => !is_global(cand),
-            (ObjectCandidate(_) | ProjectionCandidate(..), ParamCandidate(ref cand)) => {
+            ) => {
+                if is_global(other_cand) {
+                    DropVictim::No
+                } else {
+                    // We have a where clause so don't go around looking
+                    // for impls. Arbitrarily give param candidates priority
+                    // over projection and object candidates.
+                    //
+                    // Global bounds from the where clause should be ignored
+                    // here (see issue #50825).
+                    DropVictim::Yes
+                }
+            }
+            (ObjectCandidate(_) | ProjectionCandidate(..), ParamCandidate(ref victim_cand)) => {
                 // Prefer these to a global where-clause bound
                 // (see issue #50825).
-                is_global(cand)
+                if is_global(victim_cand) { DropVictim::Yes } else { DropVictim::No }
             }
             (
                 ImplCandidate(_)
@@ -1956,18 +1976,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 | TraitUpcastingUnsizeCandidate(_)
                 | BuiltinCandidate { has_nested: true }
                 | TraitAliasCandidate,
-                ParamCandidate(ref cand),
+                ParamCandidate(ref victim_cand),
             ) => {
                 // Prefer these to a global where-clause bound
                 // (see issue #50825).
-                is_global(cand) && other.evaluation.must_apply_modulo_regions()
+                if is_global(victim_cand) && other.evaluation.must_apply_modulo_regions() {
+                    DropVictim::Yes
+                } else {
+                    DropVictim::No
+                }
             }
 
             (ProjectionCandidate(i, _), ProjectionCandidate(j, _))
             | (ObjectCandidate(i), ObjectCandidate(j)) => {
                 // Arbitrarily pick the lower numbered candidate for backwards
                 // compatibility reasons. Don't let this affect inference.
-                i < j && !needs_infer
+                if i < j && !needs_infer { DropVictim::Yes } else { DropVictim::No }
             }
             (ObjectCandidate(_), ProjectionCandidate(..))
             | (ProjectionCandidate(..), ObjectCandidate(_)) => {
@@ -1987,7 +2011,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 | TraitUpcastingUnsizeCandidate(_)
                 | BuiltinCandidate { .. }
                 | TraitAliasCandidate,
-            ) => true,
+            ) => DropVictim::Yes,
 
             (
                 ImplCandidate(..)
@@ -2001,7 +2025,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 | BuiltinCandidate { .. }
                 | TraitAliasCandidate,
                 ObjectCandidate(_) | ProjectionCandidate(..),
-            ) => false,
+            ) => DropVictim::No,
 
             (&ImplCandidate(other_def), &ImplCandidate(victim_def)) => {
                 // See if we can toss out `victim` based on specialization.
@@ -2014,7 +2038,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 let tcx = self.tcx();
                 if other.evaluation.must_apply_modulo_regions() {
                     if tcx.specializes((other_def, victim_def)) {
-                        return true;
+                        return DropVictim::Yes;
                     }
                 }
 
@@ -2060,13 +2084,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                             // will then correctly report an inference error, since the
                             // existence of multiple marker trait impls tells us nothing
                             // about which one should actually apply.
-                            !needs_infer
+                            if needs_infer { DropVictim::No } else { DropVictim::Yes }
                         }
-                        Some(_) => true,
-                        None => false,
+                        Some(_) => DropVictim::Yes,
+                        None => DropVictim::No,
                     }
                 } else {
-                    false
+                    DropVictim::No
                 }
             }
 
@@ -2092,10 +2116,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 | TraitUpcastingUnsizeCandidate(_)
                 | BuiltinCandidate { has_nested: true }
                 | TraitAliasCandidate,
-            ) => false,
+            ) => DropVictim::No,
         }
     }
+}
 
+impl<'tcx> SelectionContext<'_, 'tcx> {
     fn sized_conditions(
         &mut self,
         obligation: &TraitObligation<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index d1d6a7a90cf..fcfb60b2603 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -99,10 +99,10 @@ pub fn translate_substs<'tcx>(
             }
 
             fulfill_implication(infcx, param_env, source_trait_ref, target_impl).unwrap_or_else(
-                |_| {
+                |()| {
                     bug!(
-                        "When translating substitutions for specialization, the expected \
-                         specialization failed to hold"
+                        "When translating substitutions from {source_impl:?} to {target_impl:?}, \
+                        the expected specialization failed to hold"
                     )
                 },
             )
diff --git a/compiler/rustc_ty_utils/locales/en-US.ftl b/compiler/rustc_ty_utils/messages.ftl
index abe65a0e3fe..abe65a0e3fe 100644
--- a/compiler/rustc_ty_utils/locales/en-US.ftl
+++ b/compiler/rustc_ty_utils/messages.ftl
diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs
index 2fc55a2527d..209365b70a7 100644
--- a/compiler/rustc_ty_utils/src/assoc.rs
+++ b/compiler/rustc_ty_utils/src/assoc.rs
@@ -1,7 +1,6 @@
-use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
-use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
 use rustc_hir::definitions::DefPathData;
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_middle::ty::{self, ImplTraitInTraitData, InternalSubsts, TyCtxt};
@@ -23,7 +22,7 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] {
     let item = tcx.hir().expect_item(def_id.expect_local());
     match item.kind {
         hir::ItemKind::Trait(.., ref trait_item_refs) => {
-            if tcx.sess.opts.unstable_opts.lower_impl_trait_in_trait_to_assoc_ty {
+            if tcx.lower_impl_trait_in_trait_to_assoc_ty() {
                 // We collect RPITITs for each trait method's return type and create a
                 // corresponding associated item using associated_items_for_impl_trait_in_trait
                 // query.
@@ -54,7 +53,7 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] {
             }
         }
         hir::ItemKind::Impl(ref impl_) => {
-            if tcx.sess.opts.unstable_opts.lower_impl_trait_in_trait_to_assoc_ty {
+            if tcx.lower_impl_trait_in_trait_to_assoc_ty() {
                 // We collect RPITITs for each trait method's return type, on the impl side too and
                 // create a corresponding associated item using
                 // associated_items_for_impl_trait_in_trait query.
@@ -97,7 +96,7 @@ fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItems {
     }
 }
 
-fn impl_item_implementor_ids(tcx: TyCtxt<'_>, impl_id: DefId) -> FxHashMap<DefId, DefId> {
+fn impl_item_implementor_ids(tcx: TyCtxt<'_>, impl_id: DefId) -> DefIdMap<DefId> {
     tcx.associated_items(impl_id)
         .in_definition_order()
         .filter_map(|item| item.trait_item_def_id.map(|trait_item| (trait_item, item.def_id)))
@@ -302,9 +301,6 @@ fn associated_item_for_impl_trait_in_trait(
     // There are no inferred outlives for the synthesized associated type.
     trait_assoc_ty.inferred_outlives_of(&[]);
 
-    // FIXME implement this.
-    trait_assoc_ty.explicit_item_bounds(&[]);
-
     local_def_id
 }
 
@@ -316,11 +312,12 @@ fn impl_associated_item_for_impl_trait_in_trait(
     trait_assoc_def_id: LocalDefId,
     impl_fn_def_id: LocalDefId,
 ) -> LocalDefId {
-    let impl_def_id = tcx.local_parent(impl_fn_def_id);
+    let impl_local_def_id = tcx.local_parent(impl_fn_def_id);
+    let impl_def_id = impl_local_def_id.to_def_id();
 
     // FIXME fix the span, we probably want the def_id of the return type of the function
     let span = tcx.def_span(impl_fn_def_id);
-    let impl_assoc_ty = tcx.at(span).create_def(impl_def_id, DefPathData::ImplTraitAssocTy);
+    let impl_assoc_ty = tcx.at(span).create_def(impl_local_def_id, DefPathData::ImplTraitAssocTy);
 
     let local_def_id = impl_assoc_ty.def_id();
     let def_id = local_def_id.to_def_id();
@@ -345,13 +342,53 @@ fn impl_associated_item_for_impl_trait_in_trait(
         fn_has_self_parameter: false,
     });
 
+    // Copy param_env of the containing function. The synthesized associated type doesn't have
+    // extra predicates to assume.
+    impl_assoc_ty.param_env(tcx.param_env(impl_fn_def_id));
+
     // Copy impl_defaultness of the containing function.
     impl_assoc_ty.impl_defaultness(tcx.impl_defaultness(impl_fn_def_id));
 
-    // Copy generics_of the trait's associated item.
-    // FIXME: This is not correct, in particular the parent is going to be wrong. So we would need
-    // to copy from trait_assoc_def_id and adjust things.
-    impl_assoc_ty.generics_of(tcx.generics_of(trait_assoc_def_id).clone());
+    // Copy generics_of the trait's associated item but the impl as the parent.
+    impl_assoc_ty.generics_of({
+        let trait_assoc_generics = tcx.generics_of(trait_assoc_def_id);
+        let trait_assoc_parent_count = trait_assoc_generics.parent_count;
+        let mut params = trait_assoc_generics.params.clone();
+
+        let parent_generics = tcx.generics_of(impl_def_id);
+        let parent_count = parent_generics.parent_count + parent_generics.params.len();
+
+        let mut impl_fn_params = tcx.generics_of(impl_fn_def_id).params.clone();
+
+        for param in &mut params {
+            param.index = param.index + parent_count as u32 + impl_fn_params.len() as u32
+                - trait_assoc_parent_count as u32;
+        }
+
+        impl_fn_params.extend(params);
+        params = impl_fn_params;
+
+        let param_def_id_to_index =
+            params.iter().map(|param| (param.def_id, param.index)).collect();
+
+        ty::Generics {
+            parent: Some(impl_def_id),
+            parent_count,
+            params,
+            param_def_id_to_index,
+            has_self: false,
+            has_late_bound_regions: trait_assoc_generics.has_late_bound_regions,
+        }
+    });
+
+    // There are no predicates for the synthesized associated type.
+    impl_assoc_ty.explicit_predicates_of(ty::GenericPredicates {
+        parent: Some(impl_def_id),
+        predicates: &[],
+    });
+
+    // There are no inferred outlives for the synthesized associated type.
+    impl_assoc_ty.inferred_outlives_of(&[]);
 
     local_def_id
 }
diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs
index 35f468aa952..3865a8f3223 100644
--- a/compiler/rustc_ty_utils/src/lib.rs
+++ b/compiler/rustc_ty_utils/src/lib.rs
@@ -33,7 +33,7 @@ pub mod representability;
 mod structural_match;
 mod ty;
 
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
 
 pub fn provide(providers: &mut Providers) {
     abi::provide(providers);
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index 70c9de91fbf..c8ed1f365f1 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -3,8 +3,8 @@ use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_index::bit_set::BitSet;
 use rustc_middle::ty::{
-    self, Binder, EarlyBinder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt,
-    TypeSuperVisitable, TypeVisitable, TypeVisitor,
+    self, Binder, EarlyBinder, ImplTraitInTraitData, Predicate, PredicateKind, ToPredicate, Ty,
+    TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
 };
 use rustc_session::config::TraitSolver;
 use rustc_span::def_id::{DefId, CRATE_DEF_ID};
@@ -117,6 +117,15 @@ fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> &[Ty<'_>] {
 
 /// See `ParamEnv` struct definition for details.
 fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
+    // When computing the param_env of an RPITIT, copy param_env of the containing function. The
+    // synthesized associated type doesn't have extra predicates to assume.
+    let def_id =
+        if let Some(ImplTraitInTraitData::Trait { fn_def_id, .. }) = tcx.opt_rpitit_info(def_id) {
+            fn_def_id
+        } else {
+            def_id
+        };
+
     // Compute the bounds on Self and the type parameters.
     let ty::InstantiatedPredicates { mut predicates, .. } =
         tcx.predicates_of(def_id).instantiate_identity(tcx);
diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs
index ee4ef57c38f..3a053d4c6a9 100644
--- a/compiler/rustc_type_ir/src/fold.rs
+++ b/compiler/rustc_type_ir/src/fold.rs
@@ -18,7 +18,7 @@
 //!     It defines a "skeleton" of how they should be folded.
 //! - `TypeSuperFoldable`. This is implemented only for each type of interest,
 //!   and defines the folding "skeleton" for these types.
-//! - `TypeFolder`/`FallibleTypeFolder. One of these is implemented for each
+//! - `TypeFolder`/`FallibleTypeFolder`. One of these is implemented for each
 //!   folder. This defines how types of interest are folded.
 //!
 //! This means each fold is a mixture of (a) generic folding operations, and (b)
diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs
index ebe2b76aef3..62e699eefd7 100644
--- a/compiler/rustc_type_ir/src/sty.rs
+++ b/compiler/rustc_type_ir/src/sty.rs
@@ -167,7 +167,7 @@ pub enum TyKind<I: Interner> {
     /// lifetimes bound by the witness itself.
     ///
     /// This variant is only using when `drop_tracking_mir` is set.
-    /// This contains the `DefId` and the `SubstRef` of the generator.
+    /// This contains the `DefId` and the `SubstsRef` of the generator.
     /// The actual witness types are computed on MIR by the `mir_generator_witnesses` query.
     ///
     /// Looking at the following example, the witness for this generator
diff --git a/config.toml.example b/config.example.toml
index dee0d8f254b..dee0d8f254b 100644
--- a/config.toml.example
+++ b/config.example.toml
diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs
index 44a37899007..241b11c3f5f 100644
--- a/library/alloc/src/boxed.rs
+++ b/library/alloc/src/boxed.rs
@@ -214,6 +214,7 @@ impl<T> Box<T> {
     #[inline(always)]
     #[stable(feature = "rust1", since = "1.0.0")]
     #[must_use]
+    #[rustc_diagnostic_item = "box_new"]
     pub fn new(x: T) -> Self {
         #[rustc_box]
         Box::new(x)
diff --git a/library/alloc/src/collections/vec_deque/drain.rs b/library/alloc/src/collections/vec_deque/drain.rs
index 89feb361ddc..0be274a3822 100644
--- a/library/alloc/src/collections/vec_deque/drain.rs
+++ b/library/alloc/src/collections/vec_deque/drain.rs
@@ -52,36 +52,22 @@ impl<'a, T, A: Allocator> Drain<'a, T, A> {
         }
     }
 
-    // Only returns pointers to the slices, as that's
-    // all we need to drop them. May only be called if `self.remaining != 0`.
+    // Only returns pointers to the slices, as that's all we need
+    // to drop them. May only be called if `self.remaining != 0`.
     unsafe fn as_slices(&self) -> (*mut [T], *mut [T]) {
         unsafe {
             let deque = self.deque.as_ref();
-            // FIXME: This is doing almost exactly the same thing as the else branch in `VecDeque::slice_ranges`.
-            // Unfortunately, we can't just call `slice_ranges` here, as the deque's `len` is currently
-            // just `drain_start`, so the range check would (almost) always panic. Between temporarily
-            // adjusting the deques `len` to call `slice_ranges`, and just copy pasting the `slice_ranges`
-            // implementation, this seemed like the less hacky solution, though it might be good to
-            // find a better one in the future.
-
-            // because `self.remaining != 0`, we know that `self.idx < deque.original_len`, so it's a valid
-            // logical index.
-            let wrapped_start = deque.to_physical_idx(self.idx);
-
-            let head_len = deque.capacity() - wrapped_start;
-
-            let (a_range, b_range) = if head_len >= self.remaining {
-                (wrapped_start..wrapped_start + self.remaining, 0..0)
-            } else {
-                let tail_len = self.remaining - head_len;
-                (wrapped_start..deque.capacity(), 0..tail_len)
-            };
-
-            // SAFETY: the range `self.idx..self.idx+self.remaining` lies strictly inside
-            // the range `0..deque.original_len`. because of this, and because of the fact
-            // that we acquire `a_range` and `b_range` exactly like `slice_ranges` would,
-            // it's guaranteed that `a_range` and `b_range` represent valid ranges into
-            // the deques buffer.
+
+            // We know that `self.idx + self.remaining <= deque.len <= usize::MAX`, so this won't overflow.
+            let logical_remaining_range = self.idx..self.idx + self.remaining;
+
+            // SAFETY: `logical_remaining_range` represents the
+            // range into the logical buffer of elements that
+            // haven't been drained yet, so they're all initialized,
+            // and `slice::range(start..end, end) == start..end`,
+            // so the preconditions for `slice_ranges` are met.
+            let (a_range, b_range) =
+                deque.slice_ranges(logical_remaining_range.clone(), logical_remaining_range.end);
             (deque.buffer_range(a_range), deque.buffer_range(b_range))
         }
     }
diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs
index 8317ac431a5..48e907e402c 100644
--- a/library/alloc/src/collections/vec_deque/mod.rs
+++ b/library/alloc/src/collections/vec_deque/mod.rs
@@ -1156,7 +1156,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     #[inline]
     #[stable(feature = "deque_extras_15", since = "1.5.0")]
     pub fn as_slices(&self) -> (&[T], &[T]) {
-        let (a_range, b_range) = self.slice_ranges(..);
+        let (a_range, b_range) = self.slice_ranges(.., self.len);
         // SAFETY: `slice_ranges` always returns valid ranges into
         // the physical buffer.
         unsafe { (&*self.buffer_range(a_range), &*self.buffer_range(b_range)) }
@@ -1190,7 +1190,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     #[inline]
     #[stable(feature = "deque_extras_15", since = "1.5.0")]
     pub fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) {
-        let (a_range, b_range) = self.slice_ranges(..);
+        let (a_range, b_range) = self.slice_ranges(.., self.len);
         // SAFETY: `slice_ranges` always returns valid ranges into
         // the physical buffer.
         unsafe { (&mut *self.buffer_range(a_range), &mut *self.buffer_range(b_range)) }
@@ -1232,19 +1232,28 @@ impl<T, A: Allocator> VecDeque<T, A> {
 
     /// Given a range into the logical buffer of the deque, this function
     /// return two ranges into the physical buffer that correspond to
-    /// the given range.
-    fn slice_ranges<R>(&self, range: R) -> (Range<usize>, Range<usize>)
+    /// the given range. The `len` parameter should usually just be `self.len`;
+    /// the reason it's passed explicitly is that if the deque is wrapped in
+    /// a `Drain`, then `self.len` is not actually the length of the deque.
+    ///
+    /// # Safety
+    ///
+    /// This function is always safe to call. For the resulting ranges to be valid
+    /// ranges into the physical buffer, the caller must ensure that the result of
+    /// calling `slice::range(range, ..len)` represents a valid range into the
+    /// logical buffer, and that all elements in that range are initialized.
+    fn slice_ranges<R>(&self, range: R, len: usize) -> (Range<usize>, Range<usize>)
     where
         R: RangeBounds<usize>,
     {
-        let Range { start, end } = slice::range(range, ..self.len);
+        let Range { start, end } = slice::range(range, ..len);
         let len = end - start;
 
         if len == 0 {
             (0..0, 0..0)
         } else {
-            // `slice::range` guarantees that `start <= end <= self.len`.
-            // because `len != 0`, we know that `start < end`, so `start < self.len`
+            // `slice::range` guarantees that `start <= end <= len`.
+            // because `len != 0`, we know that `start < end`, so `start < len`
             // and the indexing is valid.
             let wrapped_start = self.to_physical_idx(start);
 
@@ -1290,7 +1299,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     where
         R: RangeBounds<usize>,
     {
-        let (a_range, b_range) = self.slice_ranges(range);
+        let (a_range, b_range) = self.slice_ranges(range, self.len);
         // SAFETY: The ranges returned by `slice_ranges`
         // are valid ranges into the physical buffer, so
         // it's ok to pass them to `buffer_range` and
@@ -1330,7 +1339,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     where
         R: RangeBounds<usize>,
     {
-        let (a_range, b_range) = self.slice_ranges(range);
+        let (a_range, b_range) = self.slice_ranges(range, self.len);
         // SAFETY: The ranges returned by `slice_ranges`
         // are valid ranges into the physical buffer, so
         // it's ok to pass them to `buffer_range` and
diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs
index 932a537c598..77b0447b345 100644
--- a/library/alloc/src/rc.rs
+++ b/library/alloc/src/rc.rs
@@ -681,6 +681,24 @@ impl<T> Rc<T> {
             Err(this)
         }
     }
+
+    /// Returns the inner value, if the `Rc` has exactly one strong reference.
+    ///
+    /// Otherwise, [`None`] is returned and the `Rc` is dropped.
+    ///
+    /// This will succeed even if there are outstanding weak references.
+    ///
+    /// If `Rc::into_inner` is called on every clone of this `Rc`,
+    /// it is guaranteed that exactly one of the calls returns the inner value.
+    /// This means in particular that the inner value is not dropped.
+    ///
+    /// This is equivalent to `Rc::try_unwrap(...).ok()`. (Note that these are not equivalent for
+    /// `Arc`, due to race conditions that do not apply to `Rc`.)
+    #[inline]
+    #[unstable(feature = "rc_into_inner", issue = "106894")]
+    pub fn into_inner(this: Self) -> Option<T> {
+        Rc::try_unwrap(this).ok()
+    }
 }
 
 impl<T> Rc<[T]> {
diff --git a/library/alloc/src/rc/tests.rs b/library/alloc/src/rc/tests.rs
index 32433cfbdcf..2784108e0e6 100644
--- a/library/alloc/src/rc/tests.rs
+++ b/library/alloc/src/rc/tests.rs
@@ -152,6 +152,21 @@ fn try_unwrap() {
 }
 
 #[test]
+fn into_inner() {
+    let x = Rc::new(3);
+    assert_eq!(Rc::into_inner(x), Some(3));
+
+    let x = Rc::new(4);
+    let y = Rc::clone(&x);
+    assert_eq!(Rc::into_inner(x), None);
+    assert_eq!(Rc::into_inner(y), Some(4));
+
+    let x = Rc::new(5);
+    let _w = Rc::downgrade(&x);
+    assert_eq!(Rc::into_inner(x), Some(5));
+}
+
+#[test]
 fn into_from_raw() {
     let x = Rc::new(Box::new("hello"));
     let y = x.clone();
diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs
index fdd341a06ef..f37573c6f27 100644
--- a/library/alloc/src/sync.rs
+++ b/library/alloc/src/sync.rs
@@ -51,8 +51,16 @@ mod tests;
 ///
 /// Going above this limit will abort your program (although not
 /// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references.
+/// Trying to go above it might call a `panic` (if not actually going above it).
+///
+/// This is a global invariant, and also applies when using a compare-exchange loop.
+///
+/// See comment in `Arc::clone`.
 const MAX_REFCOUNT: usize = (isize::MAX) as usize;
 
+/// The error in case either counter reaches above `MAX_REFCOUNT`, and we can `panic` safely.
+const INTERNAL_OVERFLOW_ERROR: &str = "Arc counter overflow";
+
 #[cfg(not(sanitize = "thread"))]
 macro_rules! acquire {
     ($x:expr) => {
@@ -1104,6 +1112,9 @@ impl<T: ?Sized> Arc<T> {
                 continue;
             }
 
+            // We can't allow the refcount to increase much past `MAX_REFCOUNT`.
+            assert!(cur <= MAX_REFCOUNT, "{}", INTERNAL_OVERFLOW_ERROR);
+
             // NOTE: this code currently ignores the possibility of overflow
             // into usize::MAX; in general both Rc and Arc need to be adjusted
             // to deal with overflow.
@@ -1519,6 +1530,11 @@ impl<T: ?Sized> Clone for Arc<T> {
         // the worst already happened and we actually do overflow the `usize` counter. However, that
         // requires the counter to grow from `isize::MAX` to `usize::MAX` between the increment
         // above and the `abort` below, which seems exceedingly unlikely.
+        //
+        // This is a global invariant, and also applies when using a compare-exchange loop to increment
+        // counters in other methods.
+        // Otherwise, the counter could be brought to an almost-overflow using a compare-exchange loop,
+        // and then overflow using a few `fetch_add`s.
         if old_size > MAX_REFCOUNT {
             abort();
         }
@@ -2180,9 +2196,7 @@ impl<T: ?Sized> Weak<T> {
                     return None;
                 }
                 // See comments in `Arc::clone` for why we do this (for `mem::forget`).
-                if n > MAX_REFCOUNT {
-                    abort();
-                }
+                assert!(n <= MAX_REFCOUNT, "{}", INTERNAL_OVERFLOW_ERROR);
                 Some(n + 1)
             })
             .ok()
diff --git a/library/alloc/src/tests.rs b/library/alloc/src/tests.rs
index 299ed156a5d..b1d3a9fa8ac 100644
--- a/library/alloc/src/tests.rs
+++ b/library/alloc/src/tests.rs
@@ -4,7 +4,6 @@ use core::any::Any;
 use core::clone::Clone;
 use core::convert::TryInto;
 use core::ops::Deref;
-use core::result::Result::{Err, Ok};
 
 use std::boxed::Box;
 
@@ -15,7 +14,7 @@ fn test_owned_clone() {
     assert!(a == b);
 }
 
-#[derive(PartialEq, Eq)]
+#[derive(Debug, PartialEq, Eq)]
 struct Test;
 
 #[test]
@@ -23,24 +22,17 @@ fn any_move() {
     let a = Box::new(8) as Box<dyn Any>;
     let b = Box::new(Test) as Box<dyn Any>;
 
-    match a.downcast::<i32>() {
-        Ok(a) => {
-            assert!(a == Box::new(8));
-        }
-        Err(..) => panic!(),
-    }
-    match b.downcast::<Test>() {
-        Ok(a) => {
-            assert!(a == Box::new(Test));
-        }
-        Err(..) => panic!(),
-    }
+    let a: Box<i32> = a.downcast::<i32>().unwrap();
+    assert_eq!(*a, 8);
+
+    let b: Box<Test> = b.downcast::<Test>().unwrap();
+    assert_eq!(*b, Test);
 
     let a = Box::new(8) as Box<dyn Any>;
     let b = Box::new(Test) as Box<dyn Any>;
 
-    assert!(a.downcast::<Box<Test>>().is_err());
-    assert!(b.downcast::<Box<i32>>().is_err());
+    assert!(a.downcast::<Box<i32>>().is_err());
+    assert!(b.downcast::<Box<Test>>().is_err());
 }
 
 #[test]
diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs
index 897d03595d7..9d1720acf36 100644
--- a/library/core/src/cell.rs
+++ b/library/core/src/cell.rs
@@ -209,6 +209,12 @@ pub use once::OnceCell;
 
 /// A mutable memory location.
 ///
+/// # Memory layout
+///
+/// `Cell<T>` has the same [memory layout and caveats as
+/// `UnsafeCell<T>`](UnsafeCell#memory-layout). In particular, this means that
+/// `Cell<T>` has the same in-memory representation as its inner type `T`.
+///
 /// # Examples
 ///
 /// In this example, you can see that `Cell<T>` enables mutation inside an
diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs
index 4da7c323492..a74a56bc5b2 100644
--- a/library/core/src/convert/num.rs
+++ b/library/core/src/convert/num.rs
@@ -172,7 +172,18 @@ impl_from! { f32, f64, #[stable(feature = "lossless_float_conv", since = "1.6.0"
 #[stable(feature = "float_from_bool", since = "1.68.0")]
 #[rustc_const_unstable(feature = "const_num_from_num", issue = "87852")]
 impl const From<bool> for f32 {
-    /// Converts `bool` to `f32` losslessly.
+    /// Converts `bool` to `f32` losslessly. The resulting value is positive
+    /// `0.0` for `false` and `1.0` for `true` values.
+    ///
+    /// # Examples
+    /// ```
+    /// let x: f32 = false.into();
+    /// assert_eq!(x, 0.0);
+    /// assert!(x.is_sign_positive());
+    ///
+    /// let y: f32 = true.into();
+    /// assert_eq!(y, 1.0);
+    /// ```
     #[inline]
     fn from(small: bool) -> Self {
         small as u8 as Self
@@ -181,7 +192,18 @@ impl const From<bool> for f32 {
 #[stable(feature = "float_from_bool", since = "1.68.0")]
 #[rustc_const_unstable(feature = "const_num_from_num", issue = "87852")]
 impl const From<bool> for f64 {
-    /// Converts `bool` to `f64` losslessly.
+    /// Converts `bool` to `f64` losslessly. The resulting value is positive
+    /// `0.0` for `false` and `1.0` for `true` values.
+    ///
+    /// # Examples
+    /// ```
+    /// let x: f64 = false.into();
+    /// assert_eq!(x, 0.0);
+    /// assert!(x.is_sign_positive());
+    ///
+    /// let y: f64 = true.into();
+    /// assert_eq!(y, 1.0);
+    /// ```
     #[inline]
     fn from(small: bool) -> Self {
         small as u8 as Self
diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs
index 72db1d87ca3..d2d9771bdce 100644
--- a/library/core/src/intrinsics/mir.rs
+++ b/library/core/src/intrinsics/mir.rs
@@ -227,7 +227,7 @@
 //! are no resume and abort terminators, and terminators that might unwind do not have any way to
 //! indicate the unwind block.
 //!
-//!  - [`Goto`], [`Return`], [`Unreachable`], [`Drop`](Drop()), and [`DropAndReplace`] have associated functions.
+//!  - [`Goto`], [`Return`], [`Unreachable`] and [`Drop`](Drop()) have associated functions.
 //!  - `match some_int_operand` becomes a `SwitchInt`. Each arm should be `literal => basic_block`
 //!     - The exception is the last arm, which must be `_ => basic_block` and corresponds to the
 //!       otherwise branch.
@@ -259,7 +259,6 @@ define!("mir_return", fn Return() -> BasicBlock);
 define!("mir_goto", fn Goto(destination: BasicBlock) -> BasicBlock);
 define!("mir_unreachable", fn Unreachable() -> BasicBlock);
 define!("mir_drop", fn Drop<T>(place: T, goto: BasicBlock));
-define!("mir_drop_and_replace", fn DropAndReplace<T>(place: T, value: T, goto: BasicBlock));
 define!("mir_call", fn Call<T>(place: T, goto: BasicBlock, call: T));
 define!("mir_storage_live", fn StorageLive<T>(local: T));
 define!("mir_storage_dead", fn StorageDead<T>(local: T));
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index 520ae0edb09..427146941ad 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -324,7 +324,7 @@ pub trait StructuralEq {
 /// attempt to derive a `Copy` implementation, we'll get an error:
 ///
 /// ```text
-/// the trait `Copy` may not be implemented for this type; field `points` does not implement `Copy`
+/// the trait `Copy` cannot be implemented for this type; field `points` does not implement `Copy`
 /// ```
 ///
 /// Shared references (`&T`) are also `Copy`, so a type can be `Copy`, even when it holds
diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs
index fbda8f82b1b..06d22d84aed 100644
--- a/library/core/src/num/nonzero.rs
+++ b/library/core/src/num/nonzero.rs
@@ -1147,12 +1147,10 @@ macro_rules! nonzero_min_max_unsigned {
                 /// # Examples
                 ///
                 /// ```
-                /// #![feature(nonzero_min_max)]
-                ///
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 #[doc = concat!("assert_eq!(", stringify!($Ty), "::MIN.get(), 1", stringify!($Int), ");")]
                 /// ```
-                #[unstable(feature = "nonzero_min_max", issue = "89065")]
+                #[stable(feature = "nonzero_min_max", since = "CURRENT_RUSTC_VERSION")]
                 pub const MIN: Self = Self::new(1).unwrap();
 
                 /// The largest value that can be represented by this non-zero
@@ -1162,12 +1160,10 @@ macro_rules! nonzero_min_max_unsigned {
                 /// # Examples
                 ///
                 /// ```
-                /// #![feature(nonzero_min_max)]
-                ///
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 #[doc = concat!("assert_eq!(", stringify!($Ty), "::MAX.get(), ", stringify!($Int), "::MAX);")]
                 /// ```
-                #[unstable(feature = "nonzero_min_max", issue = "89065")]
+                #[stable(feature = "nonzero_min_max", since = "CURRENT_RUSTC_VERSION")]
                 pub const MAX: Self = Self::new(<$Int>::MAX).unwrap();
             }
         )+
@@ -1189,12 +1185,10 @@ macro_rules! nonzero_min_max_signed {
                 /// # Examples
                 ///
                 /// ```
-                /// #![feature(nonzero_min_max)]
-                ///
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 #[doc = concat!("assert_eq!(", stringify!($Ty), "::MIN.get(), ", stringify!($Int), "::MIN);")]
                 /// ```
-                #[unstable(feature = "nonzero_min_max", issue = "89065")]
+                #[stable(feature = "nonzero_min_max", since = "CURRENT_RUSTC_VERSION")]
                 pub const MIN: Self = Self::new(<$Int>::MIN).unwrap();
 
                 /// The largest value that can be represented by this non-zero
@@ -1208,12 +1202,10 @@ macro_rules! nonzero_min_max_signed {
                 /// # Examples
                 ///
                 /// ```
-                /// #![feature(nonzero_min_max)]
-                ///
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 #[doc = concat!("assert_eq!(", stringify!($Ty), "::MAX.get(), ", stringify!($Int), "::MAX);")]
                 /// ```
-                #[unstable(feature = "nonzero_min_max", issue = "89065")]
+                #[stable(feature = "nonzero_min_max", since = "CURRENT_RUSTC_VERSION")]
                 pub const MAX: Self = Self::new(<$Int>::MAX).unwrap();
             }
         )+
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index 57e2ffe5d20..839afc57f85 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -61,14 +61,14 @@ impl<T: ?Sized> *const T {
 
     /// Use the pointer value in a new pointer of another type.
     ///
-    /// In case `val` is a (fat) pointer to an unsized type, this operation
+    /// In case `meta` is a (fat) pointer to an unsized type, this operation
     /// will ignore the pointer part, whereas for (thin) pointers to sized
     /// types, this has the same effect as a simple cast.
     ///
     /// The resulting pointer will have provenance of `self`, i.e., for a fat
     /// pointer, this operation is semantically the same as creating a new
     /// fat pointer with the data pointer value of `self` but the metadata of
-    /// `val`.
+    /// `meta`.
     ///
     /// # Examples
     ///
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index 1ad9af1549a..f41be46abc9 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -691,7 +691,7 @@ where
 #[inline(always)]
 #[must_use]
 #[unstable(feature = "ptr_from_ref", issue = "106116")]
-pub fn from_ref<T: ?Sized>(r: &T) -> *const T {
+pub const fn from_ref<T: ?Sized>(r: &T) -> *const T {
     r
 }
 
@@ -702,7 +702,7 @@ pub fn from_ref<T: ?Sized>(r: &T) -> *const T {
 #[inline(always)]
 #[must_use]
 #[unstable(feature = "ptr_from_ref", issue = "106116")]
-pub fn from_mut<T: ?Sized>(r: &mut T) -> *mut T {
+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 422d0f2b8f0..ece5244e9a9 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -60,14 +60,14 @@ impl<T: ?Sized> *mut T {
 
     /// Use the pointer value in a new pointer of another type.
     ///
-    /// In case `val` is a (fat) pointer to an unsized type, this operation
+    /// In case `meta` is a (fat) pointer to an unsized type, this operation
     /// will ignore the pointer part, whereas for (thin) pointers to sized
     /// types, this has the same effect as a simple cast.
     ///
     /// The resulting pointer will have provenance of `self`, i.e., for a fat
     /// pointer, this operation is semantically the same as creating a new
     /// fat pointer with the data pointer value of `self` but the metadata of
-    /// `val`.
+    /// `meta`.
     ///
     /// # Examples
     ///
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index cd6b393a2ea..dbc18f7827e 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -1498,7 +1498,6 @@ impl PathBuf {
     /// # Examples
     ///
     /// ```
-    /// #![feature(path_as_mut_os_str)]
     /// use std::path::{Path, PathBuf};
     ///
     /// let mut path = PathBuf::from("/foo");
@@ -1510,7 +1509,7 @@ impl PathBuf {
     /// path.as_mut_os_string().push("baz");
     /// assert_eq!(path, Path::new("/foo/barbaz"));
     /// ```
-    #[unstable(feature = "path_as_mut_os_str", issue = "105021")]
+    #[stable(feature = "path_as_mut_os_str", since = "CURRENT_RUSTC_VERSION")]
     #[must_use]
     #[inline]
     pub fn as_mut_os_string(&mut self) -> &mut OsString {
@@ -2066,7 +2065,6 @@ impl Path {
     /// # Examples
     ///
     /// ```
-    /// #![feature(path_as_mut_os_str)]
     /// use std::path::{Path, PathBuf};
     ///
     /// let mut path = PathBuf::from("Foo.TXT");
@@ -2076,7 +2074,7 @@ impl Path {
     /// path.as_mut_os_str().make_ascii_lowercase();
     /// assert_eq!(path, Path::new("foo.txt"));
     /// ```
-    #[unstable(feature = "path_as_mut_os_str", issue = "105021")]
+    #[stable(feature = "path_as_mut_os_str", since = "CURRENT_RUSTC_VERSION")]
     #[must_use]
     #[inline]
     pub fn as_mut_os_str(&mut self) -> &mut OsStr {
diff --git a/library/std/src/sys/common/mod.rs b/library/std/src/sys/common/mod.rs
index 29fc0835d76..2b8782ddf44 100644
--- a/library/std/src/sys/common/mod.rs
+++ b/library/std/src/sys/common/mod.rs
@@ -12,6 +12,7 @@
 
 pub mod alloc;
 pub mod small_c_string;
+pub mod thread_local;
 
 #[cfg(test)]
 mod tests;
diff --git a/library/std/src/sys/common/thread_local/fast_local.rs b/library/std/src/sys/common/thread_local/fast_local.rs
new file mode 100644
index 00000000000..2addcc4a759
--- /dev/null
+++ b/library/std/src/sys/common/thread_local/fast_local.rs
@@ -0,0 +1,276 @@
+#[doc(hidden)]
+#[macro_export]
+#[allow_internal_unstable(
+    thread_local_internals,
+    cfg_target_thread_local,
+    thread_local,
+    libstd_thread_internals
+)]
+#[allow_internal_unsafe]
+macro_rules! __thread_local_inner {
+    // used to generate the `LocalKey` value for const-initialized thread locals
+    (@key $t:ty, const $init:expr) => {{
+        #[cfg_attr(not(windows), inline)] // see comments below
+        #[deny(unsafe_op_in_unsafe_fn)]
+        unsafe fn __getit(
+            _init: $crate::option::Option<&mut $crate::option::Option<$t>>,
+        ) -> $crate::option::Option<&'static $t> {
+            const INIT_EXPR: $t = $init;
+            // If the platform has support for `#[thread_local]`, use it.
+            #[thread_local]
+            static mut VAL: $t = INIT_EXPR;
+
+            // If a dtor isn't needed we can do something "very raw" and
+            // just get going.
+            if !$crate::mem::needs_drop::<$t>() {
+                unsafe {
+                    return $crate::option::Option::Some(&VAL)
+                }
+            }
+
+            // 0 == dtor not registered
+            // 1 == dtor registered, dtor not run
+            // 2 == dtor registered and is running or has run
+            #[thread_local]
+            static mut STATE: $crate::primitive::u8 = 0;
+
+            unsafe extern "C" fn destroy(ptr: *mut $crate::primitive::u8) {
+                let ptr = ptr as *mut $t;
+
+                unsafe {
+                    $crate::debug_assert_eq!(STATE, 1);
+                    STATE = 2;
+                    $crate::ptr::drop_in_place(ptr);
+                }
+            }
+
+            unsafe {
+                match STATE {
+                    // 0 == we haven't registered a destructor, so do
+                    //   so now.
+                    0 => {
+                        $crate::thread::__LocalKeyInner::<$t>::register_dtor(
+                            $crate::ptr::addr_of_mut!(VAL) as *mut $crate::primitive::u8,
+                            destroy,
+                        );
+                        STATE = 1;
+                        $crate::option::Option::Some(&VAL)
+                    }
+                    // 1 == the destructor is registered and the value
+                    //   is valid, so return the pointer.
+                    1 => $crate::option::Option::Some(&VAL),
+                    // otherwise the destructor has already run, so we
+                    // can't give access.
+                    _ => $crate::option::Option::None,
+                }
+            }
+        }
+
+        unsafe {
+            $crate::thread::LocalKey::new(__getit)
+        }
+    }};
+
+    // used to generate the `LocalKey` value for `thread_local!`
+    (@key $t:ty, $init:expr) => {
+        {
+            #[inline]
+            fn __init() -> $t { $init }
+
+            // When reading this function you might ask "why is this inlined
+            // everywhere other than Windows?", and that's a very reasonable
+            // question to ask. The short story is that it segfaults rustc if
+            // this function is inlined. The longer story is that Windows looks
+            // to not support `extern` references to thread locals across DLL
+            // boundaries. This appears to at least not be supported in the ABI
+            // that LLVM implements.
+            //
+            // Because of this we never inline on Windows, but we do inline on
+            // other platforms (where external references to thread locals
+            // across DLLs are supported). A better fix for this would be to
+            // inline this function on Windows, but only for "statically linked"
+            // components. For example if two separately compiled rlibs end up
+            // getting linked into a DLL then it's fine to inline this function
+            // across that boundary. It's only not fine to inline this function
+            // across a DLL boundary. Unfortunately rustc doesn't currently
+            // have this sort of logic available in an attribute, and it's not
+            // clear that rustc is even equipped to answer this (it's more of a
+            // Cargo question kinda). This means that, unfortunately, Windows
+            // gets the pessimistic path for now where it's never inlined.
+            //
+            // The issue of "should enable on Windows sometimes" is #84933
+            #[cfg_attr(not(windows), inline)]
+            unsafe fn __getit(
+                init: $crate::option::Option<&mut $crate::option::Option<$t>>,
+            ) -> $crate::option::Option<&'static $t> {
+                #[thread_local]
+                static __KEY: $crate::thread::__LocalKeyInner<$t> =
+                    $crate::thread::__LocalKeyInner::<$t>::new();
+
+                // FIXME: remove the #[allow(...)] marker when macros don't
+                // raise warning for missing/extraneous unsafe blocks anymore.
+                // See https://github.com/rust-lang/rust/issues/74838.
+                #[allow(unused_unsafe)]
+                unsafe {
+                    __KEY.get(move || {
+                        if let $crate::option::Option::Some(init) = init {
+                            if let $crate::option::Option::Some(value) = init.take() {
+                                return value;
+                            } else if $crate::cfg!(debug_assertions) {
+                                $crate::unreachable!("missing default value");
+                            }
+                        }
+                        __init()
+                    })
+                }
+            }
+
+            unsafe {
+                $crate::thread::LocalKey::new(__getit)
+            }
+        }
+    };
+    ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
+        $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
+            $crate::__thread_local_inner!(@key $t, $($init)*);
+    }
+}
+
+#[doc(hidden)]
+pub mod fast {
+    use super::super::lazy::LazyKeyInner;
+    use crate::cell::Cell;
+    use crate::sys::thread_local_dtor::register_dtor;
+    use crate::{fmt, mem, panic};
+
+    #[derive(Copy, Clone)]
+    enum DtorState {
+        Unregistered,
+        Registered,
+        RunningOrHasRun,
+    }
+
+    // This data structure has been carefully constructed so that the fast path
+    // only contains one branch on x86. That optimization is necessary to avoid
+    // duplicated tls lookups on OSX.
+    //
+    // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722
+    pub struct Key<T> {
+        // If `LazyKeyInner::get` returns `None`, that indicates either:
+        //   * The value has never been initialized
+        //   * The value is being recursively initialized
+        //   * The value has already been destroyed or is being destroyed
+        // To determine which kind of `None`, check `dtor_state`.
+        //
+        // This is very optimizer friendly for the fast path - initialized but
+        // not yet dropped.
+        inner: LazyKeyInner<T>,
+
+        // Metadata to keep track of the state of the destructor. Remember that
+        // this variable is thread-local, not global.
+        dtor_state: Cell<DtorState>,
+    }
+
+    impl<T> fmt::Debug for Key<T> {
+        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+            f.debug_struct("Key").finish_non_exhaustive()
+        }
+    }
+
+    impl<T> Key<T> {
+        pub const fn new() -> Key<T> {
+            Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) }
+        }
+
+        // note that this is just a publicly-callable function only for the
+        // const-initialized form of thread locals, basically a way to call the
+        // free `register_dtor` function defined elsewhere in std.
+        pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
+            unsafe {
+                register_dtor(a, dtor);
+            }
+        }
+
+        pub unsafe fn get<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> {
+            // SAFETY: See the definitions of `LazyKeyInner::get` and
+            // `try_initialize` for more information.
+            //
+            // The caller must ensure no mutable references are ever active to
+            // the inner cell or the inner T when this is called.
+            // The `try_initialize` is dependant on the passed `init` function
+            // for this.
+            unsafe {
+                match self.inner.get() {
+                    Some(val) => Some(val),
+                    None => self.try_initialize(init),
+                }
+            }
+        }
+
+        // `try_initialize` is only called once per fast thread local variable,
+        // except in corner cases where thread_local dtors reference other
+        // thread_local's, or it is being recursively initialized.
+        //
+        // Macos: Inlining this function can cause two `tlv_get_addr` calls to
+        // be performed for every call to `Key::get`.
+        // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722
+        #[inline(never)]
+        unsafe fn try_initialize<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> {
+            // SAFETY: See comment above (this function doc).
+            if !mem::needs_drop::<T>() || unsafe { self.try_register_dtor() } {
+                // SAFETY: See comment above (this function doc).
+                Some(unsafe { self.inner.initialize(init) })
+            } else {
+                None
+            }
+        }
+
+        // `try_register_dtor` is only called once per fast thread local
+        // variable, except in corner cases where thread_local dtors reference
+        // other thread_local's, or it is being recursively initialized.
+        unsafe fn try_register_dtor(&self) -> bool {
+            match self.dtor_state.get() {
+                DtorState::Unregistered => {
+                    // SAFETY: dtor registration happens before initialization.
+                    // Passing `self` as a pointer while using `destroy_value<T>`
+                    // is safe because the function will build a pointer to a
+                    // Key<T>, which is the type of self and so find the correct
+                    // size.
+                    unsafe { register_dtor(self as *const _ as *mut u8, destroy_value::<T>) };
+                    self.dtor_state.set(DtorState::Registered);
+                    true
+                }
+                DtorState::Registered => {
+                    // recursively initialized
+                    true
+                }
+                DtorState::RunningOrHasRun => false,
+            }
+        }
+    }
+
+    unsafe extern "C" fn destroy_value<T>(ptr: *mut u8) {
+        let ptr = ptr as *mut Key<T>;
+
+        // SAFETY:
+        //
+        // The pointer `ptr` has been built just above and comes from
+        // `try_register_dtor` where it is originally a Key<T> coming from `self`,
+        // making it non-NUL and of the correct type.
+        //
+        // Right before we run the user destructor be sure to set the
+        // `Option<T>` to `None`, and `dtor_state` to `RunningOrHasRun`. This
+        // causes future calls to `get` to run `try_initialize_drop` again,
+        // which will now fail, and return `None`.
+        //
+        // Wrap the call in a catch to ensure unwinding is caught in the event
+        // a panic takes place in a destructor.
+        if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| unsafe {
+            let value = (*ptr).inner.take();
+            (*ptr).dtor_state.set(DtorState::RunningOrHasRun);
+            drop(value);
+        })) {
+            rtabort!("thread local panicked on drop");
+        }
+    }
+}
diff --git a/library/std/src/sys/common/thread_local/mod.rs b/library/std/src/sys/common/thread_local/mod.rs
new file mode 100644
index 00000000000..1fee84a0434
--- /dev/null
+++ b/library/std/src/sys/common/thread_local/mod.rs
@@ -0,0 +1,109 @@
+//! The following module declarations are outside cfg_if because the internal
+//! `__thread_local_internal` macro does not seem to be exported properly when using cfg_if
+#![unstable(feature = "thread_local_internals", reason = "should not be necessary", issue = "none")]
+
+#[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics")))))]
+mod fast_local;
+#[cfg(all(
+    not(target_thread_local),
+    not(all(target_family = "wasm", not(target_feature = "atomics")))
+))]
+mod os_local;
+#[cfg(all(target_family = "wasm", not(target_feature = "atomics")))]
+mod static_local;
+
+#[cfg(not(test))]
+cfg_if::cfg_if! {
+    if #[cfg(all(target_family = "wasm", not(target_feature = "atomics")))] {
+        #[doc(hidden)]
+        pub use static_local::statik::Key;
+    } else if #[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics")))))] {
+        #[doc(hidden)]
+        pub use fast_local::fast::Key;
+    } else if #[cfg(all(not(target_thread_local), not(all(target_family = "wasm", not(target_feature = "atomics")))))] {
+        #[doc(hidden)]
+        pub use os_local::os::Key;
+    }
+}
+
+#[doc(hidden)]
+#[cfg(test)]
+pub use realstd::thread::__LocalKeyInner as Key;
+
+mod lazy {
+    use crate::cell::UnsafeCell;
+    use crate::hint;
+    use crate::mem;
+
+    pub struct LazyKeyInner<T> {
+        inner: UnsafeCell<Option<T>>,
+    }
+
+    impl<T> LazyKeyInner<T> {
+        pub const fn new() -> LazyKeyInner<T> {
+            LazyKeyInner { inner: UnsafeCell::new(None) }
+        }
+
+        pub unsafe fn get(&self) -> Option<&'static T> {
+            // SAFETY: The caller must ensure no reference is ever handed out to
+            // the inner cell nor mutable reference to the Option<T> inside said
+            // cell. This make it safe to hand a reference, though the lifetime
+            // of 'static is itself unsafe, making the get method unsafe.
+            unsafe { (*self.inner.get()).as_ref() }
+        }
+
+        /// The caller must ensure that no reference is active: this method
+        /// needs unique access.
+        pub unsafe fn initialize<F: FnOnce() -> T>(&self, init: F) -> &'static T {
+            // Execute the initialization up front, *then* move it into our slot,
+            // just in case initialization fails.
+            let value = init();
+            let ptr = self.inner.get();
+
+            // SAFETY:
+            //
+            // note that this can in theory just be `*ptr = Some(value)`, but due to
+            // the compiler will currently codegen that pattern with something like:
+            //
+            //      ptr::drop_in_place(ptr)
+            //      ptr::write(ptr, Some(value))
+            //
+            // Due to this pattern it's possible for the destructor of the value in
+            // `ptr` (e.g., if this is being recursively initialized) to re-access
+            // TLS, in which case there will be a `&` and `&mut` pointer to the same
+            // value (an aliasing violation). To avoid setting the "I'm running a
+            // destructor" flag we just use `mem::replace` which should sequence the
+            // operations a little differently and make this safe to call.
+            //
+            // The precondition also ensures that we are the only one accessing
+            // `self` at the moment so replacing is fine.
+            unsafe {
+                let _ = mem::replace(&mut *ptr, Some(value));
+            }
+
+            // SAFETY: With the call to `mem::replace` it is guaranteed there is
+            // a `Some` behind `ptr`, not a `None` so `unreachable_unchecked`
+            // will never be reached.
+            unsafe {
+                // After storing `Some` we want to get a reference to the contents of
+                // what we just stored. While we could use `unwrap` here and it should
+                // always work it empirically doesn't seem to always get optimized away,
+                // which means that using something like `try_with` can pull in
+                // panicking code and cause a large size bloat.
+                match *ptr {
+                    Some(ref x) => x,
+                    None => hint::unreachable_unchecked(),
+                }
+            }
+        }
+
+        /// The other methods hand out references while taking &self.
+        /// As such, callers of this method must ensure no `&` and `&mut` are
+        /// available and used at the same time.
+        #[allow(unused)]
+        pub unsafe fn take(&mut self) -> Option<T> {
+            // SAFETY: See doc comment for this method.
+            unsafe { (*self.inner.get()).take() }
+        }
+    }
+}
diff --git a/library/std/src/sys/common/thread_local/os_local.rs b/library/std/src/sys/common/thread_local/os_local.rs
new file mode 100644
index 00000000000..6f6560c4aa9
--- /dev/null
+++ b/library/std/src/sys/common/thread_local/os_local.rs
@@ -0,0 +1,217 @@
+#[doc(hidden)]
+#[macro_export]
+#[allow_internal_unstable(
+    thread_local_internals,
+    cfg_target_thread_local,
+    thread_local,
+    libstd_thread_internals
+)]
+#[allow_internal_unsafe]
+macro_rules! __thread_local_inner {
+    // used to generate the `LocalKey` value for const-initialized thread locals
+    (@key $t:ty, const $init:expr) => {{
+        #[cfg_attr(not(windows), inline)] // see comments below
+        #[deny(unsafe_op_in_unsafe_fn)]
+        unsafe fn __getit(
+            _init: $crate::option::Option<&mut $crate::option::Option<$t>>,
+        ) -> $crate::option::Option<&'static $t> {
+            const INIT_EXPR: $t = $init;
+
+                        // On platforms without `#[thread_local]` we fall back to the
+            // same implementation as below for os thread locals.
+            #[inline]
+            const fn __init() -> $t { INIT_EXPR }
+            static __KEY: $crate::thread::__LocalKeyInner<$t> =
+                $crate::thread::__LocalKeyInner::new();
+            #[allow(unused_unsafe)]
+            unsafe {
+                __KEY.get(move || {
+                    if let $crate::option::Option::Some(init) = _init {
+                        if let $crate::option::Option::Some(value) = init.take() {
+                            return value;
+                        } else if $crate::cfg!(debug_assertions) {
+                            $crate::unreachable!("missing initial value");
+                        }
+                    }
+                    __init()
+                })
+            }
+        }
+
+        unsafe {
+            $crate::thread::LocalKey::new(__getit)
+        }
+    }};
+
+    // used to generate the `LocalKey` value for `thread_local!`
+    (@key $t:ty, $init:expr) => {
+        {
+            #[inline]
+            fn __init() -> $t { $init }
+
+            // When reading this function you might ask "why is this inlined
+            // everywhere other than Windows?", and that's a very reasonable
+            // question to ask. The short story is that it segfaults rustc if
+            // this function is inlined. The longer story is that Windows looks
+            // to not support `extern` references to thread locals across DLL
+            // boundaries. This appears to at least not be supported in the ABI
+            // that LLVM implements.
+            //
+            // Because of this we never inline on Windows, but we do inline on
+            // other platforms (where external references to thread locals
+            // across DLLs are supported). A better fix for this would be to
+            // inline this function on Windows, but only for "statically linked"
+            // components. For example if two separately compiled rlibs end up
+            // getting linked into a DLL then it's fine to inline this function
+            // across that boundary. It's only not fine to inline this function
+            // across a DLL boundary. Unfortunately rustc doesn't currently
+            // have this sort of logic available in an attribute, and it's not
+            // clear that rustc is even equipped to answer this (it's more of a
+            // Cargo question kinda). This means that, unfortunately, Windows
+            // gets the pessimistic path for now where it's never inlined.
+            //
+            // The issue of "should enable on Windows sometimes" is #84933
+            #[cfg_attr(not(windows), inline)]
+            unsafe fn __getit(
+                init: $crate::option::Option<&mut $crate::option::Option<$t>>,
+            ) -> $crate::option::Option<&'static $t> {
+                static __KEY: $crate::thread::__LocalKeyInner<$t> =
+                    $crate::thread::__LocalKeyInner::new();
+
+                // FIXME: remove the #[allow(...)] marker when macros don't
+                // raise warning for missing/extraneous unsafe blocks anymore.
+                // See https://github.com/rust-lang/rust/issues/74838.
+                #[allow(unused_unsafe)]
+                unsafe {
+                    __KEY.get(move || {
+                        if let $crate::option::Option::Some(init) = init {
+                            if let $crate::option::Option::Some(value) = init.take() {
+                                return value;
+                            } else if $crate::cfg!(debug_assertions) {
+                                $crate::unreachable!("missing default value");
+                            }
+                        }
+                        __init()
+                    })
+                }
+            }
+
+            unsafe {
+                $crate::thread::LocalKey::new(__getit)
+            }
+        }
+    };
+    ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
+        $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
+            $crate::__thread_local_inner!(@key $t, $($init)*);
+    }
+}
+
+#[doc(hidden)]
+pub mod os {
+    use super::super::lazy::LazyKeyInner;
+    use crate::cell::Cell;
+    use crate::sys_common::thread_local_key::StaticKey as OsStaticKey;
+    use crate::{fmt, marker, panic, ptr};
+
+    /// Use a regular global static to store this key; the state provided will then be
+    /// thread-local.
+    pub struct Key<T> {
+        // OS-TLS key that we'll use to key off.
+        os: OsStaticKey,
+        marker: marker::PhantomData<Cell<T>>,
+    }
+
+    impl<T> fmt::Debug for Key<T> {
+        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+            f.debug_struct("Key").finish_non_exhaustive()
+        }
+    }
+
+    unsafe impl<T> Sync for Key<T> {}
+
+    struct Value<T: 'static> {
+        inner: LazyKeyInner<T>,
+        key: &'static Key<T>,
+    }
+
+    impl<T: 'static> Key<T> {
+        #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")]
+        pub const fn new() -> Key<T> {
+            Key { os: OsStaticKey::new(Some(destroy_value::<T>)), marker: marker::PhantomData }
+        }
+
+        /// It is a requirement for the caller to ensure that no mutable
+        /// reference is active when this method is called.
+        pub unsafe fn get(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> {
+            // SAFETY: See the documentation for this method.
+            let ptr = unsafe { self.os.get() as *mut Value<T> };
+            if ptr.addr() > 1 {
+                // SAFETY: the check ensured the pointer is safe (its destructor
+                // is not running) + it is coming from a trusted source (self).
+                if let Some(ref value) = unsafe { (*ptr).inner.get() } {
+                    return Some(value);
+                }
+            }
+            // SAFETY: At this point we are sure we have no value and so
+            // initializing (or trying to) is safe.
+            unsafe { self.try_initialize(init) }
+        }
+
+        // `try_initialize` is only called once per os thread local variable,
+        // except in corner cases where thread_local dtors reference other
+        // thread_local's, or it is being recursively initialized.
+        unsafe fn try_initialize(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> {
+            // SAFETY: No mutable references are ever handed out meaning getting
+            // the value is ok.
+            let ptr = unsafe { self.os.get() as *mut Value<T> };
+            if ptr.addr() == 1 {
+                // destructor is running
+                return None;
+            }
+
+            let ptr = if ptr.is_null() {
+                // If the lookup returned null, we haven't initialized our own
+                // local copy, so do that now.
+                let ptr = Box::into_raw(Box::new(Value { inner: LazyKeyInner::new(), key: self }));
+                // SAFETY: At this point we are sure there is no value inside
+                // ptr so setting it will not affect anyone else.
+                unsafe {
+                    self.os.set(ptr as *mut u8);
+                }
+                ptr
+            } else {
+                // recursive initialization
+                ptr
+            };
+
+            // SAFETY: ptr has been ensured as non-NUL just above an so can be
+            // dereferenced safely.
+            unsafe { Some((*ptr).inner.initialize(init)) }
+        }
+    }
+
+    unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) {
+        // SAFETY:
+        //
+        // The OS TLS ensures that this key contains a null value when this
+        // destructor starts to run. We set it back to a sentinel value of 1 to
+        // ensure that any future calls to `get` for this thread will return
+        // `None`.
+        //
+        // Note that to prevent an infinite loop we reset it back to null right
+        // before we return from the destructor ourselves.
+        //
+        // Wrap the call in a catch to ensure unwinding is caught in the event
+        // a panic takes place in a destructor.
+        if let Err(_) = panic::catch_unwind(|| unsafe {
+            let ptr = Box::from_raw(ptr as *mut Value<T>);
+            let key = ptr.key;
+            key.os.set(ptr::invalid_mut(1));
+            drop(ptr);
+            key.os.set(ptr::null_mut());
+        }) {
+            rtabort!("thread local panicked on drop");
+        }
+    }
+}
diff --git a/library/std/src/sys/common/thread_local/static_local.rs b/library/std/src/sys/common/thread_local/static_local.rs
new file mode 100644
index 00000000000..ec4f2a12b7e
--- /dev/null
+++ b/library/std/src/sys/common/thread_local/static_local.rs
@@ -0,0 +1,115 @@
+#[doc(hidden)]
+#[macro_export]
+#[allow_internal_unstable(
+    thread_local_internals,
+    cfg_target_thread_local,
+    thread_local,
+    libstd_thread_internals
+)]
+#[allow_internal_unsafe]
+macro_rules! __thread_local_inner {
+    // used to generate the `LocalKey` value for const-initialized thread locals
+    (@key $t:ty, const $init:expr) => {{
+        #[inline] // see comments below
+        #[deny(unsafe_op_in_unsafe_fn)]
+        unsafe fn __getit(
+            _init: $crate::option::Option<&mut $crate::option::Option<$t>>,
+        ) -> $crate::option::Option<&'static $t> {
+            const INIT_EXPR: $t = $init;
+
+            // wasm without atomics maps directly to `static mut`, and dtors
+            // aren't implemented because thread dtors aren't really a thing
+            // on wasm right now
+            //
+            // FIXME(#84224) this should come after the `target_thread_local`
+            // block.
+            static mut VAL: $t = INIT_EXPR;
+            unsafe { $crate::option::Option::Some(&VAL) }
+        }
+
+        unsafe {
+            $crate::thread::LocalKey::new(__getit)
+        }
+    }};
+
+    // used to generate the `LocalKey` value for `thread_local!`
+    (@key $t:ty, $init:expr) => {
+        {
+            #[inline]
+            fn __init() -> $t { $init }
+            #[inline]
+            unsafe fn __getit(
+                init: $crate::option::Option<&mut $crate::option::Option<$t>>,
+            ) -> $crate::option::Option<&'static $t> {
+                static __KEY: $crate::thread::__LocalKeyInner<$t> =
+                    $crate::thread::__LocalKeyInner::new();
+
+                // FIXME: remove the #[allow(...)] marker when macros don't
+                // raise warning for missing/extraneous unsafe blocks anymore.
+                // See https://github.com/rust-lang/rust/issues/74838.
+                #[allow(unused_unsafe)]
+                unsafe {
+                    __KEY.get(move || {
+                        if let $crate::option::Option::Some(init) = init {
+                            if let $crate::option::Option::Some(value) = init.take() {
+                                return value;
+                            } else if $crate::cfg!(debug_assertions) {
+                                $crate::unreachable!("missing default value");
+                            }
+                        }
+                        __init()
+                    })
+                }
+            }
+
+            unsafe {
+                $crate::thread::LocalKey::new(__getit)
+            }
+        }
+    };
+    ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
+        $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
+            $crate::__thread_local_inner!(@key $t, $($init)*);
+    }
+}
+
+/// On some targets like wasm there's no threads, so no need to generate
+/// thread locals and we can instead just use plain statics!
+#[doc(hidden)]
+pub mod statik {
+    use super::super::lazy::LazyKeyInner;
+    use crate::fmt;
+
+    pub struct Key<T> {
+        inner: LazyKeyInner<T>,
+    }
+
+    unsafe impl<T> Sync for Key<T> {}
+
+    impl<T> fmt::Debug for Key<T> {
+        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+            f.debug_struct("Key").finish_non_exhaustive()
+        }
+    }
+
+    impl<T> Key<T> {
+        pub const fn new() -> Key<T> {
+            Key { inner: LazyKeyInner::new() }
+        }
+
+        pub unsafe fn get(&self, init: impl FnOnce() -> T) -> Option<&'static T> {
+            // SAFETY: The caller must ensure no reference is ever handed out to
+            // the inner cell nor mutable reference to the Option<T> inside said
+            // cell. This make it safe to hand a reference, though the lifetime
+            // of 'static is itself unsafe, making the get method unsafe.
+            let value = unsafe {
+                match self.inner.get() {
+                    Some(ref value) => value,
+                    None => self.inner.initialize(init),
+                }
+            };
+
+            Some(value)
+        }
+    }
+}
diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs
index cf7c2e05a2e..7fdf03acc14 100644
--- a/library/std/src/thread/local.rs
+++ b/library/std/src/thread/local.rs
@@ -173,200 +173,6 @@ macro_rules! thread_local {
     );
 }
 
-#[doc(hidden)]
-#[unstable(feature = "thread_local_internals", reason = "should not be necessary", issue = "none")]
-#[macro_export]
-#[allow_internal_unstable(thread_local_internals, cfg_target_thread_local, thread_local)]
-#[allow_internal_unsafe]
-macro_rules! __thread_local_inner {
-    // used to generate the `LocalKey` value for const-initialized thread locals
-    (@key $t:ty, const $init:expr) => {{
-        #[cfg_attr(not(windows), inline)] // see comments below
-        #[deny(unsafe_op_in_unsafe_fn)]
-        unsafe fn __getit(
-            _init: $crate::option::Option<&mut $crate::option::Option<$t>>,
-        ) -> $crate::option::Option<&'static $t> {
-            const INIT_EXPR: $t = $init;
-
-            // wasm without atomics maps directly to `static mut`, and dtors
-            // aren't implemented because thread dtors aren't really a thing
-            // on wasm right now
-            //
-            // FIXME(#84224) this should come after the `target_thread_local`
-            // block.
-            #[cfg(all(target_family = "wasm", not(target_feature = "atomics")))]
-            {
-                static mut VAL: $t = INIT_EXPR;
-                unsafe { $crate::option::Option::Some(&VAL) }
-            }
-
-            // If the platform has support for `#[thread_local]`, use it.
-            #[cfg(all(
-                target_thread_local,
-                not(all(target_family = "wasm", not(target_feature = "atomics"))),
-            ))]
-            {
-                #[thread_local]
-                static mut VAL: $t = INIT_EXPR;
-
-                // If a dtor isn't needed we can do something "very raw" and
-                // just get going.
-                if !$crate::mem::needs_drop::<$t>() {
-                    unsafe {
-                        return $crate::option::Option::Some(&VAL)
-                    }
-                }
-
-                // 0 == dtor not registered
-                // 1 == dtor registered, dtor not run
-                // 2 == dtor registered and is running or has run
-                #[thread_local]
-                static mut STATE: $crate::primitive::u8 = 0;
-
-                unsafe extern "C" fn destroy(ptr: *mut $crate::primitive::u8) {
-                    let ptr = ptr as *mut $t;
-
-                    unsafe {
-                        $crate::debug_assert_eq!(STATE, 1);
-                        STATE = 2;
-                        $crate::ptr::drop_in_place(ptr);
-                    }
-                }
-
-                unsafe {
-                    match STATE {
-                        // 0 == we haven't registered a destructor, so do
-                        //   so now.
-                        0 => {
-                            $crate::thread::__FastLocalKeyInner::<$t>::register_dtor(
-                                $crate::ptr::addr_of_mut!(VAL) as *mut $crate::primitive::u8,
-                                destroy,
-                            );
-                            STATE = 1;
-                            $crate::option::Option::Some(&VAL)
-                        }
-                        // 1 == the destructor is registered and the value
-                        //   is valid, so return the pointer.
-                        1 => $crate::option::Option::Some(&VAL),
-                        // otherwise the destructor has already run, so we
-                        // can't give access.
-                        _ => $crate::option::Option::None,
-                    }
-                }
-            }
-
-            // On platforms without `#[thread_local]` we fall back to the
-            // same implementation as below for os thread locals.
-            #[cfg(all(
-                not(target_thread_local),
-                not(all(target_family = "wasm", not(target_feature = "atomics"))),
-            ))]
-            {
-                #[inline]
-                const fn __init() -> $t { INIT_EXPR }
-                static __KEY: $crate::thread::__OsLocalKeyInner<$t> =
-                    $crate::thread::__OsLocalKeyInner::new();
-                #[allow(unused_unsafe)]
-                unsafe {
-                    __KEY.get(move || {
-                        if let $crate::option::Option::Some(init) = _init {
-                            if let $crate::option::Option::Some(value) = init.take() {
-                                return value;
-                            } else if $crate::cfg!(debug_assertions) {
-                                $crate::unreachable!("missing initial value");
-                            }
-                        }
-                        __init()
-                    })
-                }
-            }
-        }
-
-        unsafe {
-            $crate::thread::LocalKey::new(__getit)
-        }
-    }};
-
-    // used to generate the `LocalKey` value for `thread_local!`
-    (@key $t:ty, $init:expr) => {
-        {
-            #[inline]
-            fn __init() -> $t { $init }
-
-            // When reading this function you might ask "why is this inlined
-            // everywhere other than Windows?", and that's a very reasonable
-            // question to ask. The short story is that it segfaults rustc if
-            // this function is inlined. The longer story is that Windows looks
-            // to not support `extern` references to thread locals across DLL
-            // boundaries. This appears to at least not be supported in the ABI
-            // that LLVM implements.
-            //
-            // Because of this we never inline on Windows, but we do inline on
-            // other platforms (where external references to thread locals
-            // across DLLs are supported). A better fix for this would be to
-            // inline this function on Windows, but only for "statically linked"
-            // components. For example if two separately compiled rlibs end up
-            // getting linked into a DLL then it's fine to inline this function
-            // across that boundary. It's only not fine to inline this function
-            // across a DLL boundary. Unfortunately rustc doesn't currently
-            // have this sort of logic available in an attribute, and it's not
-            // clear that rustc is even equipped to answer this (it's more of a
-            // Cargo question kinda). This means that, unfortunately, Windows
-            // gets the pessimistic path for now where it's never inlined.
-            //
-            // The issue of "should enable on Windows sometimes" is #84933
-            #[cfg_attr(not(windows), inline)]
-            unsafe fn __getit(
-                init: $crate::option::Option<&mut $crate::option::Option<$t>>,
-            ) -> $crate::option::Option<&'static $t> {
-                #[cfg(all(target_family = "wasm", not(target_feature = "atomics")))]
-                static __KEY: $crate::thread::__StaticLocalKeyInner<$t> =
-                    $crate::thread::__StaticLocalKeyInner::new();
-
-                #[thread_local]
-                #[cfg(all(
-                    target_thread_local,
-                    not(all(target_family = "wasm", not(target_feature = "atomics"))),
-                ))]
-                static __KEY: $crate::thread::__FastLocalKeyInner<$t> =
-                    $crate::thread::__FastLocalKeyInner::new();
-
-                #[cfg(all(
-                    not(target_thread_local),
-                    not(all(target_family = "wasm", not(target_feature = "atomics"))),
-                ))]
-                static __KEY: $crate::thread::__OsLocalKeyInner<$t> =
-                    $crate::thread::__OsLocalKeyInner::new();
-
-                // FIXME: remove the #[allow(...)] marker when macros don't
-                // raise warning for missing/extraneous unsafe blocks anymore.
-                // See https://github.com/rust-lang/rust/issues/74838.
-                #[allow(unused_unsafe)]
-                unsafe {
-                    __KEY.get(move || {
-                        if let $crate::option::Option::Some(init) = init {
-                            if let $crate::option::Option::Some(value) = init.take() {
-                                return value;
-                            } else if $crate::cfg!(debug_assertions) {
-                                $crate::unreachable!("missing default value");
-                            }
-                        }
-                        __init()
-                    })
-                }
-            }
-
-            unsafe {
-                $crate::thread::LocalKey::new(__getit)
-            }
-        }
-    };
-    ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
-        $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
-            $crate::__thread_local_inner!(@key $t, $($init)*);
-    }
-}
-
 /// An error returned by [`LocalKey::try_with`](struct.LocalKey.html#method.try_with).
 #[stable(feature = "thread_local_try_with", since = "1.26.0")]
 #[non_exhaustive]
@@ -779,376 +585,3 @@ impl<T: 'static> LocalKey<RefCell<T>> {
         self.with(|cell| cell.replace(value))
     }
 }
-
-mod lazy {
-    use crate::cell::UnsafeCell;
-    use crate::hint;
-    use crate::mem;
-
-    pub struct LazyKeyInner<T> {
-        inner: UnsafeCell<Option<T>>,
-    }
-
-    impl<T> LazyKeyInner<T> {
-        pub const fn new() -> LazyKeyInner<T> {
-            LazyKeyInner { inner: UnsafeCell::new(None) }
-        }
-
-        pub unsafe fn get(&self) -> Option<&'static T> {
-            // SAFETY: The caller must ensure no reference is ever handed out to
-            // the inner cell nor mutable reference to the Option<T> inside said
-            // cell. This make it safe to hand a reference, though the lifetime
-            // of 'static is itself unsafe, making the get method unsafe.
-            unsafe { (*self.inner.get()).as_ref() }
-        }
-
-        /// The caller must ensure that no reference is active: this method
-        /// needs unique access.
-        pub unsafe fn initialize<F: FnOnce() -> T>(&self, init: F) -> &'static T {
-            // Execute the initialization up front, *then* move it into our slot,
-            // just in case initialization fails.
-            let value = init();
-            let ptr = self.inner.get();
-
-            // SAFETY:
-            //
-            // note that this can in theory just be `*ptr = Some(value)`, but due to
-            // the compiler will currently codegen that pattern with something like:
-            //
-            //      ptr::drop_in_place(ptr)
-            //      ptr::write(ptr, Some(value))
-            //
-            // Due to this pattern it's possible for the destructor of the value in
-            // `ptr` (e.g., if this is being recursively initialized) to re-access
-            // TLS, in which case there will be a `&` and `&mut` pointer to the same
-            // value (an aliasing violation). To avoid setting the "I'm running a
-            // destructor" flag we just use `mem::replace` which should sequence the
-            // operations a little differently and make this safe to call.
-            //
-            // The precondition also ensures that we are the only one accessing
-            // `self` at the moment so replacing is fine.
-            unsafe {
-                let _ = mem::replace(&mut *ptr, Some(value));
-            }
-
-            // SAFETY: With the call to `mem::replace` it is guaranteed there is
-            // a `Some` behind `ptr`, not a `None` so `unreachable_unchecked`
-            // will never be reached.
-            unsafe {
-                // After storing `Some` we want to get a reference to the contents of
-                // what we just stored. While we could use `unwrap` here and it should
-                // always work it empirically doesn't seem to always get optimized away,
-                // which means that using something like `try_with` can pull in
-                // panicking code and cause a large size bloat.
-                match *ptr {
-                    Some(ref x) => x,
-                    None => hint::unreachable_unchecked(),
-                }
-            }
-        }
-
-        /// The other methods hand out references while taking &self.
-        /// As such, callers of this method must ensure no `&` and `&mut` are
-        /// available and used at the same time.
-        #[allow(unused)]
-        pub unsafe fn take(&mut self) -> Option<T> {
-            // SAFETY: See doc comment for this method.
-            unsafe { (*self.inner.get()).take() }
-        }
-    }
-}
-
-/// On some targets like wasm there's no threads, so no need to generate
-/// thread locals and we can instead just use plain statics!
-#[doc(hidden)]
-#[cfg(all(target_family = "wasm", not(target_feature = "atomics")))]
-pub mod statik {
-    use super::lazy::LazyKeyInner;
-    use crate::fmt;
-
-    pub struct Key<T> {
-        inner: LazyKeyInner<T>,
-    }
-
-    unsafe impl<T> Sync for Key<T> {}
-
-    impl<T> fmt::Debug for Key<T> {
-        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-            f.debug_struct("Key").finish_non_exhaustive()
-        }
-    }
-
-    impl<T> Key<T> {
-        pub const fn new() -> Key<T> {
-            Key { inner: LazyKeyInner::new() }
-        }
-
-        pub unsafe fn get(&self, init: impl FnOnce() -> T) -> Option<&'static T> {
-            // SAFETY: The caller must ensure no reference is ever handed out to
-            // the inner cell nor mutable reference to the Option<T> inside said
-            // cell. This make it safe to hand a reference, though the lifetime
-            // of 'static is itself unsafe, making the get method unsafe.
-            let value = unsafe {
-                match self.inner.get() {
-                    Some(ref value) => value,
-                    None => self.inner.initialize(init),
-                }
-            };
-
-            Some(value)
-        }
-    }
-}
-
-#[doc(hidden)]
-#[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics"))),))]
-pub mod fast {
-    use super::lazy::LazyKeyInner;
-    use crate::cell::Cell;
-    use crate::sys::thread_local_dtor::register_dtor;
-    use crate::{fmt, mem, panic};
-
-    #[derive(Copy, Clone)]
-    enum DtorState {
-        Unregistered,
-        Registered,
-        RunningOrHasRun,
-    }
-
-    // This data structure has been carefully constructed so that the fast path
-    // only contains one branch on x86. That optimization is necessary to avoid
-    // duplicated tls lookups on OSX.
-    //
-    // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722
-    pub struct Key<T> {
-        // If `LazyKeyInner::get` returns `None`, that indicates either:
-        //   * The value has never been initialized
-        //   * The value is being recursively initialized
-        //   * The value has already been destroyed or is being destroyed
-        // To determine which kind of `None`, check `dtor_state`.
-        //
-        // This is very optimizer friendly for the fast path - initialized but
-        // not yet dropped.
-        inner: LazyKeyInner<T>,
-
-        // Metadata to keep track of the state of the destructor. Remember that
-        // this variable is thread-local, not global.
-        dtor_state: Cell<DtorState>,
-    }
-
-    impl<T> fmt::Debug for Key<T> {
-        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-            f.debug_struct("Key").finish_non_exhaustive()
-        }
-    }
-
-    impl<T> Key<T> {
-        pub const fn new() -> Key<T> {
-            Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) }
-        }
-
-        // note that this is just a publicly-callable function only for the
-        // const-initialized form of thread locals, basically a way to call the
-        // free `register_dtor` function defined elsewhere in std.
-        pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
-            unsafe {
-                register_dtor(a, dtor);
-            }
-        }
-
-        pub unsafe fn get<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> {
-            // SAFETY: See the definitions of `LazyKeyInner::get` and
-            // `try_initialize` for more information.
-            //
-            // The caller must ensure no mutable references are ever active to
-            // the inner cell or the inner T when this is called.
-            // The `try_initialize` is dependant on the passed `init` function
-            // for this.
-            unsafe {
-                match self.inner.get() {
-                    Some(val) => Some(val),
-                    None => self.try_initialize(init),
-                }
-            }
-        }
-
-        // `try_initialize` is only called once per fast thread local variable,
-        // except in corner cases where thread_local dtors reference other
-        // thread_local's, or it is being recursively initialized.
-        //
-        // Macos: Inlining this function can cause two `tlv_get_addr` calls to
-        // be performed for every call to `Key::get`.
-        // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722
-        #[inline(never)]
-        unsafe fn try_initialize<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> {
-            // SAFETY: See comment above (this function doc).
-            if !mem::needs_drop::<T>() || unsafe { self.try_register_dtor() } {
-                // SAFETY: See comment above (this function doc).
-                Some(unsafe { self.inner.initialize(init) })
-            } else {
-                None
-            }
-        }
-
-        // `try_register_dtor` is only called once per fast thread local
-        // variable, except in corner cases where thread_local dtors reference
-        // other thread_local's, or it is being recursively initialized.
-        unsafe fn try_register_dtor(&self) -> bool {
-            match self.dtor_state.get() {
-                DtorState::Unregistered => {
-                    // SAFETY: dtor registration happens before initialization.
-                    // Passing `self` as a pointer while using `destroy_value<T>`
-                    // is safe because the function will build a pointer to a
-                    // Key<T>, which is the type of self and so find the correct
-                    // size.
-                    unsafe { register_dtor(self as *const _ as *mut u8, destroy_value::<T>) };
-                    self.dtor_state.set(DtorState::Registered);
-                    true
-                }
-                DtorState::Registered => {
-                    // recursively initialized
-                    true
-                }
-                DtorState::RunningOrHasRun => false,
-            }
-        }
-    }
-
-    unsafe extern "C" fn destroy_value<T>(ptr: *mut u8) {
-        let ptr = ptr as *mut Key<T>;
-
-        // SAFETY:
-        //
-        // The pointer `ptr` has been built just above and comes from
-        // `try_register_dtor` where it is originally a Key<T> coming from `self`,
-        // making it non-NUL and of the correct type.
-        //
-        // Right before we run the user destructor be sure to set the
-        // `Option<T>` to `None`, and `dtor_state` to `RunningOrHasRun`. This
-        // causes future calls to `get` to run `try_initialize_drop` again,
-        // which will now fail, and return `None`.
-        //
-        // Wrap the call in a catch to ensure unwinding is caught in the event
-        // a panic takes place in a destructor.
-        if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| unsafe {
-            let value = (*ptr).inner.take();
-            (*ptr).dtor_state.set(DtorState::RunningOrHasRun);
-            drop(value);
-        })) {
-            rtabort!("thread local panicked on drop");
-        }
-    }
-}
-
-#[doc(hidden)]
-#[cfg(all(
-    not(target_thread_local),
-    not(all(target_family = "wasm", not(target_feature = "atomics"))),
-))]
-pub mod os {
-    use super::lazy::LazyKeyInner;
-    use crate::cell::Cell;
-    use crate::sys_common::thread_local_key::StaticKey as OsStaticKey;
-    use crate::{fmt, marker, panic, ptr};
-
-    /// Use a regular global static to store this key; the state provided will then be
-    /// thread-local.
-    pub struct Key<T> {
-        // OS-TLS key that we'll use to key off.
-        os: OsStaticKey,
-        marker: marker::PhantomData<Cell<T>>,
-    }
-
-    impl<T> fmt::Debug for Key<T> {
-        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-            f.debug_struct("Key").finish_non_exhaustive()
-        }
-    }
-
-    unsafe impl<T> Sync for Key<T> {}
-
-    struct Value<T: 'static> {
-        inner: LazyKeyInner<T>,
-        key: &'static Key<T>,
-    }
-
-    impl<T: 'static> Key<T> {
-        #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")]
-        pub const fn new() -> Key<T> {
-            Key { os: OsStaticKey::new(Some(destroy_value::<T>)), marker: marker::PhantomData }
-        }
-
-        /// It is a requirement for the caller to ensure that no mutable
-        /// reference is active when this method is called.
-        pub unsafe fn get(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> {
-            // SAFETY: See the documentation for this method.
-            let ptr = unsafe { self.os.get() as *mut Value<T> };
-            if ptr.addr() > 1 {
-                // SAFETY: the check ensured the pointer is safe (its destructor
-                // is not running) + it is coming from a trusted source (self).
-                if let Some(ref value) = unsafe { (*ptr).inner.get() } {
-                    return Some(value);
-                }
-            }
-            // SAFETY: At this point we are sure we have no value and so
-            // initializing (or trying to) is safe.
-            unsafe { self.try_initialize(init) }
-        }
-
-        // `try_initialize` is only called once per os thread local variable,
-        // except in corner cases where thread_local dtors reference other
-        // thread_local's, or it is being recursively initialized.
-        unsafe fn try_initialize(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> {
-            // SAFETY: No mutable references are ever handed out meaning getting
-            // the value is ok.
-            let ptr = unsafe { self.os.get() as *mut Value<T> };
-            if ptr.addr() == 1 {
-                // destructor is running
-                return None;
-            }
-
-            let ptr = if ptr.is_null() {
-                // If the lookup returned null, we haven't initialized our own
-                // local copy, so do that now.
-                let ptr = Box::into_raw(Box::new(Value { inner: LazyKeyInner::new(), key: self }));
-                // SAFETY: At this point we are sure there is no value inside
-                // ptr so setting it will not affect anyone else.
-                unsafe {
-                    self.os.set(ptr as *mut u8);
-                }
-                ptr
-            } else {
-                // recursive initialization
-                ptr
-            };
-
-            // SAFETY: ptr has been ensured as non-NUL just above an so can be
-            // dereferenced safely.
-            unsafe { Some((*ptr).inner.initialize(init)) }
-        }
-    }
-
-    unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) {
-        // SAFETY:
-        //
-        // The OS TLS ensures that this key contains a null value when this
-        // destructor starts to run. We set it back to a sentinel value of 1 to
-        // ensure that any future calls to `get` for this thread will return
-        // `None`.
-        //
-        // Note that to prevent an infinite loop we reset it back to null right
-        // before we return from the destructor ourselves.
-        //
-        // Wrap the call in a catch to ensure unwinding is caught in the event
-        // a panic takes place in a destructor.
-        if let Err(_) = panic::catch_unwind(|| unsafe {
-            let ptr = Box::from_raw(ptr as *mut Value<T>);
-            let key = ptr.key;
-            key.os.set(ptr::invalid_mut(1));
-            drop(ptr);
-            key.os.set(ptr::null_mut());
-        }) {
-            rtabort!("thread local panicked on drop");
-        }
-    }
-}
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 489af776798..b9aaf5f6e15 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -203,44 +203,9 @@ pub use self::local::{AccessError, LocalKey};
 // by the elf linker. "static" is for single-threaded platforms where a global
 // static is sufficient.
 
-#[unstable(feature = "libstd_thread_internals", issue = "none")]
-#[cfg(not(test))]
-#[cfg(all(
-    target_thread_local,
-    not(all(target_family = "wasm", not(target_feature = "atomics"))),
-))]
-#[doc(hidden)]
-pub use self::local::fast::Key as __FastLocalKeyInner;
-// when building for tests, use real std's type
-#[unstable(feature = "libstd_thread_internals", issue = "none")]
-#[cfg(test)]
-#[cfg(all(
-    target_thread_local,
-    not(all(target_family = "wasm", not(target_feature = "atomics"))),
-))]
-pub use realstd::thread::__FastLocalKeyInner;
-
-#[unstable(feature = "libstd_thread_internals", issue = "none")]
-#[cfg(not(test))]
-#[cfg(all(
-    not(target_thread_local),
-    not(all(target_family = "wasm", not(target_feature = "atomics"))),
-))]
 #[doc(hidden)]
-pub use self::local::os::Key as __OsLocalKeyInner;
-// when building for tests, use real std's type
-#[unstable(feature = "libstd_thread_internals", issue = "none")]
-#[cfg(test)]
-#[cfg(all(
-    not(target_thread_local),
-    not(all(target_family = "wasm", not(target_feature = "atomics"))),
-))]
-pub use realstd::thread::__OsLocalKeyInner;
-
 #[unstable(feature = "libstd_thread_internals", issue = "none")]
-#[cfg(all(target_family = "wasm", not(target_feature = "atomics")))]
-#[doc(hidden)]
-pub use self::local::statik::Key as __StaticLocalKeyInner;
+pub use crate::sys::common::thread_local::Key as __LocalKeyInner;
 
 ////////////////////////////////////////////////////////////////////////////////
 // Builder
diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md
index 71eee8968e2..253d504d7bd 100644
--- a/src/bootstrap/README.md
+++ b/src/bootstrap/README.md
@@ -185,7 +185,7 @@ Some general areas that you may be interested in modifying are:
 If you make a major change, please remember to:
 
 + Update `VERSION` in `src/bootstrap/main.rs`.
-* Update `changelog-seen = N` in `config.toml.example`.
+* Update `changelog-seen = N` in `config.example.toml`.
 * Add an entry in `src/bootstrap/CHANGELOG.md`.
 
 A 'major change' includes
diff --git a/src/bootstrap/bin/main.rs b/src/bootstrap/bin/main.rs
index 3856bb64fb3..b345bf9fb83 100644
--- a/src/bootstrap/bin/main.rs
+++ b/src/bootstrap/bin/main.rs
@@ -44,8 +44,8 @@ fn main() {
     if suggest_setup {
         println!("warning: you have not made a `config.toml`");
         println!(
-            "help: consider running `./x.py setup` or copying `config.toml.example` by running \
-            `cp config.toml.example config.toml`"
+            "help: consider running `./x.py setup` or copying `config.example.toml` by running \
+            `cp config.example.toml config.toml`"
         );
     } else if let Some(suggestion) = &changelog_suggestion {
         println!("{}", suggestion);
@@ -57,8 +57,8 @@ fn main() {
     if suggest_setup {
         println!("warning: you have not made a `config.toml`");
         println!(
-            "help: consider running `./x.py setup` or copying `config.toml.example` by running \
-            `cp config.toml.example config.toml`"
+            "help: consider running `./x.py setup` or copying `config.example.toml` by running \
+            `cp config.example.toml config.toml`"
         );
     } else if let Some(suggestion) = &changelog_suggestion {
         println!("{}", suggestion);
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 3d48db8660a..bb07ca1908e 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -711,6 +711,7 @@ impl<'a> Builder<'a> {
                 test::RustdocUi,
                 test::RustdocJson,
                 test::HtmlCheck,
+                test::RustInstaller,
                 // Run bootstrap close to the end as it's unlikely to fail
                 test::Bootstrap,
                 // Run run-make last, since these won't pass without make on Windows
@@ -740,6 +741,7 @@ impl<'a> Builder<'a> {
                 doc::EmbeddedBook,
                 doc::EditionGuide,
                 doc::StyleGuide,
+                doc::Tidy,
             ),
             Kind::Dist => describe!(
                 dist::Docs,
@@ -1720,6 +1722,15 @@ impl<'a> Builder<'a> {
 
         cargo.env("RUSTC_VERBOSE", self.verbosity.to_string());
 
+        // Downstream forks of the Rust compiler might want to use a custom libc to add support for
+        // targets that are not yet available upstream. Adding a patch to replace libc with a
+        // custom one would cause compilation errors though, because Cargo would interpret the
+        // custom libc as part of the workspace, and apply the check-cfg lints on it.
+        //
+        // The libc build script emits check-cfg flags only when this environment variable is set,
+        // so this line allows the use of custom libcs.
+        cargo.env("LIBC_CHECK_CFG", "1");
+
         if source_type == SourceType::InTree {
             let mut lint_flags = Vec::new();
             // When extending this list, add the new lints to the RUSTFLAGS of the
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index 4f417d36511..fc5aa8a245d 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -55,7 +55,7 @@ pub enum DryRun {
 /// Note that this structure is not decoded directly into, but rather it is
 /// filled out from the decoded forms of the structs below. For documentation
 /// each field, see the corresponding fields in
-/// `config.toml.example`.
+/// `config.example.toml`.
 #[derive(Default)]
 #[cfg_attr(test, derive(Clone))]
 pub struct Config {
@@ -325,7 +325,7 @@ impl std::str::FromStr for SplitDebuginfo {
 
 impl SplitDebuginfo {
     /// Returns the default `-Csplit-debuginfo` value for the current target. See the comment for
-    /// `rust.split-debuginfo` in `config.toml.example`.
+    /// `rust.split-debuginfo` in `config.example.toml`.
     fn default_for_platform(target: &str) -> Self {
         if target.contains("apple") {
             SplitDebuginfo::Unpacked
diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py
index 5278f0c10b3..b326ae402aa 100755
--- a/src/bootstrap/configure.py
+++ b/src/bootstrap/configure.py
@@ -194,7 +194,7 @@ if '--help' in sys.argv or '-h' in sys.argv:
     print('')
     print('This configure script is a thin configuration shim over the true')
     print('configuration system, `config.toml`. You can explore the comments')
-    print('in `config.toml.example` next to this configure script to see')
+    print('in `config.example.toml` next to this configure script to see')
     print('more information about what each option is. Additionally you can')
     print('pass `--set` as an argument to set arbitrary key/value pairs')
     print('in the TOML configuration if desired')
@@ -367,7 +367,7 @@ for key in known_args:
 
 set('build.configure-args', sys.argv[1:])
 
-# "Parse" the `config.toml.example` file into the various sections, and we'll
+# "Parse" the `config.example.toml` file into the various sections, and we'll
 # use this as a template of a `config.toml` to write out which preserves
 # all the various comments and whatnot.
 #
@@ -380,7 +380,7 @@ section_order = [None]
 targets = {}
 top_level_keys = []
 
-for line in open(rust_dir + '/config.toml.example').read().split("\n"):
+for line in open(rust_dir + '/config.example.toml').read().split("\n"):
     if cur_section == None:
         if line.count('=') == 1:
             top_level_key = line.split('=')[0]
diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs
index c9384004100..a3d9cb3e10c 100644
--- a/src/bootstrap/dist.rs
+++ b/src/bootstrap/dist.rs
@@ -967,7 +967,7 @@ impl Step for PlainSourceTarball {
             "RELEASES.md",
             "configure",
             "x.py",
-            "config.toml.example",
+            "config.example.toml",
             "Cargo.toml",
             "Cargo.lock",
         ];
diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs
index cc80763ef44..36fdd4abf4f 100644
--- a/src/bootstrap/doc.rs
+++ b/src/bootstrap/doc.rs
@@ -882,6 +882,7 @@ tool_doc!(
         // "cargo-credential-wincred",
     ]
 );
+tool_doc!(Tidy, "tidy", "src/tools/tidy", ["tidy"]);
 
 #[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)]
 pub struct ErrorIndex {
diff --git a/src/bootstrap/format.rs b/src/bootstrap/format.rs
index 5cb94c2f1d6..b79969663ca 100644
--- a/src/bootstrap/format.rs
+++ b/src/bootstrap/format.rs
@@ -165,8 +165,14 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) {
             if !CiEnv::is_ci() && paths.is_empty() {
                 match get_modified_rs_files(build) {
                     Ok(Some(files)) => {
+                        if files.len() <= 10 {
+                            for file in &files {
+                                println!("formatting modified file {file}");
+                            }
+                        } else {
+                            println!("formatting {} modified files", files.len());
+                        }
                         for file in files {
-                            println!("formatting modified file {file}");
                             ignore_fmt.add(&format!("/{file}")).expect(&file);
                         }
                     }
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index ebd42bcf678..22ddf872215 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -482,12 +482,7 @@ impl Build {
 
             // Make sure we update these before gathering metadata so we don't get an error about missing
             // Cargo.toml files.
-            let rust_submodules = [
-                "src/tools/rust-installer",
-                "src/tools/cargo",
-                "library/backtrace",
-                "library/stdarch",
-            ];
+            let rust_submodules = ["src/tools/cargo", "library/backtrace", "library/stdarch"];
             for s in rust_submodules {
                 build.update_submodule(Path::new(s));
             }
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs
index 909e7d83a15..040e36ea5f8 100644
--- a/src/bootstrap/native.rs
+++ b/src/bootstrap/native.rs
@@ -216,7 +216,7 @@ pub(crate) fn is_ci_llvm_available(config: &Config, asserts: bool) -> bool {
 
 /// Returns true if we're running in CI with modified LLVM (and thus can't download it)
 pub(crate) fn is_ci_llvm_modified(config: &Config) -> bool {
-    CiEnv::is_ci() && {
+    CiEnv::is_ci() && config.rust_info.is_managed_git_subrepository() && {
         // We assume we have access to git, so it's okay to unconditionally pass
         // `true` here.
         let llvm_sha = detect_llvm_sha(config, true);
@@ -286,7 +286,7 @@ impl Step for Llvm {
             (true, true) => "RelWithDebInfo",
         };
 
-        // NOTE: remember to also update `config.toml.example` when changing the
+        // NOTE: remember to also update `config.example.toml` when changing the
         // defaults!
         let llvm_targets = match &builder.config.llvm_targets {
             Some(s) => s,
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index b4f1506dc8f..f5d680df113 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -2695,3 +2695,58 @@ impl Step for LintDocs {
         });
     }
 }
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct RustInstaller;
+
+impl Step for RustInstaller {
+    type Output = ();
+    const ONLY_HOSTS: bool = true;
+    const DEFAULT: bool = true;
+
+    /// Ensure the version placeholder replacement tool builds
+    fn run(self, builder: &Builder<'_>) {
+        builder.info("test rust-installer");
+
+        let bootstrap_host = builder.config.build;
+        let compiler = builder.compiler(0, bootstrap_host);
+        let cargo = tool::prepare_tool_cargo(
+            builder,
+            compiler,
+            Mode::ToolBootstrap,
+            bootstrap_host,
+            "test",
+            "src/tools/rust-installer",
+            SourceType::InTree,
+            &[],
+        );
+        try_run(builder, &mut cargo.into());
+
+        // We currently don't support running the test.sh script outside linux(?) environments.
+        // Eventually this should likely migrate to #[test]s in rust-installer proper rather than a
+        // set of scripts, which will likely allow dropping this if.
+        if bootstrap_host != "x86_64-unknown-linux-gnu" {
+            return;
+        }
+
+        let mut cmd =
+            std::process::Command::new(builder.src.join("src/tools/rust-installer/test.sh"));
+        let tmpdir = testdir(builder, compiler.host).join("rust-installer");
+        let _ = std::fs::remove_dir_all(&tmpdir);
+        let _ = std::fs::create_dir_all(&tmpdir);
+        cmd.current_dir(&tmpdir);
+        cmd.env("CARGO_TARGET_DIR", tmpdir.join("cargo-target"));
+        cmd.env("CARGO", &builder.initial_cargo);
+        cmd.env("RUSTC", &builder.initial_rustc);
+        cmd.env("TMP_DIR", &tmpdir);
+        try_run(builder, &mut cmd);
+    }
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.path("src/tools/rust-installer")
+    }
+
+    fn make_run(run: RunConfig<'_>) {
+        run.builder.ensure(Self);
+    }
+}
diff --git a/src/ci/docker/host-x86_64/dist-various-2/build-solaris-toolchain.sh b/src/ci/docker/host-x86_64/dist-various-2/build-solaris-toolchain.sh
index cf784a66ae4..3939b4b7c41 100755
--- a/src/ci/docker/host-x86_64/dist-various-2/build-solaris-toolchain.sh
+++ b/src/ci/docker/host-x86_64/dist-various-2/build-solaris-toolchain.sh
@@ -32,24 +32,22 @@ cd solaris
 
 dpkg --add-architecture $APT_ARCH
 apt-get update
-apt-get download $(apt-cache depends --recurse --no-replaces \
+apt-get install -y --download-only                           \
   libc:$APT_ARCH                                             \
-  liblgrp-dev:$APT_ARCH                                      \
   liblgrp:$APT_ARCH                                          \
   libm-dev:$APT_ARCH                                         \
   libpthread:$APT_ARCH                                       \
   libresolv:$APT_ARCH                                        \
   librt:$APT_ARCH                                            \
-  libsendfile-dev:$APT_ARCH                                  \
   libsendfile:$APT_ARCH                                      \
   libsocket:$APT_ARCH                                        \
   system-crt:$APT_ARCH                                       \
-  system-header:$APT_ARCH                                    \
-  | grep "^\w")
+  system-header:$APT_ARCH
 
-for deb in *$APT_ARCH.deb; do
+for deb in /var/cache/apt/archives/*$APT_ARCH.deb; do
   dpkg -x $deb .
 done
+apt-get clean
 
 # The -dev packages are not available from the apt repository we're using.
 # However, those packages are just symlinks from *.so to *.so.<version>.
diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile
index 98bd90210d6..515890aef8d 100644
--- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile
+++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile
@@ -52,4 +52,6 @@ ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \
            reuse lint && \
            # Runs checks to ensure that there are no ES5 issues in our JS code.
            es-check es6 ../src/librustdoc/html/static/js/*.js && \
-           eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js
+           eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js && \
+           eslint -c ../src/tools/rustdoc-js/.eslintrc.js ../src/tools/rustdoc-js/tester.js && \
+           eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-14/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-14/Dockerfile
index db6032f8752..a007bf183ee 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-14/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-14/Dockerfile
@@ -62,6 +62,4 @@ ENV SCRIPT ../x.py --stage 2 test --exclude src/tools/tidy && \
            # work.
            #
            ../x.ps1 --stage 2 test tests/ui --pass=check \
-                             --host='' --target=i686-unknown-linux-gnu && \
-           # Run tidy at the very end, after all the other tests.
-           python2.7 ../x.py --stage 2 test src/tools/tidy
+                             --host='' --target=i686-unknown-linux-gnu
diff --git a/src/ci/github-actions/problem_matchers.json b/src/ci/github-actions/problem_matchers.json
new file mode 100644
index 00000000000..37561924b7d
--- /dev/null
+++ b/src/ci/github-actions/problem_matchers.json
@@ -0,0 +1,15 @@
+{
+    "problemMatcher": [
+        {
+            "owner": "tidy-error-file-line",
+            "pattern": [
+                {
+                    "regexp": "^tidy error: /checkout/(.+):(\\d+): (.+)$",
+                    "file": 1,
+                    "line": 2,
+                    "message": 3
+                }
+            ]
+        }
+    ]
+}
diff --git a/src/ci/scripts/collect-cpu-stats.sh b/src/ci/scripts/collect-cpu-stats.sh
index 853b4628fab..44875b54ddc 100755
--- a/src/ci/scripts/collect-cpu-stats.sh
+++ b/src/ci/scripts/collect-cpu-stats.sh
@@ -6,4 +6,5 @@
 set -euo pipefail
 IFS=$'\n\t'
 
-python3 src/ci/cpu-usage-over-time.py &> cpu-usage.csv &
+mkdir -p build
+python3 src/ci/cpu-usage-over-time.py &> build/cpu-usage.csv &
diff --git a/src/ci/scripts/run-build-from-ci.sh b/src/ci/scripts/run-build-from-ci.sh
index c02117f459d..55e75800d91 100755
--- a/src/ci/scripts/run-build-from-ci.sh
+++ b/src/ci/scripts/run-build-from-ci.sh
@@ -10,6 +10,8 @@ source "$(cd "$(dirname "$0")" && pwd)/../shared.sh"
 export CI="true"
 export SRC=.
 
+echo "::add-matcher::src/ci/github-actions/problem_matchers.json"
+
 # Remove any preexisting rustup installation since it can interfere
 # with the cargotest step and its auto-detection of things like Clippy in
 # the environment
diff --git a/src/ci/scripts/upload-artifacts.sh b/src/ci/scripts/upload-artifacts.sh
index ffa1859fc22..9755edb6dce 100755
--- a/src/ci/scripts/upload-artifacts.sh
+++ b/src/ci/scripts/upload-artifacts.sh
@@ -23,7 +23,7 @@ if [[ "${DEPLOY-0}" -eq "1" ]] || [[ "${DEPLOY_ALT-0}" -eq "1" ]]; then
 fi
 
 # CPU usage statistics.
-cp cpu-usage.csv "${upload_dir}/cpu-${CI_JOB_NAME}.csv"
+cp build/cpu-usage.csv "${upload_dir}/cpu-${CI_JOB_NAME}.csv"
 
 # Build metrics generated by x.py.
 cp "${build_dir}/metrics.json" "${upload_dir}/metrics-${CI_JOB_NAME}.json"
diff --git a/src/doc/footer.inc b/src/doc/footer.inc
index 77e151235e8..504fe51156d 100644
--- a/src/doc/footer.inc
+++ b/src/doc/footer.inc
@@ -1,3 +1,4 @@
+<!-- REUSE-IgnoreStart -->
 <footer><p>
 Copyright &copy; 2011 The Rust Project Developers. Licensed under the
 <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License, Version 2.0</a>
@@ -5,3 +6,4 @@ or the <a href="https://opensource.org/licenses/MIT">MIT license</a>, at your op
 </p><p>
 This file may not be copied, modified, or distributed except according to those terms.
 </p></footer>
+<!-- REUSE-IgnoreEnd -->
diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md
index da91e25595c..b0b2f419642 100644
--- a/src/doc/rustc/src/instrument-coverage.md
+++ b/src/doc/rustc/src/instrument-coverage.md
@@ -31,7 +31,7 @@ Rust's source-based code coverage requires the Rust "profiler runtime". Without
 
 The Rust `nightly` distribution channel includes the profiler runtime, by default.
 
-> **Important**: If you are building the Rust compiler from the source distribution, the profiler runtime is _not_ enabled in the default `config.toml.example`. Edit your `config.toml` file and ensure the `profiler` feature is set it to `true` (either under the `[build]` section, or under the settings for an individual `[target.<triple>]`):
+> **Important**: If you are building the Rust compiler from the source distribution, the profiler runtime is _not_ enabled in the default `config.example.toml`. Edit your `config.toml` file and ensure the `profiler` feature is set it to `true` (either under the `[build]` section, or under the settings for an individual `[target.<triple>]`):
 >
 > ```toml
 > # Build the profiler runtime (required when compiling with options that depend
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index bbd9f18973a..29c3afe0d95 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -2065,23 +2065,81 @@ fn clean_bare_fn_ty<'tcx>(
     BareFunctionDecl { unsafety: bare_fn.unsafety, abi: bare_fn.abi, decl, generic_params }
 }
 
-/// This visitor is used to go through only the "top level" of a item and not enter any sub
-/// item while looking for a given `Ident` which is stored into `item` if found.
-struct OneLevelVisitor<'hir> {
+/// Get DefId of of an item's user-visible parent.
+///
+/// "User-visible" should account for re-exporting and inlining, which is why this function isn't
+/// just `tcx.parent(def_id)`. If the provided `path` has more than one path element, the `DefId`
+/// of the second-to-last will be given.
+///
+/// ```text
+/// use crate::foo::Bar;
+///            ^^^ DefId of this item will be returned
+/// ```
+///
+/// If the provided path has only one item, `tcx.parent(def_id)` will be returned instead.
+fn get_path_parent_def_id(
+    tcx: TyCtxt<'_>,
+    def_id: DefId,
+    path: &hir::UsePath<'_>,
+) -> Option<DefId> {
+    if let [.., parent_segment, _] = &path.segments {
+        match parent_segment.res {
+            hir::def::Res::Def(_, parent_def_id) => Some(parent_def_id),
+            _ if parent_segment.ident.name == kw::Crate => {
+                // In case the "parent" is the crate, it'll give `Res::Err` so we need to
+                // circumvent it this way.
+                Some(tcx.parent(def_id))
+            }
+            _ => None,
+        }
+    } else {
+        // If the path doesn't have a parent, then the parent is the current module.
+        Some(tcx.parent(def_id))
+    }
+}
+
+/// This visitor is used to find an HIR Item based on its `use` path. This doesn't use the ordinary
+/// name resolver because it does not walk all the way through a chain of re-exports.
+pub(crate) struct OneLevelVisitor<'hir> {
     map: rustc_middle::hir::map::Map<'hir>,
-    item: Option<&'hir hir::Item<'hir>>,
+    pub(crate) item: Option<&'hir hir::Item<'hir>>,
     looking_for: Ident,
     target_def_id: LocalDefId,
 }
 
 impl<'hir> OneLevelVisitor<'hir> {
-    fn new(map: rustc_middle::hir::map::Map<'hir>, target_def_id: LocalDefId) -> Self {
+    pub(crate) fn new(map: rustc_middle::hir::map::Map<'hir>, target_def_id: LocalDefId) -> Self {
         Self { map, item: None, looking_for: Ident::empty(), target_def_id }
     }
 
-    fn reset(&mut self, looking_for: Ident) {
-        self.looking_for = looking_for;
+    pub(crate) fn find_target(
+        &mut self,
+        tcx: TyCtxt<'_>,
+        def_id: DefId,
+        path: &hir::UsePath<'_>,
+    ) -> Option<&'hir hir::Item<'hir>> {
+        let parent_def_id = get_path_parent_def_id(tcx, def_id, path)?;
+        let parent = self.map.get_if_local(parent_def_id)?;
+
+        // We get the `Ident` we will be looking for into `item`.
+        self.looking_for = path.segments[path.segments.len() - 1].ident;
+        // We reset the `item`.
         self.item = None;
+
+        match parent {
+            hir::Node::Item(parent_item) => {
+                hir::intravisit::walk_item(self, parent_item);
+            }
+            hir::Node::Crate(m) => {
+                hir::intravisit::walk_mod(
+                    self,
+                    m,
+                    tcx.local_def_id_to_hir_id(parent_def_id.as_local().unwrap()),
+                );
+            }
+            _ => return None,
+        }
+        self.item
     }
 }
 
@@ -2129,41 +2187,7 @@ fn get_all_import_attributes<'hir>(
             add_without_unwanted_attributes(attributes, hir_map.attrs(item.hir_id()), is_inline);
         }
 
-        let def_id = if let [.., parent_segment, _] = &path.segments {
-            match parent_segment.res {
-                hir::def::Res::Def(_, def_id) => def_id,
-                _ if parent_segment.ident.name == kw::Crate => {
-                    // In case the "parent" is the crate, it'll give `Res::Err` so we need to
-                    // circumvent it this way.
-                    tcx.parent(item.owner_id.def_id.to_def_id())
-                }
-                _ => break,
-            }
-        } else {
-            // If the path doesn't have a parent, then the parent is the current module.
-            tcx.parent(item.owner_id.def_id.to_def_id())
-        };
-
-        let Some(parent) = hir_map.get_if_local(def_id) else { break };
-
-        // We get the `Ident` we will be looking for into `item`.
-        let looking_for = path.segments[path.segments.len() - 1].ident;
-        visitor.reset(looking_for);
-
-        match parent {
-            hir::Node::Item(parent_item) => {
-                hir::intravisit::walk_item(&mut visitor, parent_item);
-            }
-            hir::Node::Crate(m) => {
-                hir::intravisit::walk_mod(
-                    &mut visitor,
-                    m,
-                    tcx.local_def_id_to_hir_id(def_id.as_local().unwrap()),
-                );
-            }
-            _ => break,
-        }
-        if let Some(i) = visitor.item {
+        if let Some(i) = visitor.find_target(tcx, item.owner_id.def_id.to_def_id(), path) {
             item = i;
         } else {
             break;
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index 8dbfaf4bbc9..0295de8437e 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -346,6 +346,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
                                     self.cache,
                                 ),
                                 aliases: item.attrs.get_doc_aliases(),
+                                deprecation: item.deprecation(self.tcx),
                             });
                         }
                     }
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 5e4a595627b..63cd0e04a28 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -17,10 +17,11 @@ use super::print_item::{full_path, item_path, print_item};
 use super::search_index::build_index;
 use super::write_shared::write_shared;
 use super::{
-    collect_spans_and_sources, print_sidebar, scrape_examples_help, sidebar_module_like, AllTypes,
-    LinkFromSrc, StylePath,
+    collect_spans_and_sources, scrape_examples_help,
+    sidebar::print_sidebar,
+    sidebar::{sidebar_module_like, Sidebar},
+    AllTypes, LinkFromSrc, StylePath,
 };
-
 use crate::clean::{self, types::ExternalLocation, ExternalCrate};
 use crate::config::{ModuleSorting, RenderOptions};
 use crate::docfs::{DocFS, PathError};
@@ -35,6 +36,7 @@ use crate::html::url_parts_builder::UrlPartsBuilder;
 use crate::html::{layout, sources, static_files};
 use crate::scrape_examples::AllCallLocations;
 use crate::try_err;
+use askama::Template;
 
 /// Major driving force in all rustdoc rendering. This contains information
 /// about where in the tree-like hierarchy rendering is occurring and controls
@@ -600,17 +602,18 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         };
         let all = shared.all.replace(AllTypes::new());
         let mut sidebar = Buffer::html();
-        if shared.cache.crate_version.is_some() {
-            write!(sidebar, "<h2 class=\"location\">Crate {}</h2>", crate_name)
+
+        let blocks = sidebar_module_like(all.item_sections());
+        let bar = Sidebar {
+            title_prefix: "Crate ",
+            title: crate_name.as_str(),
+            is_crate: false,
+            version: "",
+            blocks: vec![blocks],
+            path: String::new(),
         };
 
-        let mut items = Buffer::html();
-        sidebar_module_like(&mut items, all.item_sections());
-        if !items.is_empty() {
-            sidebar.push_str("<div class=\"sidebar-elems\">");
-            sidebar.push_buffer(items);
-            sidebar.push_str("</div>");
-        }
+        bar.render_into(&mut sidebar).unwrap();
 
         let v = layout::render(
             &shared.layout,
@@ -649,11 +652,35 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
                      </noscript>\
                      <link rel=\"stylesheet\" \
                          href=\"{static_root_path}{settings_css}\">\
-                     <script defer src=\"{static_root_path}{settings_js}\"></script>",
+                     <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\">",
                     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.
+                //
+                // When loading settings.html as a popover, the equivalent HTML is
+                // generated in main.js.
+                for file in &shared.style_files {
+                    if let Ok(theme) = file.basename() {
+                        write!(
+                            buf,
+                            "<link rel=\"preload\" href=\"{root_path}{theme}{suffix}.css\" \
+                                as=\"style\">",
+                            root_path = page.static_root_path.unwrap_or(""),
+                            suffix = page.resource_suffix,
+                        );
+                    }
+                }
             },
             &shared.style_files,
         );
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index e6a040d02e5..da1f1cf5ecc 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -30,6 +30,7 @@ mod tests;
 
 mod context;
 mod print_item;
+mod sidebar;
 mod span_map;
 mod write_shared;
 
@@ -46,14 +47,13 @@ use std::rc::Rc;
 use std::str;
 use std::string::ToString;
 
+use askama::Template;
 use rustc_ast_pretty::pprust;
 use rustc_attr::{ConstStability, Deprecation, StabilityLevel};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir::def::CtorKind;
 use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_hir::Mutability;
 use rustc_middle::middle::stability;
-use rustc_middle::ty;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::{
     symbol::{sym, Symbol},
@@ -104,6 +104,7 @@ pub(crate) struct IndexItem {
     pub(crate) parent_idx: Option<usize>,
     pub(crate) search_type: Option<IndexItemFunctionType>,
     pub(crate) aliases: Box<[Symbol]>,
+    pub(crate) deprecation: Option<Deprecation>,
 }
 
 /// A type used for the search index.
@@ -417,7 +418,7 @@ fn document(
     if let Some(ref name) = item.name {
         info!("Documenting {}", name);
     }
-    document_item_info(w, cx, item, parent);
+    document_item_info(cx, item, parent).render_into(w).unwrap();
     if parent.is_none() {
         document_full_collapsible(w, item, cx, heading_offset);
     } else {
@@ -459,7 +460,7 @@ fn document_short(
     parent: &clean::Item,
     show_def_docs: bool,
 ) {
-    document_item_info(w, cx, item, Some(parent));
+    document_item_info(cx, item, Some(parent)).render_into(w).unwrap();
     if !show_def_docs {
         return;
     }
@@ -531,25 +532,23 @@ fn document_full_inner(
     }
 }
 
+#[derive(Template)]
+#[template(path = "item_info.html")]
+struct ItemInfo {
+    items: Vec<ShortItemInfo>,
+}
 /// Add extra information about an item such as:
 ///
 /// * Stability
 /// * Deprecated
 /// * Required features (through the `doc_cfg` feature)
 fn document_item_info(
-    w: &mut Buffer,
     cx: &mut Context<'_>,
     item: &clean::Item,
     parent: Option<&clean::Item>,
-) {
-    let item_infos = short_item_info(item, cx, parent);
-    if !item_infos.is_empty() {
-        w.write_str("<span class=\"item-info\">");
-        for info in item_infos {
-            w.write_str(&info);
-        }
-        w.write_str("</span>");
-    }
+) -> ItemInfo {
+    let items = short_item_info(item, cx, parent);
+    ItemInfo { items }
 }
 
 fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<String> {
@@ -567,7 +566,25 @@ fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<Strin
         cfg
     );
 
-    Some(format!("<div class=\"stab portability\">{}</div>", cfg?.render_long_html()))
+    Some(cfg?.render_long_html())
+}
+
+#[derive(Template)]
+#[template(path = "short_item_info.html")]
+enum ShortItemInfo {
+    /// A message describing the deprecation of this item
+    Deprecation {
+        message: String,
+    },
+    /// The feature corresponding to an unstable item, and optionally
+    /// a tracking issue URL and number.
+    Unstable {
+        feature: String,
+        tracking: Option<(String, u32)>,
+    },
+    Portability {
+        message: String,
+    },
 }
 
 /// Render the stability, deprecation and portability information that is displayed at the top of
@@ -576,7 +593,7 @@ fn short_item_info(
     item: &clean::Item,
     cx: &mut Context<'_>,
     parent: Option<&clean::Item>,
-) -> Vec<String> {
+) -> Vec<ShortItemInfo> {
     let mut extra_info = vec![];
 
     if let Some(depr @ Deprecation { note, since, is_since_rustc_version: _, suggestion: _ }) =
@@ -602,15 +619,10 @@ fn short_item_info(
         if let Some(note) = note {
             let note = note.as_str();
             let html = MarkdownItemInfo(note, &mut cx.id_map);
-            message.push_str(&format!(": {}", html.into_string()));
-        }
-        extra_info.push(format!(
-            "<div class=\"stab deprecated\">\
-                 <span class=\"emoji\">👎</span>\
-                 <span>{}</span>\
-             </div>",
-            message,
-        ));
+            message.push_str(": ");
+            message.push_str(&html.into_string());
+        }
+        extra_info.push(ShortItemInfo::Deprecation { message });
     }
 
     // Render unstable items. But don't render "rustc_private" crates (internal compiler crates).
@@ -621,26 +633,17 @@ fn short_item_info(
         .filter(|stab| stab.feature != sym::rustc_private)
         .map(|stab| (stab.level, stab.feature))
     {
-        let mut message = "<span class=\"emoji\">🔬</span>\
-             <span>This is a nightly-only experimental API."
-            .to_owned();
-
-        let mut feature = format!("<code>{}</code>", Escape(feature.as_str()));
-        if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue) {
-            feature.push_str(&format!(
-                "&nbsp;<a href=\"{url}{issue}\">#{issue}</a>",
-                url = url,
-                issue = issue
-            ));
-        }
-
-        message.push_str(&format!(" ({})</span>", feature));
-
-        extra_info.push(format!("<div class=\"stab unstable\">{}</div>", message));
+        let tracking = if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue)
+        {
+            Some((url.clone(), issue.get()))
+        } else {
+            None
+        };
+        extra_info.push(ShortItemInfo::Unstable { feature: feature.to_string(), tracking });
     }
 
-    if let Some(portability) = portability(item, parent) {
-        extra_info.push(portability);
+    if let Some(message) = portability(item, parent) {
+        extra_info.push(ShortItemInfo::Portability { message });
     }
 
     extra_info
@@ -1472,7 +1475,9 @@ fn render_impl(
                         // We need the stability of the item from the trait
                         // because impls can't have a stability.
                         if item.doc_value().is_some() {
-                            document_item_info(&mut info_buffer, cx, it, Some(parent));
+                            document_item_info(cx, it, Some(parent))
+                                .render_into(&mut info_buffer)
+                                .unwrap();
                             document_full(&mut doc_buffer, item, cx, HeadingOffset::H5);
                             short_documented = false;
                         } else {
@@ -1489,7 +1494,9 @@ fn render_impl(
                         }
                     }
                 } else {
-                    document_item_info(&mut info_buffer, cx, item, Some(parent));
+                    document_item_info(cx, item, Some(parent))
+                        .render_into(&mut info_buffer)
+                        .unwrap();
                     if rendering_params.show_def_docs {
                         document_full(&mut doc_buffer, item, cx, HeadingOffset::H5);
                         short_documented = false;
@@ -1862,161 +1869,17 @@ pub(crate) fn render_impl_summary(
     let is_trait = inner_impl.trait_.is_some();
     if is_trait {
         if let Some(portability) = portability(&i.impl_item, Some(parent)) {
-            write!(w, "<span class=\"item-info\">{}</span>", portability);
+            write!(
+                w,
+                "<span class=\"item-info\"><div class=\"stab portability\">{}</div></span>",
+                portability
+            );
         }
     }
 
     w.write_str("</section>");
 }
 
-fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
-    if it.is_struct()
-        || it.is_trait()
-        || it.is_primitive()
-        || it.is_union()
-        || it.is_enum()
-        || it.is_mod()
-        || it.is_typedef()
-    {
-        write!(
-            buffer,
-            "<h2 class=\"location\"><a href=\"#\">{}{}</a></h2>",
-            match *it.kind {
-                clean::ModuleItem(..) =>
-                    if it.is_crate() {
-                        "Crate "
-                    } else {
-                        "Module "
-                    },
-                _ => "",
-            },
-            it.name.as_ref().unwrap()
-        );
-    }
-
-    buffer.write_str("<div class=\"sidebar-elems\">");
-    if it.is_crate() {
-        write!(buffer, "<ul class=\"block\">");
-        if let Some(ref version) = cx.cache().crate_version {
-            write!(buffer, "<li class=\"version\">Version {}</li>", Escape(version));
-        }
-        write!(buffer, "<li><a id=\"all-types\" href=\"all.html\">All Items</a></li>");
-        buffer.write_str("</ul>");
-    }
-
-    match *it.kind {
-        clean::StructItem(ref s) => sidebar_struct(cx, buffer, it, s),
-        clean::TraitItem(ref t) => sidebar_trait(cx, buffer, it, t),
-        clean::PrimitiveItem(_) => sidebar_primitive(cx, buffer, it),
-        clean::UnionItem(ref u) => sidebar_union(cx, buffer, it, u),
-        clean::EnumItem(ref e) => sidebar_enum(cx, buffer, it, e),
-        clean::TypedefItem(_) => sidebar_typedef(cx, buffer, it),
-        clean::ModuleItem(ref m) => sidebar_module(buffer, &m.items),
-        clean::ForeignTypeItem => sidebar_foreign_type(cx, buffer, it),
-        _ => {}
-    }
-
-    // The sidebar is designed to display sibling functions, modules and
-    // other miscellaneous information. since there are lots of sibling
-    // items (and that causes quadratic growth in large modules),
-    // we refactor common parts into a shared JavaScript file per module.
-    // still, we don't move everything into JS because we want to preserve
-    // as much HTML as possible in order to allow non-JS-enabled browsers
-    // to navigate the documentation (though slightly inefficiently).
-
-    if !it.is_mod() {
-        let path: String = cx.current.iter().map(|s| s.as_str()).intersperse("::").collect();
-
-        write!(buffer, "<h2><a href=\"index.html\">In {}</a></h2>", path);
-    }
-
-    // Closes sidebar-elems div.
-    buffer.write_str("</div>");
-}
-
-fn get_next_url(used_links: &mut FxHashSet<String>, url: String) -> String {
-    if used_links.insert(url.clone()) {
-        return url;
-    }
-    let mut add = 1;
-    while !used_links.insert(format!("{}-{}", url, add)) {
-        add += 1;
-    }
-    format!("{}-{}", url, add)
-}
-
-struct SidebarLink {
-    name: Symbol,
-    url: String,
-}
-
-impl fmt::Display for SidebarLink {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "<a href=\"#{}\">{}</a>", self.url, self.name)
-    }
-}
-
-impl PartialEq for SidebarLink {
-    fn eq(&self, other: &Self) -> bool {
-        self.url == other.url
-    }
-}
-
-impl Eq for SidebarLink {}
-
-impl PartialOrd for SidebarLink {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl Ord for SidebarLink {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        self.url.cmp(&other.url)
-    }
-}
-
-fn get_methods(
-    i: &clean::Impl,
-    for_deref: bool,
-    used_links: &mut FxHashSet<String>,
-    deref_mut: bool,
-    tcx: TyCtxt<'_>,
-) -> Vec<SidebarLink> {
-    i.items
-        .iter()
-        .filter_map(|item| match item.name {
-            Some(name) if !name.is_empty() && item.is_method() => {
-                if !for_deref || should_render_item(item, deref_mut, tcx) {
-                    Some(SidebarLink {
-                        name,
-                        url: get_next_url(used_links, format!("{}.{}", ItemType::Method, name)),
-                    })
-                } else {
-                    None
-                }
-            }
-            _ => None,
-        })
-        .collect::<Vec<_>>()
-}
-
-fn get_associated_constants(
-    i: &clean::Impl,
-    used_links: &mut FxHashSet<String>,
-) -> Vec<SidebarLink> {
-    i.items
-        .iter()
-        .filter_map(|item| match item.name {
-            Some(name) if !name.is_empty() && item.is_associated_const() => Some(SidebarLink {
-                name,
-                url: get_next_url(used_links, format!("{}.{}", ItemType::AssocConst, name)),
-            }),
-            _ => None,
-        })
-        .collect::<Vec<_>>()
-}
-
 pub(crate) fn small_url_encode(s: String) -> String {
     // These characters don't need to be escaped in a URI.
     // See https://url.spec.whatwg.org/#query-percent-encode-set
@@ -2082,232 +1945,6 @@ pub(crate) fn small_url_encode(s: String) -> String {
     }
 }
 
-pub(crate) fn sidebar_render_assoc_items(
-    cx: &Context<'_>,
-    out: &mut Buffer,
-    id_map: &mut IdMap,
-    concrete: Vec<&Impl>,
-    synthetic: Vec<&Impl>,
-    blanket_impl: Vec<&Impl>,
-) {
-    let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
-        let mut links = FxHashSet::default();
-
-        let mut ret = impls
-            .iter()
-            .filter_map(|it| {
-                let trait_ = it.inner_impl().trait_.as_ref()?;
-                let encoded =
-                    id_map.derive(get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx));
-
-                let i_display = format!("{:#}", trait_.print(cx));
-                let out = Escape(&i_display);
-                let prefix = match it.inner_impl().polarity {
-                    ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
-                    ty::ImplPolarity::Negative => "!",
-                };
-                let generated = format!("<a href=\"#{}\">{}{}</a>", encoded, prefix, out);
-                if links.insert(generated.clone()) { Some(generated) } else { None }
-            })
-            .collect::<Vec<String>>();
-        ret.sort();
-        ret
-    };
-
-    let concrete_format = format_impls(concrete, id_map);
-    let synthetic_format = format_impls(synthetic, id_map);
-    let blanket_format = format_impls(blanket_impl, id_map);
-
-    if !concrete_format.is_empty() {
-        print_sidebar_block(
-            out,
-            "trait-implementations",
-            "Trait Implementations",
-            concrete_format.iter(),
-        );
-    }
-
-    if !synthetic_format.is_empty() {
-        print_sidebar_block(
-            out,
-            "synthetic-implementations",
-            "Auto Trait Implementations",
-            synthetic_format.iter(),
-        );
-    }
-
-    if !blanket_format.is_empty() {
-        print_sidebar_block(
-            out,
-            "blanket-implementations",
-            "Blanket Implementations",
-            blanket_format.iter(),
-        );
-    }
-}
-
-fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
-    let did = it.item_id.expect_def_id();
-    let cache = cx.cache();
-
-    if let Some(v) = cache.impls.get(&did) {
-        let mut used_links = FxHashSet::default();
-        let mut id_map = IdMap::new();
-
-        {
-            let used_links_bor = &mut used_links;
-            let mut assoc_consts = v
-                .iter()
-                .filter(|i| i.inner_impl().trait_.is_none())
-                .flat_map(|i| get_associated_constants(i.inner_impl(), used_links_bor))
-                .collect::<Vec<_>>();
-            if !assoc_consts.is_empty() {
-                // We want links' order to be reproducible so we don't use unstable sort.
-                assoc_consts.sort();
-
-                print_sidebar_block(
-                    out,
-                    "implementations",
-                    "Associated Constants",
-                    assoc_consts.iter(),
-                );
-            }
-            let mut methods = v
-                .iter()
-                .filter(|i| i.inner_impl().trait_.is_none())
-                .flat_map(|i| get_methods(i.inner_impl(), false, used_links_bor, false, cx.tcx()))
-                .collect::<Vec<_>>();
-            if !methods.is_empty() {
-                // We want links' order to be reproducible so we don't use unstable sort.
-                methods.sort();
-
-                print_sidebar_block(out, "implementations", "Methods", methods.iter());
-            }
-        }
-
-        if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
-            if let Some(impl_) =
-                v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait())
-            {
-                let mut derefs = DefIdSet::default();
-                derefs.insert(did);
-                sidebar_deref_methods(cx, out, impl_, v, &mut derefs, &mut used_links);
-            }
-
-            let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
-                v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto());
-            let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
-                concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket());
-
-            sidebar_render_assoc_items(cx, out, &mut id_map, concrete, synthetic, blanket_impl);
-        }
-    }
-}
-
-fn sidebar_deref_methods(
-    cx: &Context<'_>,
-    out: &mut Buffer,
-    impl_: &Impl,
-    v: &[Impl],
-    derefs: &mut DefIdSet,
-    used_links: &mut FxHashSet<String>,
-) {
-    let c = cx.cache();
-
-    debug!("found Deref: {:?}", impl_);
-    if let Some((target, real_target)) =
-        impl_.inner_impl().items.iter().find_map(|item| match *item.kind {
-            clean::AssocTypeItem(box ref t, _) => Some(match *t {
-                clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
-                _ => (&t.type_, &t.type_),
-            }),
-            _ => None,
-        })
-    {
-        debug!("found target, real_target: {:?} {:?}", target, real_target);
-        if let Some(did) = target.def_id(c) &&
-            let Some(type_did) = impl_.inner_impl().for_.def_id(c) &&
-            // `impl Deref<Target = S> for S`
-            (did == type_did || !derefs.insert(did))
-        {
-            // Avoid infinite cycles
-            return;
-        }
-        let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
-        let inner_impl = target
-            .def_id(c)
-            .or_else(|| {
-                target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned())
-            })
-            .and_then(|did| c.impls.get(&did));
-        if let Some(impls) = inner_impl {
-            debug!("found inner_impl: {:?}", impls);
-            let mut ret = impls
-                .iter()
-                .filter(|i| i.inner_impl().trait_.is_none())
-                .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx()))
-                .collect::<Vec<_>>();
-            if !ret.is_empty() {
-                let id = if let Some(target_def_id) = real_target.def_id(c) {
-                    cx.deref_id_map.get(&target_def_id).expect("Deref section without derived id")
-                } else {
-                    "deref-methods"
-                };
-                let title = format!(
-                    "Methods from {}&lt;Target={}&gt;",
-                    Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print(cx))),
-                    Escape(&format!("{:#}", real_target.print(cx))),
-                );
-                // We want links' order to be reproducible so we don't use unstable sort.
-                ret.sort();
-                print_sidebar_block(out, id, &title, ret.iter());
-            }
-        }
-
-        // Recurse into any further impls that might exist for `target`
-        if let Some(target_did) = target.def_id(c) &&
-            let Some(target_impls) = c.impls.get(&target_did) &&
-            let Some(target_deref_impl) = target_impls.iter().find(|i| {
-                i.inner_impl()
-                    .trait_
-                    .as_ref()
-                    .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait())
-                    .unwrap_or(false)
-            })
-        {
-            sidebar_deref_methods(
-                cx,
-                out,
-                target_deref_impl,
-                target_impls,
-                derefs,
-                used_links,
-            );
-        }
-    }
-}
-
-fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) {
-    let mut sidebar = Buffer::new();
-    let fields = get_struct_fields_name(&s.fields);
-
-    if !fields.is_empty() {
-        match s.ctor_kind {
-            None => {
-                print_sidebar_block(&mut sidebar, "fields", "Fields", fields.iter());
-            }
-            Some(CtorKind::Fn) => print_sidebar_title(&mut sidebar, "fields", "Tuple Fields"),
-            Some(CtorKind::Const) => {}
-        }
-    }
-
-    sidebar_assoc_items(cx, &mut sidebar, it);
-
-    if !sidebar.is_empty() {
-        write!(buf, "<section>{}</section>", sidebar.into_inner());
-    }
-}
-
 fn get_id_for_impl(for_: &clean::Type, trait_: Option<&clean::Path>, cx: &Context<'_>) -> String {
     match trait_ {
         Some(t) => small_url_encode(format!("impl-{:#}-for-{:#}", t.print(cx), for_.print(cx))),
@@ -2328,131 +1965,6 @@ fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String
     }
 }
 
-fn print_sidebar_title(buf: &mut Buffer, id: &str, title: &str) {
-    write!(buf, "<h3><a href=\"#{}\">{}</a></h3>", id, title);
-}
-
-fn print_sidebar_block(
-    buf: &mut Buffer,
-    id: &str,
-    title: &str,
-    items: impl Iterator<Item = impl fmt::Display>,
-) {
-    print_sidebar_title(buf, id, title);
-    buf.push_str("<ul class=\"block\">");
-    for item in items {
-        write!(buf, "<li>{}</li>", item);
-    }
-    buf.push_str("</ul>");
-}
-
-fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) {
-    buf.write_str("<section>");
-
-    fn print_sidebar_section(
-        out: &mut Buffer,
-        items: &[clean::Item],
-        id: &str,
-        title: &str,
-        filter: impl Fn(&clean::Item) -> bool,
-        mapper: impl Fn(&str) -> String,
-    ) {
-        let mut items: Vec<&str> = items
-            .iter()
-            .filter_map(|m| match m.name {
-                Some(ref name) if filter(m) => Some(name.as_str()),
-                _ => None,
-            })
-            .collect::<Vec<_>>();
-
-        if !items.is_empty() {
-            items.sort_unstable();
-            print_sidebar_block(out, id, title, items.into_iter().map(mapper));
-        }
-    }
-
-    print_sidebar_section(
-        buf,
-        &t.items,
-        "required-associated-types",
-        "Required Associated Types",
-        |m| m.is_ty_associated_type(),
-        |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocType),
-    );
-
-    print_sidebar_section(
-        buf,
-        &t.items,
-        "provided-associated-types",
-        "Provided Associated Types",
-        |m| m.is_associated_type(),
-        |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocType),
-    );
-
-    print_sidebar_section(
-        buf,
-        &t.items,
-        "required-associated-consts",
-        "Required Associated Constants",
-        |m| m.is_ty_associated_const(),
-        |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocConst),
-    );
-
-    print_sidebar_section(
-        buf,
-        &t.items,
-        "provided-associated-consts",
-        "Provided Associated Constants",
-        |m| m.is_associated_const(),
-        |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocConst),
-    );
-
-    print_sidebar_section(
-        buf,
-        &t.items,
-        "required-methods",
-        "Required Methods",
-        |m| m.is_ty_method(),
-        |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::TyMethod),
-    );
-
-    print_sidebar_section(
-        buf,
-        &t.items,
-        "provided-methods",
-        "Provided Methods",
-        |m| m.is_method(),
-        |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::Method),
-    );
-
-    if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) {
-        let mut res = implementors
-            .iter()
-            .filter(|i| !i.is_on_local_type(cx))
-            .filter_map(|i| extract_for_impl_name(&i.impl_item, cx))
-            .collect::<Vec<_>>();
-
-        if !res.is_empty() {
-            res.sort();
-            print_sidebar_block(
-                buf,
-                "foreign-impls",
-                "Implementations on Foreign Types",
-                res.iter().map(|(name, id)| format!("<a href=\"#{}\">{}</a>", id, Escape(name))),
-            );
-        }
-    }
-
-    sidebar_assoc_items(cx, buf, it);
-
-    print_sidebar_title(buf, "implementors", "Implementors");
-    if t.is_auto(cx.tcx()) {
-        print_sidebar_title(buf, "synthetic-implementors", "Auto Implementors");
-    }
-
-    buf.push_str("</section>")
-}
-
 /// Returns the list of implementations for the primitive reference type, filtering out any
 /// implementations that are on concrete or partially generic types, only keeping implementations
 /// of the form `impl<T> Trait for &T`.
@@ -2483,89 +1995,6 @@ pub(crate) fn get_filtered_impls_for_reference<'a>(
     (concrete, synthetic, blanket_impl)
 }
 
-fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
-    let mut sidebar = Buffer::new();
-
-    if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
-        sidebar_assoc_items(cx, &mut sidebar, it);
-    } else {
-        let shared = Rc::clone(&cx.shared);
-        let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&shared, it);
-
-        sidebar_render_assoc_items(
-            cx,
-            &mut sidebar,
-            &mut IdMap::new(),
-            concrete,
-            synthetic,
-            blanket_impl,
-        );
-    }
-
-    if !sidebar.is_empty() {
-        write!(buf, "<section>{}</section>", sidebar.into_inner());
-    }
-}
-
-fn sidebar_typedef(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
-    let mut sidebar = Buffer::new();
-    sidebar_assoc_items(cx, &mut sidebar, it);
-
-    if !sidebar.is_empty() {
-        write!(buf, "<section>{}</section>", sidebar.into_inner());
-    }
-}
-
-fn get_struct_fields_name(fields: &[clean::Item]) -> Vec<String> {
-    let mut fields = fields
-        .iter()
-        .filter(|f| matches!(*f.kind, clean::StructFieldItem(..)))
-        .filter_map(|f| {
-            f.name.map(|name| format!("<a href=\"#structfield.{name}\">{name}</a>", name = name))
-        })
-        .collect::<Vec<_>>();
-    fields.sort();
-    fields
-}
-
-fn sidebar_union(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, u: &clean::Union) {
-    let mut sidebar = Buffer::new();
-    let fields = get_struct_fields_name(&u.fields);
-
-    if !fields.is_empty() {
-        print_sidebar_block(&mut sidebar, "fields", "Fields", fields.iter());
-    }
-
-    sidebar_assoc_items(cx, &mut sidebar, it);
-
-    if !sidebar.is_empty() {
-        write!(buf, "<section>{}</section>", sidebar.into_inner());
-    }
-}
-
-fn sidebar_enum(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) {
-    let mut sidebar = Buffer::new();
-
-    let mut variants = e
-        .variants()
-        .filter_map(|v| {
-            v.name
-                .as_ref()
-                .map(|name| format!("<a href=\"#variant.{name}\">{name}</a>", name = name))
-        })
-        .collect::<Vec<_>>();
-    if !variants.is_empty() {
-        variants.sort_unstable();
-        print_sidebar_block(&mut sidebar, "variants", "Variants", variants.iter());
-    }
-
-    sidebar_assoc_items(cx, &mut sidebar, it);
-
-    if !sidebar.is_empty() {
-        write!(buf, "<section>{}</section>", sidebar.into_inner());
-    }
-}
-
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub(crate) enum ItemSection {
     Reexports,
@@ -2719,54 +2148,6 @@ fn item_ty_to_section(ty: ItemType) -> ItemSection {
     }
 }
 
-pub(crate) fn sidebar_module_like(buf: &mut Buffer, item_sections_in_use: FxHashSet<ItemSection>) {
-    use std::fmt::Write as _;
-
-    let mut sidebar = String::new();
-
-    for &sec in ItemSection::ALL.iter().filter(|sec| item_sections_in_use.contains(sec)) {
-        let _ = write!(sidebar, "<li><a href=\"#{}\">{}</a></li>", sec.id(), sec.name());
-    }
-
-    if !sidebar.is_empty() {
-        write!(
-            buf,
-            "<section>\
-                 <ul class=\"block\">{}</ul>\
-             </section>",
-            sidebar
-        );
-    }
-}
-
-fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) {
-    let item_sections_in_use: FxHashSet<_> = items
-        .iter()
-        .filter(|it| {
-            !it.is_stripped()
-                && it
-                    .name
-                    .or_else(|| {
-                        if let clean::ImportItem(ref i) = *it.kind &&
-                            let clean::ImportKind::Simple(s) = i.kind { Some(s) } else { None }
-                    })
-                    .is_some()
-        })
-        .map(|it| item_ty_to_section(it.type_()))
-        .collect();
-
-    sidebar_module_like(buf, item_sections_in_use);
-}
-
-fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
-    let mut sidebar = Buffer::new();
-    sidebar_assoc_items(cx, &mut sidebar, it);
-
-    if !sidebar.is_empty() {
-        write!(buf, "<section>{}</section>", sidebar.into_inner());
-    }
-}
-
 /// Returns a list of all paths used in the type.
 /// This is used to help deduplicate imported impls
 /// for reexported types. If any of the contained
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 08796f10d92..577497868f6 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -470,10 +470,11 @@ fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) ->
 
     // The trailing space after each tag is to space it properly against the rest of the docs.
     if let Some(depr) = &item.deprecation(tcx) {
-        let mut message = "Deprecated";
-        if !stability::deprecation_in_effect(depr) {
-            message = "Deprecation planned";
-        }
+        let message = if stability::deprecation_in_effect(depr) {
+            "Deprecated"
+        } else {
+            "Deprecation planned"
+        };
         tags += &tag_html("deprecated", "", message);
     }
 
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index e22ac6ec19b..146221f5806 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -40,6 +40,7 @@ pub(crate) fn build_index<'tcx>(
                 parent_idx: None,
                 search_type: get_function_type_for_search(item, tcx, impl_generics.as_ref(), cache),
                 aliases: item.attrs.get_doc_aliases(),
+                deprecation: item.deprecation(tcx),
             });
         }
     }
@@ -251,7 +252,17 @@ pub(crate) fn build_index<'tcx>(
             )?;
             crate_data.serialize_field(
                 "q",
-                &self.items.iter().map(|item| &item.path).collect::<Vec<_>>(),
+                &self
+                    .items
+                    .iter()
+                    .enumerate()
+                    // Serialize as an array of item indices and full paths
+                    .filter_map(
+                        |(index, item)| {
+                            if item.path.is_empty() { None } else { Some((index, &item.path)) }
+                        },
+                    )
+                    .collect::<Vec<_>>(),
             )?;
             crate_data.serialize_field(
                 "d",
@@ -305,6 +316,16 @@ pub(crate) fn build_index<'tcx>(
                     .collect::<Vec<_>>(),
             )?;
             crate_data.serialize_field(
+                "c",
+                &self
+                    .items
+                    .iter()
+                    .enumerate()
+                    // Serialize as an array of deprecated item indices
+                    .filter_map(|(index, item)| item.deprecation.map(|_| index))
+                    .collect::<Vec<_>>(),
+            )?;
+            crate_data.serialize_field(
                 "p",
                 &self.paths.iter().map(|(it, s)| (it, s.as_str())).collect::<Vec<_>>(),
             )?;
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
new file mode 100644
index 00000000000..94ad4753d7c
--- /dev/null
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -0,0 +1,561 @@
+use std::{borrow::Cow, rc::Rc};
+
+use askama::Template;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir::{def::CtorKind, def_id::DefIdSet};
+use rustc_middle::ty::{self, TyCtxt};
+
+use crate::{
+    clean,
+    formats::{item_type::ItemType, Impl},
+    html::{format::Buffer, markdown::IdMap},
+};
+
+use super::{item_ty_to_section, Context, ItemSection};
+
+#[derive(Template)]
+#[template(path = "sidebar.html")]
+pub(super) struct Sidebar<'a> {
+    pub(super) title_prefix: &'static str,
+    pub(super) title: &'a str,
+    pub(super) is_crate: bool,
+    pub(super) version: &'a str,
+    pub(super) blocks: Vec<LinkBlock<'a>>,
+    pub(super) path: String,
+}
+
+impl<'a> Sidebar<'a> {
+    /// Only create a `<section>` if there are any blocks
+    /// which should actually be rendered.
+    pub fn should_render_blocks(&self) -> bool {
+        self.blocks.iter().any(LinkBlock::should_render)
+    }
+}
+
+/// A sidebar section such as 'Methods'.
+pub(crate) struct LinkBlock<'a> {
+    /// The name of this section, e.g. 'Methods'
+    /// as well as the link to it, e.g. `#implementations`.
+    /// Will be rendered inside an `<h3>` tag
+    heading: Link<'a>,
+    links: Vec<Link<'a>>,
+    /// Render the heading even if there are no links
+    force_render: bool,
+}
+
+impl<'a> LinkBlock<'a> {
+    pub fn new(heading: Link<'a>, links: Vec<Link<'a>>) -> Self {
+        Self { heading, links, force_render: false }
+    }
+
+    pub fn forced(heading: Link<'a>) -> Self {
+        Self { heading, links: vec![], force_render: true }
+    }
+
+    pub fn should_render(&self) -> bool {
+        self.force_render || !self.links.is_empty()
+    }
+}
+
+/// A link to an item. Content should not be escaped.
+#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone)]
+pub(crate) struct Link<'a> {
+    /// The content for the anchor tag
+    name: Cow<'a, str>,
+    /// The id of an anchor within the page (without a `#` prefix)
+    href: Cow<'a, str>,
+}
+
+impl<'a> Link<'a> {
+    pub fn new(href: impl Into<Cow<'a, str>>, name: impl Into<Cow<'a, str>>) -> Self {
+        Self { href: href.into(), name: name.into() }
+    }
+    pub fn empty() -> Link<'static> {
+        Link::new("", "")
+    }
+}
+
+pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
+    let blocks: Vec<LinkBlock<'_>> = match *it.kind {
+        clean::StructItem(ref s) => sidebar_struct(cx, it, s),
+        clean::TraitItem(ref t) => sidebar_trait(cx, it, t),
+        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::TypedefItem(_) => sidebar_typedef(cx, it),
+        clean::ModuleItem(ref m) => vec![sidebar_module(&m.items)],
+        clean::ForeignTypeItem => sidebar_foreign_type(cx, it),
+        _ => vec![],
+    };
+    // The sidebar is designed to display sibling functions, modules and
+    // other miscellaneous information. since there are lots of sibling
+    // items (and that causes quadratic growth in large modules),
+    // we refactor common parts into a shared JavaScript file per module.
+    // still, we don't move everything into JS because we want to preserve
+    // as much HTML as possible in order to allow non-JS-enabled browsers
+    // to navigate the documentation (though slightly inefficiently).
+    let (title_prefix, title) = if it.is_struct()
+        || it.is_trait()
+        || it.is_primitive()
+        || it.is_union()
+        || it.is_enum()
+        || it.is_mod()
+        || it.is_typedef()
+    {
+        (
+            match *it.kind {
+                clean::ModuleItem(..) if it.is_crate() => "Crate ",
+                clean::ModuleItem(..) => "Module ",
+                _ => "",
+            },
+            it.name.as_ref().unwrap().as_str(),
+        )
+    } else {
+        ("", "")
+    };
+    let version = if it.is_crate() {
+        cx.cache().crate_version.as_ref().map(String::as_str).unwrap_or_default()
+    } else {
+        ""
+    };
+    let path: String = if !it.is_mod() {
+        cx.current.iter().map(|s| s.as_str()).intersperse("::").collect()
+    } else {
+        "".into()
+    };
+    let sidebar = Sidebar { title_prefix, title, is_crate: it.is_crate(), version, blocks, path };
+    sidebar.render_into(buffer).unwrap();
+}
+
+fn get_struct_fields_name<'a>(fields: &'a [clean::Item]) -> Vec<Link<'a>> {
+    let mut fields = fields
+        .iter()
+        .filter(|f| matches!(*f.kind, clean::StructFieldItem(..)))
+        .filter_map(|f| {
+            f.name.as_ref().map(|name| Link::new(format!("structfield.{name}"), name.as_str()))
+        })
+        .collect::<Vec<Link<'a>>>();
+    fields.sort();
+    fields
+}
+
+fn sidebar_struct<'a>(
+    cx: &'a Context<'_>,
+    it: &'a clean::Item,
+    s: &'a clean::Struct,
+) -> Vec<LinkBlock<'a>> {
+    let fields = get_struct_fields_name(&s.fields);
+    let field_name = match s.ctor_kind {
+        Some(CtorKind::Fn) => Some("Tuple Fields"),
+        None => Some("Fields"),
+        _ => None,
+    };
+    let mut items = vec![];
+    if let Some(name) = field_name {
+        items.push(LinkBlock::new(Link::new("fields", name), fields));
+    }
+    sidebar_assoc_items(cx, it, &mut items);
+    items
+}
+
+fn sidebar_trait<'a>(
+    cx: &'a Context<'_>,
+    it: &'a clean::Item,
+    t: &'a clean::Trait,
+) -> Vec<LinkBlock<'a>> {
+    fn filter_items<'a>(
+        items: &'a [clean::Item],
+        filt: impl Fn(&clean::Item) -> bool,
+        ty: &str,
+    ) -> Vec<Link<'a>> {
+        let mut res = items
+            .iter()
+            .filter_map(|m: &clean::Item| match m.name {
+                Some(ref name) if filt(m) => Some(Link::new(format!("{ty}.{name}"), name.as_str())),
+                _ => None,
+            })
+            .collect::<Vec<Link<'a>>>();
+        res.sort();
+        res
+    }
+
+    let req_assoc = filter_items(&t.items, |m| m.is_ty_associated_type(), "associatedtype");
+    let prov_assoc = filter_items(&t.items, |m| m.is_associated_type(), "associatedtype");
+    let req_assoc_const =
+        filter_items(&t.items, |m| m.is_ty_associated_const(), "associatedconstant");
+    let prov_assoc_const =
+        filter_items(&t.items, |m| m.is_associated_const(), "associatedconstant");
+    let req_method = filter_items(&t.items, |m| m.is_ty_method(), "tymethod");
+    let prov_method = filter_items(&t.items, |m| m.is_method(), "method");
+    let mut foreign_impls = vec![];
+    if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) {
+        foreign_impls.extend(
+            implementors
+                .iter()
+                .filter(|i| !i.is_on_local_type(cx))
+                .filter_map(|i| super::extract_for_impl_name(&i.impl_item, cx))
+                .map(|(name, id)| Link::new(id, name)),
+        );
+        foreign_impls.sort();
+    }
+
+    let mut blocks: Vec<LinkBlock<'_>> = [
+        ("required-associated-types", "Required Associated Types", req_assoc),
+        ("provided-associated-types", "Provided Associated Types", prov_assoc),
+        ("required-associated-consts", "Required Associated Constants", req_assoc_const),
+        ("provided-associated-consts", "Provided Associated Constants", prov_assoc_const),
+        ("required-methods", "Required Methods", req_method),
+        ("provided-methods", "Provided Methods", prov_method),
+        ("foreign-impls", "Implementations on Foreign Types", foreign_impls),
+    ]
+    .into_iter()
+    .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), items))
+    .collect();
+    sidebar_assoc_items(cx, it, &mut blocks);
+    blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors")));
+    if t.is_auto(cx.tcx()) {
+        blocks.push(LinkBlock::forced(Link::new("synthetic-implementors", "Auto Implementors")));
+    }
+    blocks
+}
+
+fn sidebar_primitive<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> {
+    if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
+        let mut items = vec![];
+        sidebar_assoc_items(cx, it, &mut items);
+        items
+    } else {
+        let shared = Rc::clone(&cx.shared);
+        let (concrete, synthetic, blanket_impl) =
+            super::get_filtered_impls_for_reference(&shared, it);
+
+        sidebar_render_assoc_items(cx, &mut IdMap::new(), concrete, synthetic, blanket_impl).into()
+    }
+}
+
+fn sidebar_typedef<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> {
+    let mut items = vec![];
+    sidebar_assoc_items(cx, it, &mut items);
+    items
+}
+
+fn sidebar_union<'a>(
+    cx: &'a Context<'_>,
+    it: &'a clean::Item,
+    u: &'a clean::Union,
+) -> Vec<LinkBlock<'a>> {
+    let fields = get_struct_fields_name(&u.fields);
+    let mut items = vec![LinkBlock::new(Link::new("fields", "Fields"), fields)];
+    sidebar_assoc_items(cx, it, &mut items);
+    items
+}
+
+/// Adds trait implementations into the blocks of links
+fn sidebar_assoc_items<'a>(
+    cx: &'a Context<'_>,
+    it: &'a clean::Item,
+    links: &mut Vec<LinkBlock<'a>>,
+) {
+    let did = it.item_id.expect_def_id();
+    let cache = cx.cache();
+
+    let mut assoc_consts = Vec::new();
+    let mut methods = Vec::new();
+    if let Some(v) = cache.impls.get(&did) {
+        let mut used_links = FxHashSet::default();
+        let mut id_map = IdMap::new();
+
+        {
+            let used_links_bor = &mut used_links;
+            assoc_consts.extend(
+                v.iter()
+                    .filter(|i| i.inner_impl().trait_.is_none())
+                    .flat_map(|i| get_associated_constants(i.inner_impl(), used_links_bor)),
+            );
+            // We want links' order to be reproducible so we don't use unstable sort.
+            assoc_consts.sort();
+
+            #[rustfmt::skip] // rustfmt makes the pipeline less readable
+            methods.extend(
+                v.iter()
+                    .filter(|i| i.inner_impl().trait_.is_none())
+                    .flat_map(|i| get_methods(i.inner_impl(), false, used_links_bor, false, cx.tcx())),
+            );
+
+            // We want links' order to be reproducible so we don't use unstable sort.
+            methods.sort();
+        }
+
+        let mut deref_methods = Vec::new();
+        let [concrete, synthetic, blanket] = if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
+            if let Some(impl_) =
+                v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait())
+            {
+                let mut derefs = DefIdSet::default();
+                derefs.insert(did);
+                sidebar_deref_methods(
+                    cx,
+                    &mut deref_methods,
+                    impl_,
+                    v,
+                    &mut derefs,
+                    &mut used_links,
+                );
+            }
+
+            let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
+                v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto());
+            let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
+                concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket());
+
+            sidebar_render_assoc_items(cx, &mut id_map, concrete, synthetic, blanket_impl)
+        } else {
+            std::array::from_fn(|_| LinkBlock::new(Link::empty(), vec![]))
+        };
+
+        let mut blocks = vec![
+            LinkBlock::new(Link::new("implementations", "Associated Constants"), assoc_consts),
+            LinkBlock::new(Link::new("implementations", "Methods"), methods),
+        ];
+        blocks.append(&mut deref_methods);
+        blocks.extend([concrete, synthetic, blanket]);
+        links.append(&mut blocks);
+    }
+}
+
+fn sidebar_deref_methods<'a>(
+    cx: &'a Context<'_>,
+    out: &mut Vec<LinkBlock<'a>>,
+    impl_: &Impl,
+    v: &[Impl],
+    derefs: &mut DefIdSet,
+    used_links: &mut FxHashSet<String>,
+) {
+    let c = cx.cache();
+
+    debug!("found Deref: {:?}", impl_);
+    if let Some((target, real_target)) =
+        impl_.inner_impl().items.iter().find_map(|item| match *item.kind {
+            clean::AssocTypeItem(box ref t, _) => Some(match *t {
+                clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
+                _ => (&t.type_, &t.type_),
+            }),
+            _ => None,
+        })
+    {
+        debug!("found target, real_target: {:?} {:?}", target, real_target);
+        if let Some(did) = target.def_id(c) &&
+            let Some(type_did) = impl_.inner_impl().for_.def_id(c) &&
+            // `impl Deref<Target = S> for S`
+            (did == type_did || !derefs.insert(did))
+        {
+            // Avoid infinite cycles
+            return;
+        }
+        let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
+        let inner_impl = target
+            .def_id(c)
+            .or_else(|| {
+                target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned())
+            })
+            .and_then(|did| c.impls.get(&did));
+        if let Some(impls) = inner_impl {
+            debug!("found inner_impl: {:?}", impls);
+            let mut ret = impls
+                .iter()
+                .filter(|i| i.inner_impl().trait_.is_none())
+                .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx()))
+                .collect::<Vec<_>>();
+            if !ret.is_empty() {
+                let id = if let Some(target_def_id) = real_target.def_id(c) {
+                    Cow::Borrowed(
+                        cx.deref_id_map
+                            .get(&target_def_id)
+                            .expect("Deref section without derived id")
+                            .as_str(),
+                    )
+                } else {
+                    Cow::Borrowed("deref-methods")
+                };
+                let title = format!(
+                    "Methods from {:#}<Target={:#}>",
+                    impl_.inner_impl().trait_.as_ref().unwrap().print(cx),
+                    real_target.print(cx),
+                );
+                // We want links' order to be reproducible so we don't use unstable sort.
+                ret.sort();
+                out.push(LinkBlock::new(Link::new(id, title), ret));
+            }
+        }
+
+        // Recurse into any further impls that might exist for `target`
+        if let Some(target_did) = target.def_id(c) &&
+            let Some(target_impls) = c.impls.get(&target_did) &&
+            let Some(target_deref_impl) = target_impls.iter().find(|i| {
+                i.inner_impl()
+                    .trait_
+                    .as_ref()
+                    .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait())
+                    .unwrap_or(false)
+            })
+        {
+            sidebar_deref_methods(
+                cx,
+                out,
+                target_deref_impl,
+                target_impls,
+                derefs,
+                used_links,
+            );
+        }
+    }
+}
+
+fn sidebar_enum<'a>(
+    cx: &'a Context<'_>,
+    it: &'a clean::Item,
+    e: &'a clean::Enum,
+) -> Vec<LinkBlock<'a>> {
+    let mut variants = e
+        .variants()
+        .filter_map(|v| v.name)
+        .map(|name| Link::new(format!("variant.{name}"), name.to_string()))
+        .collect::<Vec<_>>();
+    variants.sort_unstable();
+
+    let mut items = vec![LinkBlock::new(Link::new("variants", "Variants"), variants)];
+    sidebar_assoc_items(cx, it, &mut items);
+    items
+}
+
+pub(crate) fn sidebar_module_like(
+    item_sections_in_use: FxHashSet<ItemSection>,
+) -> LinkBlock<'static> {
+    let item_sections = ItemSection::ALL
+        .iter()
+        .copied()
+        .filter(|sec| item_sections_in_use.contains(sec))
+        .map(|sec| Link::new(sec.id(), sec.name()))
+        .collect();
+    LinkBlock::new(Link::empty(), item_sections)
+}
+
+fn sidebar_module(items: &[clean::Item]) -> LinkBlock<'static> {
+    let item_sections_in_use: FxHashSet<_> = items
+        .iter()
+        .filter(|it| {
+            !it.is_stripped()
+                && it
+                    .name
+                    .or_else(|| {
+                        if let clean::ImportItem(ref i) = *it.kind &&
+                            let clean::ImportKind::Simple(s) = i.kind { Some(s) } else { None }
+                    })
+                    .is_some()
+        })
+        .map(|it| item_ty_to_section(it.type_()))
+        .collect();
+
+    sidebar_module_like(item_sections_in_use)
+}
+
+fn sidebar_foreign_type<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> {
+    let mut items = vec![];
+    sidebar_assoc_items(cx, it, &mut items);
+    items
+}
+
+/// Renders the trait implementations for this type
+fn sidebar_render_assoc_items(
+    cx: &Context<'_>,
+    id_map: &mut IdMap,
+    concrete: Vec<&Impl>,
+    synthetic: Vec<&Impl>,
+    blanket_impl: Vec<&Impl>,
+) -> [LinkBlock<'static>; 3] {
+    let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
+        let mut links = FxHashSet::default();
+
+        let mut ret = impls
+            .iter()
+            .filter_map(|it| {
+                let trait_ = it.inner_impl().trait_.as_ref()?;
+                let encoded =
+                    id_map.derive(super::get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx));
+
+                let prefix = match it.inner_impl().polarity {
+                    ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
+                    ty::ImplPolarity::Negative => "!",
+                };
+                let generated = Link::new(encoded, format!("{prefix}{:#}", trait_.print(cx)));
+                if links.insert(generated.clone()) { Some(generated) } else { None }
+            })
+            .collect::<Vec<Link<'static>>>();
+        ret.sort();
+        ret
+    };
+
+    let concrete = format_impls(concrete, id_map);
+    let synthetic = format_impls(synthetic, id_map);
+    let blanket = format_impls(blanket_impl, id_map);
+    [
+        LinkBlock::new(Link::new("trait-implementations", "Trait Implementations"), concrete),
+        LinkBlock::new(
+            Link::new("synthetic-implementations", "Auto Trait Implementations"),
+            synthetic,
+        ),
+        LinkBlock::new(Link::new("blanket-implementations", "Blanket Implementations"), blanket),
+    ]
+}
+
+fn get_next_url(used_links: &mut FxHashSet<String>, url: String) -> String {
+    if used_links.insert(url.clone()) {
+        return url;
+    }
+    let mut add = 1;
+    while !used_links.insert(format!("{}-{}", url, add)) {
+        add += 1;
+    }
+    format!("{}-{}", url, add)
+}
+
+fn get_methods<'a>(
+    i: &'a clean::Impl,
+    for_deref: bool,
+    used_links: &mut FxHashSet<String>,
+    deref_mut: bool,
+    tcx: TyCtxt<'_>,
+) -> Vec<Link<'a>> {
+    i.items
+        .iter()
+        .filter_map(|item| match item.name {
+            Some(ref name) if !name.is_empty() && item.is_method() => {
+                if !for_deref || super::should_render_item(item, deref_mut, tcx) {
+                    Some(Link::new(
+                        get_next_url(used_links, format!("{}.{}", ItemType::Method, name)),
+                        name.as_str(),
+                    ))
+                } else {
+                    None
+                }
+            }
+            _ => None,
+        })
+        .collect::<Vec<_>>()
+}
+
+fn get_associated_constants<'a>(
+    i: &'a clean::Impl,
+    used_links: &mut FxHashSet<String>,
+) -> Vec<Link<'a>> {
+    i.items
+        .iter()
+        .filter_map(|item| match item.name {
+            Some(ref name) if !name.is_empty() && item.is_associated_const() => Some(Link::new(
+                get_next_url(used_links, format!("{}.{}", ItemType::AssocConst, name)),
+                name.as_str(),
+            )),
+            _ => None,
+        })
+        .collect::<Vec<_>>()
+}
diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs
index 4514894cabe..eb9262f472b 100644
--- a/src/librustdoc/html/render/span_map.rs
+++ b/src/librustdoc/html/render/span_map.rs
@@ -29,12 +29,12 @@ pub(crate) enum LinkFromSrc {
 
 /// This function will do at most two things:
 ///
-/// 1. Generate a `span` correspondance map which links an item `span` to its definition `span`.
+/// 1. Generate a `span` correspondence map which links an item `span` to its definition `span`.
 /// 2. Collect the source code files.
 ///
-/// It returns the `krate`, the source code files and the `span` correspondance map.
+/// It returns the `krate`, the source code files and the `span` correspondence map.
 ///
-/// Note about the `span` correspondance map: the keys are actually `(lo, hi)` of `span`s. We don't
+/// Note about the `span` correspondence map: the keys are actually `(lo, hi)` of `span`s. We don't
 /// need the `span` context later on, only their position, so instead of keep a whole `Span`, we
 /// only keep the `lo` and `hi`.
 pub(crate) fn collect_spans_and_sources(
diff --git a/src/librustdoc/html/static/COPYRIGHT.txt b/src/librustdoc/html/static/COPYRIGHT.txt
index 34e48134cc3..1447df792f6 100644
--- a/src/librustdoc/html/static/COPYRIGHT.txt
+++ b/src/librustdoc/html/static/COPYRIGHT.txt
@@ -1,3 +1,5 @@
+# REUSE-IgnoreStart
+
 These documentation pages include resources by third parties. This copyright
 file applies only to those resources. The following third party resources are
 included, and carry their own copyright notices and license terms:
@@ -44,3 +46,5 @@ included, and carry their own copyright notices and license terms:
     See SourceSerif4-LICENSE.md.
 
 This copyright file is intended to be distributed with rustdoc output.
+
+# REUSE-IgnoreEnd
diff --git a/src/librustdoc/html/static/fonts/FiraSans-LICENSE.txt b/src/librustdoc/html/static/fonts/FiraSans-LICENSE.txt
index ff9afab064a..d7e9c149b7e 100644
--- a/src/librustdoc/html/static/fonts/FiraSans-LICENSE.txt
+++ b/src/librustdoc/html/static/fonts/FiraSans-LICENSE.txt
@@ -1,3 +1,5 @@
+// REUSE-IgnoreStart
+
 Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A.
 with Reserved Font Name < Fira >,
 
@@ -92,3 +94,5 @@ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
 DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
 OTHER DEALINGS IN THE FONT SOFTWARE.
+
+// REUSE-IgnoreEnd
diff --git a/src/librustdoc/html/static/fonts/NanumBarunGothic-LICENSE.txt b/src/librustdoc/html/static/fonts/NanumBarunGothic-LICENSE.txt
index 0bf46682b5b..4b3edc29eb9 100644
--- a/src/librustdoc/html/static/fonts/NanumBarunGothic-LICENSE.txt
+++ b/src/librustdoc/html/static/fonts/NanumBarunGothic-LICENSE.txt
@@ -1,3 +1,5 @@
+// REUSE-IgnoreStart
+
 Copyright (c) 2010, NAVER Corporation (https://www.navercorp.com/),
 
 with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic,
@@ -97,3 +99,5 @@ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
 DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
 OTHER DEALINGS IN THE FONT SOFTWARE.
+
+// REUSE-IgnoreEnd
diff --git a/src/librustdoc/html/static/fonts/SourceCodePro-LICENSE.txt b/src/librustdoc/html/static/fonts/SourceCodePro-LICENSE.txt
index 07542572e33..0d2941e148d 100644
--- a/src/librustdoc/html/static/fonts/SourceCodePro-LICENSE.txt
+++ b/src/librustdoc/html/static/fonts/SourceCodePro-LICENSE.txt
@@ -1,3 +1,5 @@
+// REUSE-IgnoreStart
+
 Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
 
 This Font Software is licensed under the SIL Open Font License, Version 1.1.
@@ -91,3 +93,5 @@ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
 DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
 OTHER DEALINGS IN THE FONT SOFTWARE.
+
+// REUSE-IgnoreEnd
diff --git a/src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md b/src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md
index 5871e1f3d1b..175fa4f47ae 100644
--- a/src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md
+++ b/src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md
@@ -1,3 +1,6 @@
+<!-- REUSE-IgnoreStart -->
+
+Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries.
 Copyright 2014 - 2023 Adobe (http://www.adobe.com/), with Reserved Font Name ‘Source’. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries.
 
 This Font Software is licensed under the SIL Open Font License, Version 1.1.
@@ -91,3 +94,5 @@ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
 DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
 OTHER DEALINGS IN THE FONT SOFTWARE.
+
+<!-- REUSE-IgnoreEnd -->
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 5e8c0e8d10c..403b5004d65 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -1,20 +1,9 @@
 // Local js definitions:
 /* global addClass, getSettingValue, hasClass, searchState */
-/* global onEach, onEachLazy, removeClass */
+/* global onEach, onEachLazy, removeClass, getVar */
 
 "use strict";
 
-// Get a value from the rustdoc-vars div, which is used to convey data from
-// Rust to the JS. If there is no such element, return null.
-function getVar(name) {
-    const el = document.getElementById("rustdoc-vars");
-    if (el) {
-        return el.attributes["data-" + name].value;
-    } else {
-        return null;
-    }
-}
-
 // Given a basename (e.g. "storage") and an extension (e.g. ".js"), return a URL
 // for a resource under the root-path, with the resource-suffix.
 function resourcePath(basename, extension) {
@@ -187,6 +176,15 @@ function loadCss(cssUrl) {
     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");
+    link.href = cssUrl;
+    link.rel = "preload";
+    link.as = "style";
+    document.getElementsByTagName("head")[0].appendChild(link);
+}
+
 (function() {
     const isHelpPage = window.location.pathname.endsWith("/help.html");
 
@@ -207,6 +205,23 @@ function loadCss(cssUrl) {
         // 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
+        // generated in context.rs.
+        setTimeout(() => {
+            const themes = getVar("themes").split(",");
+            for (const theme of themes) {
+                // if there are no themes, do nothing
+                // "".split(",") == [""]
+                if (theme !== "") {
+                    preLoadCss(getVar("root-path") + theme + ".css");
+                }
+            }
+        }, 0);
     };
 
     window.searchState = {
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index b98bced4126..c71ce2c3001 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -76,39 +76,111 @@ function printTab(nb) {
 }
 
 /**
- * A function to compute the Levenshtein distance between two strings
- * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported
- * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode
- * This code is an unmodified version of the code written by Marco de Wit
- * and was found at https://stackoverflow.com/a/18514751/745719
+ * The [edit distance] is a metric for measuring the difference between two strings.
+ *
+ * [edit distance]: https://en.wikipedia.org/wiki/Edit_distance
  */
-const levenshtein_row2 = [];
-function levenshtein(s1, s2) {
-    if (s1 === s2) {
-        return 0;
-    }
-    const s1_len = s1.length, s2_len = s2.length;
-    if (s1_len && s2_len) {
-        let i1 = 0, i2 = 0, a, b, c, c2;
-        const row = levenshtein_row2;
-        while (i1 < s1_len) {
-            row[i1] = ++i1;
-        }
-        while (i2 < s2_len) {
-            c2 = s2.charCodeAt(i2);
-            a = i2;
-            ++i2;
-            b = i2;
-            for (i1 = 0; i1 < s1_len; ++i1) {
-                c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0);
-                a = row[i1];
-                b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
-                row[i1] = b;
-            }
-        }
-        return b;
-    }
-    return s1_len + s2_len;
+
+/*
+ * This function was translated, mostly line-for-line, from
+ * https://github.com/rust-lang/rust/blob/ff4b772f805ec1e/compiler/rustc_span/src/edit_distance.rs
+ *
+ * The current implementation is the restricted Damerau-Levenshtein algorithm. It is restricted
+ * because it does not permit modifying characters that have already been transposed. The specific
+ * algorithm should not matter to the caller of the methods, which is why it is not noted in the
+ * documentation.
+ */
+const editDistanceState = {
+    current: [],
+    prev: [],
+    prevPrev: [],
+    calculate: function calculate(a, b, limit) {
+        // Ensure that `b` is the shorter string, minimizing memory use.
+        if (a.length < b.length) {
+            const aTmp = a;
+            a = b;
+            b = aTmp;
+        }
+
+        const minDist = a.length - b.length;
+        // If we know the limit will be exceeded, we can return early.
+        if (minDist > limit) {
+            return limit + 1;
+        }
+
+        // Strip common prefix.
+        // We know that `b` is the shorter string, so we don't need to check
+        // `a.length`.
+        while (b.length > 0 && b[0] === a[0]) {
+            a = a.substring(1);
+            b = b.substring(1);
+        }
+        // Strip common suffix.
+        while (b.length > 0 && b[b.length - 1] === a[a.length - 1]) {
+            a = a.substring(0, a.length - 1);
+            b = b.substring(0, b.length - 1);
+        }
+
+        // If either string is empty, the distance is the length of the other.
+        // We know that `b` is the shorter string, so we don't need to check `a`.
+        if (b.length === 0) {
+            return minDist;
+        }
+
+        const aLength = a.length;
+        const bLength = b.length;
+
+        for (let i = 0; i <= bLength; ++i) {
+            this.current[i] = 0;
+            this.prev[i] = i;
+            this.prevPrev[i] = Number.MAX_VALUE;
+        }
+
+        // row by row
+        for (let i = 1; i <= aLength; ++i) {
+            this.current[0] = i;
+            const aIdx = i - 1;
+
+            // column by column
+            for (let j = 1; j <= bLength; ++j) {
+                const bIdx = j - 1;
+
+                // There is no cost to substitute a character with itself.
+                const substitutionCost = a[aIdx] === b[bIdx] ? 0 : 1;
+
+                this.current[j] = Math.min(
+                    // deletion
+                    this.prev[j] + 1,
+                    // insertion
+                    this.current[j - 1] + 1,
+                    // substitution
+                    this.prev[j - 1] + substitutionCost
+                );
+
+                if ((i > 1) && (j > 1) && (a[aIdx] === b[bIdx - 1]) && (a[aIdx - 1] === b[bIdx])) {
+                    // transposition
+                    this.current[j] = Math.min(
+                        this.current[j],
+                        this.prevPrev[j - 2] + 1
+                    );
+                }
+            }
+
+            // Rotate the buffers, reusing the memory
+            const prevPrevTmp = this.prevPrev;
+            this.prevPrev = this.prev;
+            this.prev = this.current;
+            this.current = prevPrevTmp;
+        }
+
+        // `prev` because we already rotated the buffers.
+        const distance = this.prev[bLength];
+        return distance <= limit ? distance : (limit + 1);
+    },
+};
+
+function editDistance(a, b, limit) {
+    return editDistanceState.calculate(a, b, limit);
 }
 
 function initSearch(rawSearchIndex) {
@@ -802,7 +874,7 @@ function initSearch(rawSearchIndex) {
             for (const result of results) {
                 if (result.id > -1) {
                     const obj = searchIndex[result.id];
-                    obj.lev = result.lev;
+                    obj.dist = result.dist;
                     const res = buildHrefAndPath(obj);
                     obj.displayPath = pathSplitter(res[0]);
                     obj.fullPath = obj.displayPath + obj.name;
@@ -860,8 +932,8 @@ function initSearch(rawSearchIndex) {
 
                 // Sort by distance in the path part, if specified
                 // (less changes required to match means higher rankings)
-                a = aaa.path_lev;
-                b = bbb.path_lev;
+                a = aaa.path_dist;
+                b = bbb.path_dist;
                 if (a !== b) {
                     return a - b;
                 }
@@ -875,8 +947,15 @@ function initSearch(rawSearchIndex) {
 
                 // Sort by distance in the name part, the last part of the path
                 // (less changes required to match means higher rankings)
-                a = (aaa.lev);
-                b = (bbb.lev);
+                a = (aaa.dist);
+                b = (bbb.dist);
+                if (a !== b) {
+                    return a - b;
+                }
+
+                // sort deprecated items later
+                a = aaa.item.deprecated;
+                b = bbb.item.deprecated;
                 if (a !== b) {
                     return a - b;
                 }
@@ -961,19 +1040,20 @@ function initSearch(rawSearchIndex) {
 
         /**
          * This function checks if the object (`row`) generics match the given type (`elem`)
-         * generics. If there are no generics on `row`, `defaultLev` is returned.
+         * generics. If there are no generics on `row`, `defaultDistance` is returned.
          *
-         * @param {Row} row            - The object to check.
-         * @param {QueryElement} elem  - The element from the parsed query.
-         * @param {integer} defaultLev - This is the value to return in case there are no generics.
+         * @param {Row} row                 - The object to check.
+         * @param {QueryElement} elem       - The element from the parsed query.
+         * @param {integer} defaultDistance - This is the value to return in case there are no
+         *                                    generics.
          *
-         * @return {integer}           - Returns the best match (if any) or `maxLevDistance + 1`.
+         * @return {integer}           - Returns the best match (if any) or `maxEditDistance + 1`.
          */
-        function checkGenerics(row, elem, defaultLev, maxLevDistance) {
+        function checkGenerics(row, elem, defaultDistance, maxEditDistance) {
             if (row.generics.length === 0) {
-                return elem.generics.length === 0 ? defaultLev : maxLevDistance + 1;
+                return elem.generics.length === 0 ? defaultDistance : maxEditDistance + 1;
             } else if (row.generics.length > 0 && row.generics[0].name === null) {
-                return checkGenerics(row.generics[0], elem, defaultLev, maxLevDistance);
+                return checkGenerics(row.generics[0], elem, defaultDistance, maxEditDistance);
             }
             // The names match, but we need to be sure that all generics kinda
             // match as well.
@@ -984,8 +1064,9 @@ function initSearch(rawSearchIndex) {
                     elem_name = entry.name;
                     if (elem_name === "") {
                         // Pure generic, needs to check into it.
-                        if (checkGenerics(entry, elem, maxLevDistance + 1, maxLevDistance) !== 0) {
-                            return maxLevDistance + 1;
+                        if (checkGenerics(entry, elem, maxEditDistance + 1, maxEditDistance)
+                            !== 0) {
+                            return maxEditDistance + 1;
                         }
                         continue;
                     }
@@ -1012,7 +1093,7 @@ function initSearch(rawSearchIndex) {
                         }
                     }
                     if (match === null) {
-                        return maxLevDistance + 1;
+                        return maxEditDistance + 1;
                     }
                     elems[match] -= 1;
                     if (elems[match] === 0) {
@@ -1021,7 +1102,7 @@ function initSearch(rawSearchIndex) {
                 }
                 return 0;
             }
-            return maxLevDistance + 1;
+            return maxEditDistance + 1;
         }
 
         /**
@@ -1031,17 +1112,17 @@ function initSearch(rawSearchIndex) {
           * @param {Row} row
           * @param {QueryElement} elem    - The element from the parsed query.
           *
-          * @return {integer} - Returns a Levenshtein distance to the best match.
+          * @return {integer} - Returns an edit distance to the best match.
           */
-        function checkIfInGenerics(row, elem, maxLevDistance) {
-            let lev = maxLevDistance + 1;
+        function checkIfInGenerics(row, elem, maxEditDistance) {
+            let dist = maxEditDistance + 1;
             for (const entry of row.generics) {
-                lev = Math.min(checkType(entry, elem, true, maxLevDistance), lev);
-                if (lev === 0) {
+                dist = Math.min(checkType(entry, elem, true, maxEditDistance), dist);
+                if (dist === 0) {
                     break;
                 }
             }
-            return lev;
+            return dist;
         }
 
         /**
@@ -1052,21 +1133,21 @@ function initSearch(rawSearchIndex) {
           * @param {QueryElement} elem      - The element from the parsed query.
           * @param {boolean} literalSearch
           *
-          * @return {integer} - Returns a Levenshtein distance to the best match. If there is
-          *                     no match, returns `maxLevDistance + 1`.
+          * @return {integer} - Returns an edit distance to the best match. If there is
+          *                     no match, returns `maxEditDistance + 1`.
           */
-        function checkType(row, elem, literalSearch, maxLevDistance) {
+        function checkType(row, elem, literalSearch, maxEditDistance) {
             if (row.name === null) {
                 // This is a pure "generic" search, no need to run other checks.
                 if (row.generics.length > 0) {
-                    return checkIfInGenerics(row, elem, maxLevDistance);
+                    return checkIfInGenerics(row, elem, maxEditDistance);
                 }
-                return maxLevDistance + 1;
+                return maxEditDistance + 1;
             }
 
-            let lev = levenshtein(row.name, elem.name);
+            let dist = editDistance(row.name, elem.name, maxEditDistance);
             if (literalSearch) {
-                if (lev !== 0) {
+                if (dist !== 0) {
                     // The name didn't match, let's try to check if the generics do.
                     if (elem.generics.length === 0) {
                         const checkGeneric = row.generics.length > 0;
@@ -1075,44 +1156,44 @@ function initSearch(rawSearchIndex) {
                             return 0;
                         }
                     }
-                    return maxLevDistance + 1;
+                    return maxEditDistance + 1;
                 } else if (elem.generics.length > 0) {
-                    return checkGenerics(row, elem, maxLevDistance + 1, maxLevDistance);
+                    return checkGenerics(row, elem, maxEditDistance + 1, maxEditDistance);
                 }
                 return 0;
             } else if (row.generics.length > 0) {
                 if (elem.generics.length === 0) {
-                    if (lev === 0) {
+                    if (dist === 0) {
                         return 0;
                     }
                     // The name didn't match so we now check if the type we're looking for is inside
                     // the generics!
-                    lev = Math.min(lev, checkIfInGenerics(row, elem, maxLevDistance));
-                    return lev;
-                } else if (lev > maxLevDistance) {
+                    dist = Math.min(dist, checkIfInGenerics(row, elem, maxEditDistance));
+                    return dist;
+                } else if (dist > maxEditDistance) {
                     // So our item's name doesn't match at all and has generics.
                     //
                     // Maybe it's present in a sub generic? For example "f<A<B<C>>>()", if we're
                     // looking for "B<C>", we'll need to go down.
-                    return checkIfInGenerics(row, elem, maxLevDistance);
+                    return checkIfInGenerics(row, elem, maxEditDistance);
                 } else {
                     // At this point, the name kinda match and we have generics to check, so
                     // let's go!
-                    const tmp_lev = checkGenerics(row, elem, lev, maxLevDistance);
-                    if (tmp_lev > maxLevDistance) {
-                        return maxLevDistance + 1;
+                    const tmp_dist = checkGenerics(row, elem, dist, maxEditDistance);
+                    if (tmp_dist > maxEditDistance) {
+                        return maxEditDistance + 1;
                     }
                     // We compute the median value of both checks and return it.
-                    return (tmp_lev + lev) / 2;
+                    return (tmp_dist + dist) / 2;
                 }
             } else if (elem.generics.length > 0) {
                 // In this case, we were expecting generics but there isn't so we simply reject this
                 // one.
-                return maxLevDistance + 1;
+                return maxEditDistance + 1;
             }
             // No generics on our query or on the target type so we can return without doing
             // anything else.
-            return lev;
+            return dist;
         }
 
         /**
@@ -1122,27 +1203,27 @@ function initSearch(rawSearchIndex) {
          * @param {QueryElement} elem    - The element from the parsed query.
          * @param {integer} typeFilter
          *
-         * @return {integer} - Returns a Levenshtein distance to the best match. If there is no
-         *                      match, returns `maxLevDistance + 1`.
+         * @return {integer} - Returns an edit distance to the best match. If there is no
+         *                      match, returns `maxEditDistance + 1`.
          */
-        function findArg(row, elem, typeFilter, maxLevDistance) {
-            let lev = maxLevDistance + 1;
+        function findArg(row, elem, typeFilter, maxEditDistance) {
+            let dist = maxEditDistance + 1;
 
             if (row && row.type && row.type.inputs && row.type.inputs.length > 0) {
                 for (const input of row.type.inputs) {
                     if (!typePassesFilter(typeFilter, input.ty)) {
                         continue;
                     }
-                    lev = Math.min(
-                        lev,
-                        checkType(input, elem, parsedQuery.literalSearch, maxLevDistance)
+                    dist = Math.min(
+                        dist,
+                        checkType(input, elem, parsedQuery.literalSearch, maxEditDistance)
                     );
-                    if (lev === 0) {
+                    if (dist === 0) {
                         return 0;
                     }
                 }
             }
-            return parsedQuery.literalSearch ? maxLevDistance + 1 : lev;
+            return parsedQuery.literalSearch ? maxEditDistance + 1 : dist;
         }
 
         /**
@@ -1152,11 +1233,11 @@ function initSearch(rawSearchIndex) {
          * @param {QueryElement} elem   - The element from the parsed query.
          * @param {integer} typeFilter
          *
-         * @return {integer} - Returns a Levenshtein distance to the best match. If there is no
-         *                      match, returns `maxLevDistance + 1`.
+         * @return {integer} - Returns an edit distance to the best match. If there is no
+         *                      match, returns `maxEditDistance + 1`.
          */
-        function checkReturned(row, elem, typeFilter, maxLevDistance) {
-            let lev = maxLevDistance + 1;
+        function checkReturned(row, elem, typeFilter, maxEditDistance) {
+            let dist = maxEditDistance + 1;
 
             if (row && row.type && row.type.output.length > 0) {
                 const ret = row.type.output;
@@ -1164,23 +1245,23 @@ function initSearch(rawSearchIndex) {
                     if (!typePassesFilter(typeFilter, ret_ty.ty)) {
                         continue;
                     }
-                    lev = Math.min(
-                        lev,
-                        checkType(ret_ty, elem, parsedQuery.literalSearch, maxLevDistance)
+                    dist = Math.min(
+                        dist,
+                        checkType(ret_ty, elem, parsedQuery.literalSearch, maxEditDistance)
                     );
-                    if (lev === 0) {
+                    if (dist === 0) {
                         return 0;
                     }
                 }
             }
-            return parsedQuery.literalSearch ? maxLevDistance + 1 : lev;
+            return parsedQuery.literalSearch ? maxEditDistance + 1 : dist;
         }
 
-        function checkPath(contains, ty, maxLevDistance) {
+        function checkPath(contains, ty, maxEditDistance) {
             if (contains.length === 0) {
                 return 0;
             }
-            let ret_lev = maxLevDistance + 1;
+            let ret_dist = maxEditDistance + 1;
             const path = ty.path.split("::");
 
             if (ty.parent && ty.parent.name) {
@@ -1190,27 +1271,27 @@ function initSearch(rawSearchIndex) {
             const length = path.length;
             const clength = contains.length;
             if (clength > length) {
-                return maxLevDistance + 1;
+                return maxEditDistance + 1;
             }
             for (let i = 0; i < length; ++i) {
                 if (i + clength > length) {
                     break;
                 }
-                let lev_total = 0;
+                let dist_total = 0;
                 let aborted = false;
                 for (let x = 0; x < clength; ++x) {
-                    const lev = levenshtein(path[i + x], contains[x]);
-                    if (lev > maxLevDistance) {
+                    const dist = editDistance(path[i + x], contains[x], maxEditDistance);
+                    if (dist > maxEditDistance) {
                         aborted = true;
                         break;
                     }
-                    lev_total += lev;
+                    dist_total += dist;
                 }
                 if (!aborted) {
-                    ret_lev = Math.min(ret_lev, Math.round(lev_total / clength));
+                    ret_dist = Math.min(ret_dist, Math.round(dist_total / clength));
                 }
             }
-            return ret_lev;
+            return ret_dist;
         }
 
         function typePassesFilter(filter, type) {
@@ -1244,6 +1325,7 @@ function initSearch(rawSearchIndex) {
                 parent: item.parent,
                 type: item.type,
                 is_alias: true,
+                deprecated: item.deprecated,
             };
         }
 
@@ -1304,31 +1386,31 @@ function initSearch(rawSearchIndex) {
          * This function adds the given result into the provided `results` map if it matches the
          * following condition:
          *
-         * * If it is a "literal search" (`parsedQuery.literalSearch`), then `lev` must be 0.
-         * * If it is not a "literal search", `lev` must be <= `maxLevDistance`.
+         * * If it is a "literal search" (`parsedQuery.literalSearch`), then `dist` must be 0.
+         * * If it is not a "literal search", `dist` must be <= `maxEditDistance`.
          *
          * The `results` map contains information which will be used to sort the search results:
          *
          * * `fullId` is a `string`` used as the key of the object we use for the `results` map.
          * * `id` is the index in both `searchWords` and `searchIndex` arrays for this element.
          * * `index` is an `integer`` used to sort by the position of the word in the item's name.
-         * * `lev` is the main metric used to sort the search results.
-         * * `path_lev` is zero if a single-component search query is used, otherwise it's the
+         * * `dist` is the main metric used to sort the search results.
+         * * `path_dist` is zero if a single-component search query is used, otherwise it's the
          *   distance computed for everything other than the last path component.
          *
          * @param {Results} results
          * @param {string} fullId
          * @param {integer} id
          * @param {integer} index
-         * @param {integer} lev
-         * @param {integer} path_lev
+         * @param {integer} dist
+         * @param {integer} path_dist
          */
-        function addIntoResults(results, fullId, id, index, lev, path_lev, maxLevDistance) {
-            const inBounds = lev <= maxLevDistance || index !== -1;
-            if (lev === 0 || (!parsedQuery.literalSearch && inBounds)) {
+        function addIntoResults(results, fullId, id, index, dist, path_dist, maxEditDistance) {
+            const inBounds = dist <= maxEditDistance || index !== -1;
+            if (dist === 0 || (!parsedQuery.literalSearch && inBounds)) {
                 if (results[fullId] !== undefined) {
                     const result = results[fullId];
-                    if (result.dontValidate || result.lev <= lev) {
+                    if (result.dontValidate || result.dist <= dist) {
                         return;
                     }
                 }
@@ -1336,8 +1418,8 @@ function initSearch(rawSearchIndex) {
                     id: id,
                     index: index,
                     dontValidate: parsedQuery.literalSearch,
-                    lev: lev,
-                    path_lev: path_lev,
+                    dist: dist,
+                    path_dist: path_dist,
                 };
             }
         }
@@ -1346,7 +1428,7 @@ function initSearch(rawSearchIndex) {
          * This function is called in case the query is only one element (with or without generics).
          * This element will be compared to arguments' and returned values' items and also to items.
          *
-         * Other important thing to note: since there is only one element, we use levenshtein
+         * Other important thing to note: since there is only one element, we use edit
          * distance for name comparisons.
          *
          * @param {Row} row
@@ -1364,22 +1446,22 @@ function initSearch(rawSearchIndex) {
             results_others,
             results_in_args,
             results_returned,
-            maxLevDistance
+            maxEditDistance
         ) {
             if (!row || (filterCrates !== null && row.crate !== filterCrates)) {
                 return;
             }
-            let lev, index = -1, path_lev = 0;
+            let dist, index = -1, path_dist = 0;
             const fullId = row.id;
             const searchWord = searchWords[pos];
 
-            const in_args = findArg(row, elem, parsedQuery.typeFilter, maxLevDistance);
-            const returned = checkReturned(row, elem, parsedQuery.typeFilter, maxLevDistance);
+            const in_args = findArg(row, elem, parsedQuery.typeFilter, maxEditDistance);
+            const returned = checkReturned(row, elem, parsedQuery.typeFilter, maxEditDistance);
 
-            // path_lev is 0 because no parent path information is currently stored
+            // path_dist is 0 because no parent path information is currently stored
             // in the search index
-            addIntoResults(results_in_args, fullId, pos, -1, in_args, 0, maxLevDistance);
-            addIntoResults(results_returned, fullId, pos, -1, returned, 0, maxLevDistance);
+            addIntoResults(results_in_args, fullId, pos, -1, in_args, 0, maxEditDistance);
+            addIntoResults(results_returned, fullId, pos, -1, returned, 0, maxEditDistance);
 
             if (!typePassesFilter(parsedQuery.typeFilter, row.ty)) {
                 return;
@@ -1403,34 +1485,34 @@ function initSearch(rawSearchIndex) {
             // No need to check anything else if it's a "pure" generics search.
             if (elem.name.length === 0) {
                 if (row.type !== null) {
-                    lev = checkGenerics(row.type, elem, maxLevDistance + 1, maxLevDistance);
-                    // path_lev is 0 because we know it's empty
-                    addIntoResults(results_others, fullId, pos, index, lev, 0, maxLevDistance);
+                    dist = checkGenerics(row.type, elem, maxEditDistance + 1, maxEditDistance);
+                    // path_dist is 0 because we know it's empty
+                    addIntoResults(results_others, fullId, pos, index, dist, 0, maxEditDistance);
                 }
                 return;
             }
 
             if (elem.fullPath.length > 1) {
-                path_lev = checkPath(elem.pathWithoutLast, row, maxLevDistance);
-                if (path_lev > maxLevDistance) {
+                path_dist = checkPath(elem.pathWithoutLast, row, maxEditDistance);
+                if (path_dist > maxEditDistance) {
                     return;
                 }
             }
 
             if (parsedQuery.literalSearch) {
                 if (searchWord === elem.name) {
-                    addIntoResults(results_others, fullId, pos, index, 0, path_lev);
+                    addIntoResults(results_others, fullId, pos, index, 0, path_dist);
                 }
                 return;
             }
 
-            lev = levenshtein(searchWord, elem.pathLast);
+            dist = editDistance(searchWord, elem.pathLast, maxEditDistance);
 
-            if (index === -1 && lev + path_lev > maxLevDistance) {
+            if (index === -1 && dist + path_dist > maxEditDistance) {
                 return;
             }
 
-            addIntoResults(results_others, fullId, pos, index, lev, path_lev, maxLevDistance);
+            addIntoResults(results_others, fullId, pos, index, dist, path_dist, maxEditDistance);
         }
 
         /**
@@ -1442,22 +1524,22 @@ function initSearch(rawSearchIndex) {
          * @param {integer} pos      - Position in the `searchIndex`.
          * @param {Object} results
          */
-        function handleArgs(row, pos, results, maxLevDistance) {
+        function handleArgs(row, pos, results, maxEditDistance) {
             if (!row || (filterCrates !== null && row.crate !== filterCrates)) {
                 return;
             }
 
-            let totalLev = 0;
-            let nbLev = 0;
+            let totalDist = 0;
+            let nbDist = 0;
 
             // If the result is too "bad", we return false and it ends this search.
             function checkArgs(elems, callback) {
                 for (const elem of elems) {
                     // There is more than one parameter to the query so all checks should be "exact"
-                    const lev = callback(row, elem, NO_TYPE_FILTER, maxLevDistance);
-                    if (lev <= 1) {
-                        nbLev += 1;
-                        totalLev += lev;
+                    const dist = callback(row, elem, NO_TYPE_FILTER, maxEditDistance);
+                    if (dist <= 1) {
+                        nbDist += 1;
+                        totalDist += dist;
                     } else {
                         return false;
                     }
@@ -1471,11 +1553,11 @@ function initSearch(rawSearchIndex) {
                 return;
             }
 
-            if (nbLev === 0) {
+            if (nbDist === 0) {
                 return;
             }
-            const lev = Math.round(totalLev / nbLev);
-            addIntoResults(results, row.id, pos, 0, lev, 0, maxLevDistance);
+            const dist = Math.round(totalDist / nbDist);
+            addIntoResults(results, row.id, pos, 0, dist, 0, maxEditDistance);
         }
 
         function innerRunQuery() {
@@ -1488,7 +1570,7 @@ function initSearch(rawSearchIndex) {
             for (const elem of parsedQuery.returned) {
                 queryLen += elem.name.length;
             }
-            const maxLevDistance = Math.floor(queryLen / 3);
+            const maxEditDistance = Math.floor(queryLen / 3);
 
             if (parsedQuery.foundElems === 1) {
                 if (parsedQuery.elems.length === 1) {
@@ -1503,7 +1585,7 @@ function initSearch(rawSearchIndex) {
                             results_others,
                             results_in_args,
                             results_returned,
-                            maxLevDistance
+                            maxEditDistance
                         );
                     }
                 } else if (parsedQuery.returned.length === 1) {
@@ -1515,14 +1597,14 @@ function initSearch(rawSearchIndex) {
                             row,
                             elem,
                             parsedQuery.typeFilter,
-                            maxLevDistance
+                            maxEditDistance
                         );
-                        addIntoResults(results_others, row.id, i, -1, in_returned, maxLevDistance);
+                        addIntoResults(results_others, row.id, i, -1, in_returned, maxEditDistance);
                     }
                 }
             } else if (parsedQuery.foundElems > 0) {
                 for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
-                    handleArgs(searchIndex[i], i, results_others, maxLevDistance);
+                    handleArgs(searchIndex[i], i, results_others, maxEditDistance);
                 }
             }
         }
@@ -1560,7 +1642,7 @@ function initSearch(rawSearchIndex) {
      *
      * @return {boolean}       - Whether the result is valid or not
      */
-    function validateResult(name, path, keys, parent, maxLevDistance) {
+    function validateResult(name, path, keys, parent, maxEditDistance) {
         if (!keys || !keys.length) {
             return true;
         }
@@ -1574,8 +1656,8 @@ function initSearch(rawSearchIndex) {
                 // next if there is a parent, check for exact parent match
                 (parent !== undefined && parent.name !== undefined &&
                     parent.name.toLowerCase().indexOf(key) > -1) ||
-                // lastly check to see if the name was a levenshtein match
-                levenshtein(name, key) <= maxLevDistance)) {
+                // lastly check to see if the name was an editDistance match
+                editDistance(name, key, maxEditDistance) <= maxEditDistance)) {
                 return false;
             }
         }
@@ -2064,10 +2146,11 @@ function initSearch(rawSearchIndex) {
              *   n: Array<string>,
              *   t: String,
              *   d: Array<string>,
-             *   q: Array<string>,
+             *   q: Array<[Number, string]>,
              *   i: Array<Number>,
              *   f: Array<RawFunctionSearchType>,
              *   p: Array<Object>,
+             *   c: Array<Number>
              * }}
              */
             const crateCorpus = rawSearchIndex[crate];
@@ -2086,6 +2169,7 @@ function initSearch(rawSearchIndex) {
                 type: null,
                 id: id,
                 normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""),
+                deprecated: null,
             };
             id += 1;
             searchIndex.push(crateRow);
@@ -2095,14 +2179,20 @@ function initSearch(rawSearchIndex) {
             const itemTypes = crateCorpus.t;
             // an array of (String) item names
             const itemNames = crateCorpus.n;
-            // an array of (String) full paths (or empty string for previous path)
-            const itemPaths = crateCorpus.q;
+            // an array of [(Number) item index,
+            //              (String) full path]
+            // an item whose index is not present will fall back to the previous present path
+            // i.e. if indices 4 and 11 are present, but 5-10 and 12-13 are not present,
+            // 5-10 will fall back to the path for 4 and 12-13 will fall back to the path for 11
+            const itemPaths = new Map(crateCorpus.q);
             // an array of (String) descriptions
             const itemDescs = crateCorpus.d;
             // an array of (Number) the parent path index + 1 to `paths`, or 0 if none
             const itemParentIdxs = crateCorpus.i;
             // an array of (Object | null) the type of the function, if any
             const itemFunctionSearchTypes = crateCorpus.f;
+            // an array of (Number) indices for the deprecated items
+            const deprecatedItems = new Set(crateCorpus.c);
             // an array of [(Number) item type,
             //              (String) name]
             const paths = crateCorpus.p;
@@ -2142,12 +2232,13 @@ function initSearch(rawSearchIndex) {
                     crate: crate,
                     ty: itemTypes.charCodeAt(i) - charA,
                     name: itemNames[i],
-                    path: itemPaths[i] ? itemPaths[i] : lastPath,
+                    path: itemPaths.has(i) ? itemPaths.get(i) : lastPath,
                     desc: itemDescs[i],
                     parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
                     type: buildFunctionSearchType(itemFunctionSearchTypes[i], lowercasePaths),
                     id: id,
                     normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""),
+                    deprecated: deprecatedItems.has(i),
                 };
                 id += 1;
                 searchIndex.push(row);
diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js
index c72ac254fc0..c3fed9a72d4 100644
--- a/src/librustdoc/html/static/js/storage.js
+++ b/src/librustdoc/html/static/js/storage.js
@@ -7,7 +7,6 @@
 
 const darkThemes = ["dark", "ayu"];
 window.currentTheme = document.getElementById("themeStyle");
-window.mainTheme = document.getElementById("mainThemeStyle");
 
 // WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY
 // If you update this line, then you also need to update the media query with the same
@@ -44,8 +43,6 @@ function getSettingValue(settingName) {
 
 const localStoredTheme = getSettingValue("theme");
 
-const savedHref = [];
-
 // eslint-disable-next-line no-unused-vars
 function hasClass(elem, className) {
     return elem && elem.classList && elem.classList.contains(className);
@@ -102,6 +99,7 @@ function onEach(arr, func, reversed) {
  * @param {function(?)}                   func       - The callback
  * @param {boolean}                       [reversed] - Whether to iterate in reverse
  */
+// eslint-disable-next-line no-unused-vars
 function onEachLazy(lazyArray, func, reversed) {
     return onEach(
         Array.prototype.slice.call(lazyArray),
@@ -125,30 +123,37 @@ function getCurrentValue(name) {
     }
 }
 
-function switchTheme(styleElem, mainStyleElem, newThemeName, saveTheme) {
+// Get a value from the rustdoc-vars div, which is used to convey data from
+// Rust to the JS. If there is no such element, return null.
+const getVar = (function getVar(name) {
+    const el = document.getElementById("rustdoc-vars");
+    if (el) {
+        return el.attributes["data-" + name].value;
+    } else {
+        return null;
+    }
+});
+
+function switchTheme(newThemeName, saveTheme) {
     // If this new value comes from a system setting or from the previously
     // saved theme, no need to save it.
     if (saveTheme) {
         updateLocalStorage("theme", newThemeName);
     }
 
-    if (savedHref.length === 0) {
-        onEachLazy(document.getElementsByTagName("link"), el => {
-            savedHref.push(el.href);
-        });
+    let newHref;
+
+    if (newThemeName === "light" || newThemeName === "dark" || newThemeName === "ayu") {
+        newHref = getVar("static-root-path") + getVar("theme-" + newThemeName + "-css");
+    } else {
+        newHref = getVar("root-path") + newThemeName + getVar("resource-suffix") + ".css";
     }
-    const newHref = savedHref.find(url => {
-        const m = url.match(/static\.files\/(.*)-[a-f0-9]{16}\.css$/);
-        if (m && m[1] === newThemeName) {
-            return true;
-        }
-        const m2 = url.match(/\/([^/]*)\.css$/);
-        if (m2 && m2[1].startsWith(newThemeName)) {
-            return true;
-        }
-    });
-    if (newHref && newHref !== styleElem.href) {
-        styleElem.href = newHref;
+
+    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;
     }
 }
 
@@ -164,7 +169,7 @@ const updateTheme = (function() {
      */
     function updateTheme() {
         const use = (theme, saveTheme) => {
-            switchTheme(window.currentTheme, window.mainTheme, theme, saveTheme);
+            switchTheme(theme, saveTheme);
         };
 
         // maybe the user has disabled the setting in the meantime!
diff --git a/src/librustdoc/html/templates/item_info.html b/src/librustdoc/html/templates/item_info.html
new file mode 100644
index 00000000000..d2ea9bdae9c
--- /dev/null
+++ b/src/librustdoc/html/templates/item_info.html
@@ -0,0 +1,7 @@
+{% if !items.is_empty() %}
+    <span class="item-info"> {# #}
+        {% for item in items %}
+            {{item|safe}} {# #}
+        {% endfor %}
+    </span>
+{% endif %}
diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html
index e896850fab6..532660e3d33 100644
--- a/src/librustdoc/html/templates/page.html
+++ b/src/librustdoc/html/templates/page.html
@@ -17,12 +17,6 @@
     <link rel="stylesheet" {#+ #}
           href="{{static_root_path|safe}}{{files.rustdoc_css}}" {#+ #}
           id="mainThemeStyle"> {# #}
-    <link rel="stylesheet" id="themeStyle" href="{{static_root_path|safe}}{{files.theme_light_css}}"> {# #}
-    <link rel="stylesheet" disabled href="{{static_root_path|safe}}{{files.theme_dark_css}}"> {# #}
-    <link rel="stylesheet" disabled href="{{static_root_path|safe}}{{files.theme_ayu_css}}"> {# #}
-    {% for theme in themes %}
-        <link rel="stylesheet" disabled href="{{page.root_path|safe}}{{theme}}{{page.resource_suffix}}.css"> {# #}
-    {% endfor %}
     {% if !layout.default_settings.is_empty() %}
     <script id="default-settings" {#+ #}
       {%~ for (k, v) in layout.default_settings ~%}
@@ -30,6 +24,21 @@
       {% endfor %}
     ></script> {# #}
     {% endif %}
+    <div id="rustdoc-vars" {#+ #}
+         data-root-path="{{page.root_path|safe}}" {#+ #}
+         data-static-root-path="{{static_root_path|safe}}" {#+ #}
+         data-current-crate="{{layout.krate}}" {#+ #}
+         data-themes="{{themes|join(",") }}" {#+ #}
+         data-resource-suffix="{{page.resource_suffix}}" {#+ #}
+         data-rustdoc-version="{{rustdoc_version}}" {#+ #}
+         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}}" {#+ #}
+    > {# #}
+    </div> {# #}
     <script src="{{static_root_path|safe}}{{files.storage_js}}"></script> {# #}
     {% if page.css_class.contains("crate") %}
     <script defer src="{{page.root_path|safe}}crates{{page.resource_suffix}}.js"></script> {# #}
@@ -45,6 +54,12 @@
     {% 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() %}
@@ -132,17 +147,5 @@
         {% if page.css_class != "source" %}</div>{% endif %}
     </main> {# #}
     {{ layout.external_html.after_content|safe }}
-    <div id="rustdoc-vars" {#+ #}
-         data-root-path="{{page.root_path|safe}}" {#+ #}
-         data-static-root-path="{{static_root_path|safe}}" {#+ #}
-         data-current-crate="{{layout.krate}}" {#+ #}
-         data-themes="{{themes|join(",") }}" {#+ #}
-         data-resource-suffix="{{page.resource_suffix}}" {#+ #}
-         data-rustdoc-version="{{rustdoc_version}}" {#+ #}
-         data-search-js="{{files.search_js}}" {#+ #}
-         data-settings-js="{{files.settings_js}}" {#+ #}
-         data-settings-css="{{files.settings_css}}" {#+ #}
-    > {# #}
-    </div> {# #}
 </body> {# #}
 </html> {# #}
diff --git a/src/librustdoc/html/templates/short_item_info.html b/src/librustdoc/html/templates/short_item_info.html
new file mode 100644
index 00000000000..e3125af0e47
--- /dev/null
+++ b/src/librustdoc/html/templates/short_item_info.html
@@ -0,0 +1,23 @@
+{% match self %}
+    {% when Self::Deprecation with { message } %}
+        <div class="stab deprecated"> {# #}
+            <span class="emoji">👎</span> {# #}
+            <span>{{message}}</span> {# #}
+        </div> {# #}
+    {% when Self::Unstable with { feature, tracking } %}
+        <div class="stab unstable"> {# #}
+            <span class="emoji">🔬</span> {# #}
+            <span> {# #}
+                This is a nightly-only experimental API. ({# #}
+                <code>{{feature}}</code> {# #}
+                {% match tracking %}
+                    {% when Some with ((url, num)) %}
+                        &nbsp;<a href="{{url}}{{num}}">#{{num}}</a> {# #}
+                    {% when None %}
+                {% endmatch %}
+                ) {# #}
+            </span> {# #}
+        </div> {# #}
+    {% when Self::Portability with { message } %}
+        <div class="stab portability">{{message|safe}}</div> {# #}
+{% endmatch %}
diff --git a/src/librustdoc/html/templates/sidebar.html b/src/librustdoc/html/templates/sidebar.html
new file mode 100644
index 00000000000..01d476ad29f
--- /dev/null
+++ b/src/librustdoc/html/templates/sidebar.html
@@ -0,0 +1,37 @@
+{% if !title.is_empty() %}
+    <h2 class="location"> {# #}
+        <a href="#">{{title_prefix}}{{title}}</a> {# #}
+    </h2>
+{% endif %}
+<div class="sidebar-elems">
+    {% if is_crate %}
+        <ul class="block">
+            {% if !version.is_empty() %}
+                <li class="version">Version {{+ version}}</li>
+            {% endif %}
+            <li><a id="all-types" href="all.html">All Items</a></li> {# #}
+        </ul>
+    {% endif %}
+
+    {% if self.should_render_blocks() %}
+        <section>
+            {% for block in blocks %}
+                {% if block.should_render() %}
+                    {% if !block.heading.name.is_empty() %}
+                        <h3><a href="#{{block.heading.href|safe}}">{{block.heading.name}}</a></h3>
+                    {% endif %}
+                    {% if !block.links.is_empty() %}
+                        <ul class="block">
+                            {% for link in block.links %}
+                                <li><a href="#{{link.href|safe}}">{{link.name}}</a></li>
+                            {% endfor %}
+                        </ul>
+                    {% endif %}
+                {% endif %}
+            {% endfor %}
+        </section>
+    {% endif %}
+    {% if !path.is_empty() %}
+        <h2><a href="index.html">In {{+ path}}</a></h2>
+    {% endif %}
+</div>
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 5bbbff175cf..e09a68069e8 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -15,7 +15,7 @@ use rustc_span::Span;
 
 use std::mem;
 
-use crate::clean::{cfg::Cfg, AttributesExt, NestedAttributesExt};
+use crate::clean::{cfg::Cfg, AttributesExt, NestedAttributesExt, OneLevelVisitor};
 use crate::core;
 
 /// This module is used to store stuff from Rust's AST in a more convenient
@@ -220,9 +220,15 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
         renamed: Option<Symbol>,
         glob: bool,
         please_inline: bool,
+        path: &hir::UsePath<'_>,
     ) -> bool {
         debug!("maybe_inline_local res: {:?}", res);
 
+        if renamed == Some(kw::Underscore) {
+            // We never inline `_` reexports.
+            return false;
+        }
+
         if self.cx.output_format.is_json() {
             return false;
         }
@@ -263,6 +269,22 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             return false;
         }
 
+        if !please_inline &&
+            let mut visitor = OneLevelVisitor::new(self.cx.tcx.hir(), res_did) &&
+            let Some(item) = visitor.find_target(self.cx.tcx, def_id.to_def_id(), path) &&
+            let item_def_id = item.owner_id.def_id &&
+            item_def_id != def_id &&
+            self
+                .cx
+                .cache
+                .effective_visibilities
+                .is_directly_public(self.cx.tcx, item_def_id.to_def_id()) &&
+            !inherits_doc_hidden(self.cx.tcx, item_def_id)
+        {
+            // The imported item is public and not `doc(hidden)` so no need to inline it.
+            return false;
+        }
+
         let ret = match tcx.hir().get_by_def_id(res_did) {
             Node::Item(&hir::Item { kind: hir::ItemKind::Mod(ref m), .. }) if glob => {
                 let prev = mem::replace(&mut self.inlining, true);
@@ -329,8 +351,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                     self.visit_foreign_item_inner(item, None);
                 }
             }
-            // If we're inlining, skip private items or item reexported as "_".
-            _ if self.inlining && (!is_pub || renamed == Some(kw::Underscore)) => {}
+            // If we're inlining, skip private items.
+            _ if self.inlining && !is_pub => {}
             hir::ItemKind::GlobalAsm(..) => {}
             hir::ItemKind::Use(_, hir::UseKind::ListStem) => {}
             hir::ItemKind::Use(path, kind) => {
@@ -361,6 +383,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                             ident,
                             is_glob,
                             please_inline,
+                            path,
                         ) {
                             continue;
                         }
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject 9880b408a3af50c08fab3dbf4aa2a972df71e95
+Subproject 7d3033d2e59383fd76193daf9423c3d141972a7
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 765826ed867..0abe234fc8f 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -6,11 +6,156 @@ document.
 
 ## Unreleased / Beta / In Rust Nightly
 
-[d822110d...master](https://github.com/rust-lang/rust-clippy/compare/d822110d...master)
+[7f27e2e7...master](https://github.com/rust-lang/rust-clippy/compare/7f27e2e7...master)
+
+## Rust 1.68
+
+Current stable, released 2023-03-09
+
+[d822110d...7f27e2e7](https://github.com/rust-lang/rust-clippy/compare/d822110d...7f27e2e7)
+
+### New Lints
+
+* [`permissions_set_readonly_false`]
+  [#10063](https://github.com/rust-lang/rust-clippy/pull/10063)
+* [`almost_complete_range`]
+  [#10043](https://github.com/rust-lang/rust-clippy/pull/10043)
+* [`size_of_ref`]
+  [#10098](https://github.com/rust-lang/rust-clippy/pull/10098)
+* [`semicolon_outside_block`]
+  [#9826](https://github.com/rust-lang/rust-clippy/pull/9826)
+* [`semicolon_inside_block`]
+  [#9826](https://github.com/rust-lang/rust-clippy/pull/9826)
+* [`transmute_null_to_fn`]
+  [#10099](https://github.com/rust-lang/rust-clippy/pull/10099)
+* [`fn_null_check`]
+  [#10099](https://github.com/rust-lang/rust-clippy/pull/10099)
+
+### Moves and Deprecations
+
+* Moved [`manual_clamp`] to `nursery` (Now allow-by-default)
+  [#10101](https://github.com/rust-lang/rust-clippy/pull/10101)
+* Moved [`mutex_atomic`] to `restriction`
+  [#10115](https://github.com/rust-lang/rust-clippy/pull/10115)
+* Renamed `derive_hash_xor_eq` to [`derived_hash_with_manual_eq`]
+  [#10184](https://github.com/rust-lang/rust-clippy/pull/10184)
+
+### Enhancements
+
+* [`collapsible_str_replace`]: Now takes MSRV into consideration. The minimal version is 1.58
+  [#10047](https://github.com/rust-lang/rust-clippy/pull/10047)
+* [`unused_self`]: No longer lints, if the method body contains a `todo!()` call
+  [#10166](https://github.com/rust-lang/rust-clippy/pull/10166)
+* [`derivable_impls`]: Now suggests deriving `Default` for enums with default unit variants
+  [#10161](https://github.com/rust-lang/rust-clippy/pull/10161)
+* [`arithmetic_side_effects`]: Added two new config values
+  `arithmetic-side-effects-allowed-binary` and `arithmetic-side-effects-allowed-unary`
+  to allow operation on user types
+  [#9840](https://github.com/rust-lang/rust-clippy/pull/9840)
+* [`large_const_arrays`], [`large_stack_arrays`]: avoid integer overflow when calculating
+  total array size
+  [#10103](https://github.com/rust-lang/rust-clippy/pull/10103)
+* [`indexing_slicing`]: add new config `suppress-restriction-lint-in-const` to enable
+  restriction lints, even if the suggestion might not be applicable
+  [#9920](https://github.com/rust-lang/rust-clippy/pull/9920)
+* [`needless_borrow`], [`redundant_clone`]: Now track references better and detect more cases
+  [#9701](https://github.com/rust-lang/rust-clippy/pull/9701)
+* [`derived_hash_with_manual_eq`]: Now allows `#[derive(PartialEq)]` with custom `Hash`
+  implementations
+  [#10184](https://github.com/rust-lang/rust-clippy/pull/10184)
+* [`manual_is_ascii_check`]: Now detects ranges with `.contains()` calls
+  [#10053](https://github.com/rust-lang/rust-clippy/pull/10053)
+* [`transmuting_null`]: Now detects `const` pointers to all types
+  [#10099](https://github.com/rust-lang/rust-clippy/pull/10099)
+* [`needless_return`]: Now detects more cases for returns of owned values
+  [#10110](https://github.com/rust-lang/rust-clippy/pull/10110)
+
+### False Positive Fixes
+
+* [`field_reassign_with_default`]: No longer lints cases, where values are initializes from
+  closures capturing struct values
+  [#10143](https://github.com/rust-lang/rust-clippy/pull/10143)
+* [`seek_to_start_instead_of_rewind`]: No longer lints, if the return of `seek` is used.
+  [#10096](https://github.com/rust-lang/rust-clippy/pull/10096)
+* [`manual_filter`]: Now ignores if expressions where the else branch has side effects or
+  doesn't return `None`
+  [#10091](https://github.com/rust-lang/rust-clippy/pull/10091)
+* [`implicit_clone`]: No longer lints if the type doesn't implement clone
+  [#10022](https://github.com/rust-lang/rust-clippy/pull/10022)
+* [`match_wildcard_for_single_variants`]: No longer lints on wildcards with a guard
+  [#10056](https://github.com/rust-lang/rust-clippy/pull/10056)
+* [`drop_ref`]: No longer lints idiomatic expression in `match` arms
+  [#10142](https://github.com/rust-lang/rust-clippy/pull/10142)
+* [`arithmetic_side_effects`]: No longer lints on corner cases with negative number literals
+  [#9867](https://github.com/rust-lang/rust-clippy/pull/9867)
+* [`string_lit_as_bytes`]: No longer lints in scrutinies of `match` statements
+  [#10012](https://github.com/rust-lang/rust-clippy/pull/10012)
+* [`manual_assert`]: No longer lints in `else if` statements
+  [#10013](https://github.com/rust-lang/rust-clippy/pull/10013)
+* [`needless_return`]: don't lint when using `do yeet`
+  [#10109](https://github.com/rust-lang/rust-clippy/pull/10109)
+* All lints: No longer lint in enum discriminant values when the suggestion won't work in a
+  const context
+  [#10008](https://github.com/rust-lang/rust-clippy/pull/10008)
+* [`single_element_loop`]: No longer lints, if the loop contains a `break` or `continue`
+  [#10162](https://github.com/rust-lang/rust-clippy/pull/10162)
+* [`uninlined_format_args`]: No longer suggests inlining arguments in `assert!` and
+  `debug_assert!` macros before 2021 edition
+  [#10055](https://github.com/rust-lang/rust-clippy/pull/10055)
+* [`explicit_counter_loop`]: No longer ignores counter changes after `continue` expressions
+  [#10094](https://github.com/rust-lang/rust-clippy/pull/10094)
+* [`from_over_into`]: No longer lints on opaque types
+  [#9982](https://github.com/rust-lang/rust-clippy/pull/9982)
+* [`expl_impl_clone_on_copy`]: No longer lints on `#[repr(packed)]` structs with generic
+  parameters
+  [#10189](https://github.com/rust-lang/rust-clippy/pull/10189)
+
+### Suggestion Fixes/Improvements
+
+* [`zero_ptr`]: Now suggests `core::` paths for `no_std` crates
+  [#10023](https://github.com/rust-lang/rust-clippy/pull/10023)
+* [`useless_conversion`]: Now suggests removing calls to `into_iter()` on an expression
+  implementing `Iterator`
+  [#10020](https://github.com/rust-lang/rust-clippy/pull/10020)
+* [`box_default`]: The suggestion now uses short paths
+  [#10153](https://github.com/rust-lang/rust-clippy/pull/10153)
+* [`default_trait_access`], [`clone_on_copy`]: The suggestion now uses short paths
+  [#10160](https://github.com/rust-lang/rust-clippy/pull/10160)
+* [`comparison_to_empty`]: The suggestion now removes unused deref operations
+  [#9962](https://github.com/rust-lang/rust-clippy/pull/9962)
+* [`manual_let_else`]: Suggestions for or-patterns now include required brackets.
+  [#9966](https://github.com/rust-lang/rust-clippy/pull/9966)
+* [`match_single_binding`]: suggestion no longer introduces unneeded semicolons
+  [#10060](https://github.com/rust-lang/rust-clippy/pull/10060)
+* [`case_sensitive_file_extension_comparisons`]: Now displays a suggestion with `Path`
+  [#10107](https://github.com/rust-lang/rust-clippy/pull/10107)
+* [`empty_structs_with_brackets`]: The suggestion is no longer machine applicable, to avoid
+  errors when accessing struct fields
+  [#10141](https://github.com/rust-lang/rust-clippy/pull/10141)
+* [`identity_op`]: Removes borrows in the suggestion when needed
+  [#10004](https://github.com/rust-lang/rust-clippy/pull/10004)
+* [`suboptimal_flops`]: The suggestion now includes parentheses when required
+  [#10113](https://github.com/rust-lang/rust-clippy/pull/10113)
+* [`iter_kv_map`]: Now handles `mut` and reference annotations in the suggestion
+  [#10159](https://github.com/rust-lang/rust-clippy/pull/10159)
+* [`redundant_static_lifetimes`]: The suggestion no longer removes `mut` from references
+  [#10006](https://github.com/rust-lang/rust-clippy/pull/10006)
+
+### ICE Fixes
+
+* [`new_ret_no_self`]: Now avoids a stack overflow for `impl Trait` types
+  [#10086](https://github.com/rust-lang/rust-clippy/pull/10086)
+* [`unnecessary_to_owned`]: Now handles compiler generated notes better
+  [#10027](https://github.com/rust-lang/rust-clippy/pull/10027)
+
+### Others
+
+* `SYSROOT` and `--sysroot` can now be set at the same time
+  [#10149](https://github.com/rust-lang/rust-clippy/pull/10149)
 
 ## Rust 1.67
 
-Current stable, released 2023-01-26
+Released 2023-01-26
 
 [4f142aa1...d822110d](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...d822110d)
 
@@ -4307,6 +4452,7 @@ Released 2018-09-13
 [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
 [`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
 [`collapsible_str_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace
+[`collection_is_never_read`]: https://rust-lang.github.io/rust-clippy/master/index.html#collection_is_never_read
 [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
 [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
 [`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
@@ -4497,6 +4643,7 @@ Released 2018-09-13
 [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use
 [`let_underscore_untyped`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped
 [`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
+[`let_with_type_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_with_type_underscore
 [`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
 [`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
 [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
@@ -4560,6 +4707,7 @@ Released 2018-09-13
 [`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order
 [`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_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
@@ -4689,6 +4837,7 @@ Released 2018-09-13
 [`read_zero_byte_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#read_zero_byte_vec
 [`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl
 [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
+[`redundant_async_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_async_block
 [`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
 [`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
 [`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
diff --git a/src/tools/clippy/COPYRIGHT b/src/tools/clippy/COPYRIGHT
index a6be75b5e31..82703b18fd7 100644
--- a/src/tools/clippy/COPYRIGHT
+++ b/src/tools/clippy/COPYRIGHT
@@ -1,3 +1,5 @@
+// REUSE-IgnoreStart
+
 Copyright 2014-2022 The Rust Project Developers
 
 Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
@@ -5,3 +7,5 @@ http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 option. All files in the project carrying such notice may not be
 copied, modified, or distributed except according to those terms.
+
+// REUSE-IgnoreEnd
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index 70d1268090f..c35dfcbd8c4 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy"
-version = "0.1.69"
+version = "0.1.70"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md
index 3e7379ace7e..b69ed8900a4 100644
--- a/src/tools/clippy/README.md
+++ b/src/tools/clippy/README.md
@@ -275,6 +275,8 @@ If you want to contribute to Clippy, you can find more information in [CONTRIBUT
 
 ## License
 
+<!-- REUSE-IgnoreStart -->
+
 Copyright 2014-2022 The Rust Project Developers
 
 Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
@@ -282,3 +284,5 @@ Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 <LICENSE-MIT or [https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT)>, at your
 option. Files in the project may not be
 copied, modified, or distributed except according to those terms.
+
+<!-- REUSE-IgnoreEnd -->
diff --git a/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md b/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md
index c5587c4bf90..ea4978011b1 100644
--- a/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md
+++ b/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md
@@ -68,13 +68,13 @@ The second part of the motivation is clippy's dependence on unstable
 compiler-internal data structures. Clippy lints are currently written against
 the compiler's AST / HIR which means that even small changes in these data
 structures might break a lot of lints. The second goal of this RFC is to **make
-lints independant of the compiler's AST / HIR data structures**.
+lints independent of the compiler's AST / HIR data structures**.
 
 # Approach
 
 A lot of complexity in writing lints currently seems to come from having to
 manually implement the matching logic (see code samples above). It's an
-imparative style that describes *how* to match a syntax tree node instead of
+imperative style that describes *how* to match a syntax tree node instead of
 specifying *what* should be matched against declaratively. In other areas, it's
 common to use declarative patterns to describe desired information and let the
 implementation do the actual matching. A well-known example of this approach are
@@ -270,7 +270,7 @@ pattern!{
     // matches if expressions that **may or may not** have an else block
     // Attn: `If(_, _, _)` matches only ifs that **have** an else block
     //
-    //              | if with else block | if witout else block
+    //              | if with else block | if without else block
     // If(_, _, _)  |       match        |       no match
     // If(_, _, _?) |       match        |        match
     // If(_, _, ()) |      no match      |        match
@@ -568,7 +568,7 @@ another example, `Array( Lit(_)* )` is a valid pattern because the parameter of
 
 ## The IsMatch Trait
 
-The pattern syntax and the *PatternTree* are independant of specific syntax tree
+The pattern syntax and the *PatternTree* are independent of specific syntax tree
 implementations (rust ast / hir, syn, ...). When looking at the different
 pattern examples in the previous sections, it can be seen that the patterns
 don't contain any information specific to a certain syntax tree implementation.
@@ -717,7 +717,7 @@ if false {
 #### Problems
 
 Extending Rust syntax (which is quite complex by itself) with additional syntax
-needed for specifying patterns (alternations, sequences, repetisions, named
+needed for specifying patterns (alternations, sequences, repetitions, named
 submatches, ...) might become difficult to read and really hard to parse
 properly.
 
@@ -858,7 +858,7 @@ would be evaluated as soon as the `Block(_)#then` was matched.
 Another idea in this area would be to introduce a syntax for backreferences.
 They could be used to require that multiple parts of a pattern should match the
 same value. For example, the `assign_op_pattern` lint that searches for `a = a
-op b` and recommends changing it to `a op= b` requires that both occurrances of
+op b` and recommends changing it to `a op= b` requires that both occurrences of
 `a` are the same. Using `=#...` as syntax for backreferences, the lint could be
 implemented like this:
 
@@ -882,7 +882,7 @@ least two return statements" could be a practical addition.
 For patterns like "a literal that is not a boolean literal" one currently needs
 to list all alternatives except the boolean case. Introducing a negation
 operator that allows to write `Lit(!Bool(_))` might be a good idea. This pattern
-would be eqivalent to `Lit( Char(_) | Int(_) )` (given that currently only three
+would be equivalent to `Lit( Char(_) | Int(_) )` (given that currently only three
 literal types are implemented).
 
 #### Functional composition
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
index 796f1ff1695..0b3846c1316 100644
--- a/src/tools/clippy/clippy_lints/Cargo.toml
+++ b/src/tools/clippy/clippy_lints/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_lints"
-version = "0.1.69"
+version = "0.1.70"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
diff --git a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
index 42e14b5cd94..32d80f42e7e 100644
--- a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
+++ b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
@@ -24,7 +24,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let _ = 'a'..='z';
     /// ```
-    #[clippy::version = "1.63.0"]
+    #[clippy::version = "1.68.0"]
     pub ALMOST_COMPLETE_RANGE,
     suspicious,
     "almost complete range"
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
index 627b795d6ed..1233c632a79 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::{match_def_path, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -34,6 +34,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
         if let ExprKind::Path(ref qpath) = fun.kind;
         if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
         if let Some(rpk) = raw_parts_kind(cx, fun_def_id);
+        let ctxt = expr.span.ctxt();
+        if cast_expr.span.ctxt() == ctxt;
         then {
             let func = match rpk {
                 RawPartsKind::Immutable => "from_raw_parts",
@@ -41,8 +43,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
             };
             let span = expr.span;
             let mut applicability = Applicability::MachineApplicable;
-            let ptr = snippet_with_applicability(cx, ptr_arg.span, "ptr", &mut applicability);
-            let len = snippet_with_applicability(cx, len_arg.span, "len", &mut applicability);
+            let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0;
+            let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0;
             span_lint_and_sugg(
                 cx,
                 CAST_SLICE_FROM_RAW_PARTS,
diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
new file mode 100644
index 00000000000..10f2bef268a
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
@@ -0,0 +1,122 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::visitors::for_each_expr_with_closures;
+use clippy_utils::{get_enclosing_block, get_parent_node, path_to_local_id};
+use core::ops::ControlFlow;
+use rustc_hir::{Block, ExprKind, HirId, Local, Node, PatKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::sym;
+use rustc_span::Symbol;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for collections that are never queried.
+    ///
+    /// ### Why is this bad?
+    /// Putting effort into constructing a collection but then never querying it might indicate that
+    /// the author forgot to do whatever they intended to do with the collection. Example: Clone
+    /// a vector, sort it for iteration, but then mistakenly iterate the original vector
+    /// instead.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let samples = vec![3, 1, 2];
+    /// let mut sorted_samples = samples.clone();
+    /// sorted_samples.sort();
+    /// for sample in &samples { // Oops, meant to use `sorted_samples`.
+    ///     println!("{sample}");
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # let samples = vec![3, 1, 2];
+    /// let mut sorted_samples = samples.clone();
+    /// sorted_samples.sort();
+    /// for sample in &sorted_samples {
+    ///     println!("{sample}");
+    /// }
+    /// ```
+    #[clippy::version = "1.69.0"]
+    pub COLLECTION_IS_NEVER_READ,
+    nursery,
+    "a collection is never queried"
+}
+declare_lint_pass!(CollectionIsNeverRead => [COLLECTION_IS_NEVER_READ]);
+
+static COLLECTIONS: [Symbol; 10] = [
+    sym::BTreeMap,
+    sym::BTreeSet,
+    sym::BinaryHeap,
+    sym::HashMap,
+    sym::HashSet,
+    sym::LinkedList,
+    sym::Option,
+    sym::String,
+    sym::Vec,
+    sym::VecDeque,
+];
+
+impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
+    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
+        // Look for local variables whose type is a container. Search surrounding bock for read access.
+        let ty = cx.typeck_results().pat_ty(local.pat);
+        if COLLECTIONS.iter().any(|&sym| is_type_diagnostic_item(cx, ty, sym))
+            && let PatKind::Binding(_, local_id, _, _) = local.pat.kind
+            && let Some(enclosing_block) = get_enclosing_block(cx, local.hir_id)
+            && has_no_read_access(cx, local_id, enclosing_block)
+        {
+            span_lint(cx, COLLECTION_IS_NEVER_READ, local.span, "collection is never read");
+        }
+    }
+}
+
+fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Block<'tcx>) -> bool {
+    let mut has_access = false;
+    let mut has_read_access = false;
+
+    // Inspect all expressions and sub-expressions in the block.
+    for_each_expr_with_closures(cx, block, |expr| {
+        // Ignore expressions that are not simply `id`.
+        if !path_to_local_id(expr, id) {
+            return ControlFlow::Continue(());
+        }
+
+        // `id` is being accessed. Investigate if it's a read access.
+        has_access = true;
+
+        // `id` appearing in the left-hand side of an assignment is not a read access:
+        //
+        // id = ...; // Not reading `id`.
+        if let Some(Node::Expr(parent)) = get_parent_node(cx.tcx, expr.hir_id)
+            && let ExprKind::Assign(lhs, ..) = parent.kind
+            && path_to_local_id(lhs, id)
+        {
+            return ControlFlow::Continue(());
+        }
+
+        // Method call on `id` in a statement ignores any return value, so it's not a read access:
+        //
+        // id.foo(...); // Not reading `id`.
+        //
+        // Only assuming this for "official" methods defined on the type. For methods defined in extension
+        // traits (identified as local, based on the orphan rule), pessimistically assume that they might
+        // have side effects, so consider them a read.
+        if let Some(Node::Expr(parent)) = get_parent_node(cx.tcx, expr.hir_id)
+            && let ExprKind::MethodCall(_, receiver, _, _) = parent.kind
+            && path_to_local_id(receiver, id)
+            && let Some(Node::Stmt(..)) = get_parent_node(cx.tcx, parent.hir_id)
+            && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id)
+            && !method_def_id.is_local()
+        {
+            return ControlFlow::Continue(());
+        }
+
+        // Any other access to `id` is a read access. Stop searching.
+        has_read_access = true;
+        ControlFlow::Break(())
+    });
+
+    // Ignore collections that have no access at all. Other lints should catch them.
+    has_access && !has_read_access
+}
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index cd5dd7a5706..cc6024b87cd 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -92,6 +92,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO,
     crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO,
     crate::collapsible_if::COLLAPSIBLE_IF_INFO,
+    crate::collection_is_never_read::COLLECTION_IS_NEVER_READ_INFO,
     crate::comparison_chain::COMPARISON_CHAIN_INFO,
     crate::copies::BRANCHES_SHARING_CODE_INFO,
     crate::copies::IFS_SAME_COND_INFO,
@@ -226,6 +227,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::let_underscore::LET_UNDERSCORE_LOCK_INFO,
     crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO,
     crate::let_underscore::LET_UNDERSCORE_UNTYPED_INFO,
+    crate::let_with_type_underscore::LET_WITH_TYPE_UNDERSCORE_INFO,
     crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO,
     crate::lifetimes::NEEDLESS_LIFETIMES_INFO,
     crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO,
@@ -416,6 +418,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::misc_early::UNSEPARATED_LITERAL_SUFFIX_INFO,
     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_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,
@@ -517,6 +520,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::ranges::REVERSED_EMPTY_RANGES_INFO,
     crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO,
     crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO,
+    crate::redundant_async_block::REDUNDANT_ASYNC_BLOCK_INFO,
     crate::redundant_clone::REDUNDANT_CLONE_INFO,
     crate::redundant_closure_call::REDUNDANT_CLOSURE_CALL_INFO,
     crate::redundant_else::REDUNDANT_ELSE_INFO,
diff --git a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
index 1ad929864b2..f296b80d283 100644
--- a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
+++ b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
@@ -1,11 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::last_path_segment;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::{match_def_path, paths};
 use rustc_errors::Applicability;
 use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::SyntaxContext;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -38,9 +39,11 @@ impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty {
             && let QPath::Resolved(None, path) = ty_path
             && let def::Res::Def(_, def_id) = &path.res
             && match_def_path(cx, *def_id, &paths::ITER_EMPTY)
+            && let ctxt = expr.span.ctxt()
+            && ty.span.ctxt() == ctxt
         {
             let mut applicability = Applicability::MachineApplicable;
-            let sugg = make_sugg(cx, ty_path, &mut applicability);
+            let sugg = make_sugg(cx, ty_path, ctxt, &mut applicability);
             span_lint_and_sugg(
                 cx,
                 DEFAULT_INSTEAD_OF_ITER_EMPTY,
@@ -54,14 +57,19 @@ impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty {
     }
 }
 
-fn make_sugg(cx: &LateContext<'_>, ty_path: &rustc_hir::QPath<'_>, applicability: &mut Applicability) -> String {
+fn make_sugg(
+    cx: &LateContext<'_>,
+    ty_path: &rustc_hir::QPath<'_>,
+    ctxt: SyntaxContext,
+    applicability: &mut Applicability,
+) -> String {
     if let Some(last) = last_path_segment(ty_path).args
         && let Some(iter_ty) = last.args.iter().find_map(|arg| match arg {
             GenericArg::Type(ty) => Some(ty),
             _ => None,
         })
     {
-        format!("std::iter::empty::<{}>()", snippet_with_applicability(cx, iter_ty.span, "..", applicability))
+        format!("std::iter::empty::<{}>()", snippet_with_context(cx, iter_ty.span, ctxt, "..", applicability).0)
     } else {
         "std::iter::empty()".to_owned()
     }
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index 47501980e66..7f3f26bed7c 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -1357,10 +1357,10 @@ fn replace_types<'tcx>(
                     && let Some(term_ty) = projection_predicate.term.ty()
                     && let ty::Param(term_param_ty) = term_ty.kind()
                 {
-                    let item_def_id = projection_predicate.projection_ty.def_id;
-                    let assoc_item = cx.tcx.associated_item(item_def_id);
-                    let projection = cx.tcx
-                        .mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, []));
+                    let projection = cx.tcx.mk_ty_from_kind(ty::Alias(
+                        ty::Projection,
+                        projection_predicate.projection_ty.with_self_ty(cx.tcx, new_ty),
+                    ));
 
                     if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
                         && substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
index c5f4e943f4f..8a5a28c6b3d 100644
--- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
@@ -8,7 +8,7 @@ use rustc_hir::{
     Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, Ty, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::AdtDef;
+use rustc_middle::ty::{Adt, AdtDef, SubstsRef};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::sym;
 
@@ -81,13 +81,18 @@ fn check_struct<'tcx>(
     self_ty: &Ty<'_>,
     func_expr: &Expr<'_>,
     adt_def: AdtDef<'_>,
+    substs: SubstsRef<'_>,
 ) {
     if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
-        if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() {
-            for arg in a.args {
-                if !matches!(arg, GenericArg::Lifetime(_)) {
-                    return;
-                }
+        if let Some(PathSegment { args, .. }) = p.segments.last() {
+            let args = args.map(|a| a.args).unwrap_or(&[]);
+
+            // substs contains the generic parameters of the type declaration, while args contains the arguments
+            // used at instantiation time. If both len are not equal, it means that some parameters were not
+            // provided (which means that the default values were used); in this case we will not risk
+            // suggesting too broad a rewrite. We won't either if any argument is a type or a const.
+            if substs.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) {
+                return;
             }
         }
     }
@@ -184,7 +189,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
             if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir);
             if let ImplItemKind::Fn(_, b) = &impl_item.kind;
             if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
-            if let Some(adt_def) = cx.tcx.type_of(item.owner_id).subst_identity().ty_adt_def();
+            if let &Adt(adt_def, substs) = cx.tcx.type_of(item.owner_id).subst_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);
@@ -192,7 +197,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
 
             then {
                 if adt_def.is_struct() {
-                    check_struct(cx, item, self_ty, func_expr, adt_def);
+                    check_struct(cx, item, self_ty, func_expr, adt_def, substs);
                 } else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) {
                     check_enum(cx, item, func_expr, adt_def);
                 }
diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs
index 9c8b0d076df..8ba6a9e4876 100644
--- a/src/tools/clippy/clippy_lints/src/exit.rs
+++ b/src/tools/clippy/clippy_lints/src/exit.rs
@@ -11,7 +11,7 @@ declare_clippy_lint! {
     ///
     /// ### Why is this bad?
     /// Exit terminates the program at the location it is called. For unrecoverable
-    /// errors `panics` should be used to provide a stacktrace and potentualy other
+    /// errors `panics` should be used to provide a stacktrace and potentially other
     /// information. A normal termination or one with an error code should happen in
     /// the main function.
     ///
diff --git a/src/tools/clippy/clippy_lints/src/fn_null_check.rs b/src/tools/clippy/clippy_lints/src/fn_null_check.rs
index 91c8c340ce2..d8f4a5fe221 100644
--- a/src/tools/clippy/clippy_lints/src/fn_null_check.rs
+++ b/src/tools/clippy/clippy_lints/src/fn_null_check.rs
@@ -25,7 +25,7 @@ declare_clippy_lint! {
     ///
     /// if fn_ptr.is_none() { ... }
     /// ```
-    #[clippy::version = "1.67.0"]
+    #[clippy::version = "1.68.0"]
     pub FN_NULL_CHECK,
     correctness,
     "`fn()` type assumed to be nullable"
diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs
index d0fab694960..8040938c626 100644
--- a/src/tools/clippy/clippy_lints/src/format.rs
+++ b/src/tools/clippy/clippy_lints/src/format.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::sugg::Sugg;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -84,9 +84,9 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
                         _ => false,
                     };
                     let sugg = if is_new_string {
-                        snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned()
+                        snippet_with_context(cx, value.span, call_site.ctxt(), "..", &mut applicability).0.into_owned()
                     } else {
-                        let sugg = Sugg::hir_with_applicability(cx, value, "<arg>", &mut applicability);
+                        let sugg = Sugg::hir_with_context(cx, value, call_site.ctxt(), "<arg>", &mut applicability);
                         format!("{}.to_string()", sugg.maybe_par())
                     };
                     span_useless_format(cx, call_site, sugg, applicability);
diff --git a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
index 2811a73f6c1..d3d0d91c1be 100644
--- a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
@@ -22,7 +22,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body:
                             if let Some(gen_span) = generics.span_for_param_suggestion() {
                                 diag.span_suggestion_with_style(
                                     gen_span,
-                                    "add a type paremeter",
+                                    "add a type parameter",
                                     format!(", {{ /* Generic name */ }}: {}", &param.name.ident().as_str()[5..]),
                                     rustc_errors::Applicability::HasPlaceholders,
                                     rustc_errors::SuggestionStyle::ShowAlways,
@@ -35,7 +35,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body:
                                         ident.span.ctxt(),
                                         ident.span.parent(),
                                     ),
-                                    "add a type paremeter",
+                                    "add a type parameter",
                                     format!("<{{ /* Generic name */ }}: {}>", &param.name.ident().as_str()[5..]),
                                     rustc_errors::Applicability::HasPlaceholders,
                                     rustc_errors::SuggestionStyle::ShowAlways,
diff --git a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
index 8b53ee68ebd..e5945939e60 100644
--- a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
@@ -97,7 +97,7 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body:
 
     let Some(correct_field) = correct_field else {
         // There is no field corresponding to the getter name.
-        // FIXME: This can be a false positive if the correct field is reachable trought deeper autodereferences than used_field is
+        // FIXME: This can be a false positive if the correct field is reachable through deeper autodereferences than used_field is
         return;
     };
 
diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs
index d2852b4acad..7c5e44bb7dc 100644
--- a/src/tools/clippy/clippy_lints/src/functions/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs
@@ -185,7 +185,7 @@ declare_clippy_lint! {
     /// ### Examples
     /// ```rust
     /// // this could be annotated with `#[must_use]`.
-    /// fn id<T>(t: T) -> T { t }
+    /// pub fn id<T>(t: T) -> T { t }
     /// ```
     #[clippy::version = "1.40.0"]
     pub MUST_USE_CANDIDATE,
diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs
index 6e19343931e..57e6caa8711 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs
@@ -1,7 +1,7 @@
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::get_parent_expr;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use if_chain::if_chain;
 use rustc_ast::ast::{LitIntType, LitKind};
 use rustc_errors::Applicability;
@@ -55,6 +55,9 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
             if let ExprKind::AssignOp(op1, target, value) = ex.kind;
             let ty = cx.typeck_results().expr_ty(target);
             if Some(c) == get_int_max(ty);
+            let ctxt = expr.span.ctxt();
+            if ex.span.ctxt() == ctxt;
+            if expr1.span.ctxt() == ctxt;
             if clippy_utils::SpanlessEq::new(cx).eq_expr(l, target);
             if BinOpKind::Add == op1.node;
             if let ExprKind::Lit(ref lit) = value.kind;
@@ -62,8 +65,15 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
             if block.expr.is_none();
             then {
                 let mut app = Applicability::MachineApplicable;
-                let code = snippet_with_applicability(cx, target.span, "_", &mut app);
-                let sugg = if let Some(parent) = get_parent_expr(cx, expr) && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind && else_.hir_id == expr.hir_id {format!("{{{code} = {code}.saturating_add(1); }}")} else {format!("{code} = {code}.saturating_add(1);")};
+                let code = snippet_with_context(cx, target.span, ctxt, "_", &mut app).0;
+                let sugg = if let Some(parent) = get_parent_expr(cx, expr)
+                    && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind
+                    && else_.hir_id == expr.hir_id
+                {
+                    format!("{{{code} = {code}.saturating_add(1); }}")
+                } else {
+                    format!("{code} = {code}.saturating_add(1);")
+                };
                 span_lint_and_sugg(cx, IMPLICIT_SATURATING_ADD, expr.span, "manual saturating add detected", "use instead", sugg, app);
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
index 668110c7cc0..34e9991582c 100644
--- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
+++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{self, span_lint_and_sugg};
 use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::source;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty;
 use rustc_errors::Applicability;
@@ -161,14 +161,9 @@ fn print_unchecked_duration_subtraction_sugg(
 ) {
     let mut applicability = Applicability::MachineApplicable;
 
-    let left_expr =
-        source::snippet_with_applicability(cx, left_expr.span, "std::time::Instant::now()", &mut applicability);
-    let right_expr = source::snippet_with_applicability(
-        cx,
-        right_expr.span,
-        "std::time::Duration::from_secs(1)",
-        &mut applicability,
-    );
+    let ctxt = expr.span.ctxt();
+    let left_expr = snippet_with_context(cx, left_expr.span, ctxt, "<instant>", &mut applicability).0;
+    let right_expr = snippet_with_context(cx, right_expr.span, ctxt, "<duration>", &mut applicability).0;
 
     diagnostics::span_lint_and_sugg(
         cx,
diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs
index e13bc47973b..0805b4b1979 100644
--- a/src/tools/clippy/clippy_lints/src/len_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/len_zero.rs
@@ -1,13 +1,14 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators};
+use clippy_utils::source::snippet_with_context;
+use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators, sugg::Sugg};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefIdSet;
 use rustc_hir::{
-    def_id::DefId, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, ImplItem, ImplItemKind, ImplicitSelfKind, Item,
-    ItemKind, Mutability, Node, TraitItemRef, TyKind, UnOp,
+    def::Res, def_id::DefId, lang_items::LangItem, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg,
+    GenericBound, ImplItem, ImplItemKind, ImplicitSelfKind, Item, ItemKind, Mutability, Node, PathSegment, PrimTy,
+    QPath, TraitItemRef, TyKind, TypeBindingKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, AssocKind, FnSig, Ty};
@@ -16,7 +17,6 @@ use rustc_span::{
     source_map::{Span, Spanned, Symbol},
     symbol::sym,
 };
-use std::borrow::Cow;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -251,33 +251,98 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items
 }
 
 #[derive(Debug, Clone, Copy)]
-enum LenOutput<'tcx> {
+enum LenOutput {
     Integral,
     Option(DefId),
-    Result(DefId, Ty<'tcx>),
+    Result(DefId),
 }
-fn parse_len_output<'tcx>(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option<LenOutput<'tcx>> {
+
+fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
+    if let ty::Alias(_, alias_ty) = ty.kind() &&
+        let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(alias_ty.def_id) &&
+        let Item { kind: ItemKind::OpaqueTy(opaque), .. } = item &&
+        opaque.bounds.len() == 1 &&
+        let GenericBound::LangItemTrait(LangItem::Future, _, _, generic_args) = &opaque.bounds[0] &&
+        generic_args.bindings.len() == 1 &&
+        let TypeBindingKind::Equality {
+            term: rustc_hir::Term::Ty(rustc_hir::Ty {kind: TyKind::Path(QPath::Resolved(_, path)), .. }),
+        } = &generic_args.bindings[0].kind &&
+        path.segments.len() == 1 {
+            return Some(&path.segments[0]);
+        }
+
+    None
+}
+
+fn is_first_generic_integral<'tcx>(segment: &'tcx PathSegment<'tcx>) -> bool {
+    if let Some(generic_args) = segment.args {
+        if generic_args.args.is_empty() {
+            return false;
+        }
+        let arg = &generic_args.args[0];
+        if let GenericArg::Type(rustc_hir::Ty {
+            kind: TyKind::Path(QPath::Resolved(_, path)),
+            ..
+        }) = arg
+        {
+            let segments = &path.segments;
+            let segment = &segments[0];
+            let res = &segment.res;
+            if matches!(res, Res::PrimTy(PrimTy::Uint(_))) || matches!(res, Res::PrimTy(PrimTy::Int(_))) {
+                return true;
+            }
+        }
+    }
+
+    false
+}
+
+fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option<LenOutput> {
+    if let Some(segment) = extract_future_output(cx, sig.output()) {
+        let res = segment.res;
+
+        if matches!(res, Res::PrimTy(PrimTy::Uint(_))) || matches!(res, Res::PrimTy(PrimTy::Int(_))) {
+            return Some(LenOutput::Integral);
+        }
+
+        if let Res::Def(_, def_id) = res {
+            if cx.tcx.is_diagnostic_item(sym::Option, def_id) && is_first_generic_integral(segment) {
+                return Some(LenOutput::Option(def_id));
+            } else if cx.tcx.is_diagnostic_item(sym::Result, def_id) && is_first_generic_integral(segment) {
+                return Some(LenOutput::Result(def_id));
+            }
+        }
+
+        return None;
+    }
+
     match *sig.output().kind() {
         ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral),
         ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) => {
             subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did()))
         },
-        ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => subs
-            .type_at(0)
-            .is_integral()
-            .then(|| LenOutput::Result(adt.did(), subs.type_at(1))),
+        ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => {
+            subs.type_at(0).is_integral().then(|| LenOutput::Result(adt.did()))
+        },
         _ => None,
     }
 }
 
-impl<'tcx> LenOutput<'tcx> {
-    fn matches_is_empty_output(self, ty: Ty<'tcx>) -> bool {
+impl LenOutput {
+    fn matches_is_empty_output<'tcx>(self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+        if let Some(segment) = extract_future_output(cx, ty) {
+            return match (self, segment.res) {
+                (_, Res::PrimTy(PrimTy::Bool)) => true,
+                (Self::Option(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Option, def_id) => true,
+                (Self::Result(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Result, def_id) => true,
+                _ => false,
+            };
+        }
+
         match (self, ty.kind()) {
             (_, &ty::Bool) => true,
             (Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(),
-            (Self::Result(id, err_ty), &ty::Adt(adt, subs)) if id == adt.did() => {
-                subs.type_at(0).is_bool() && subs.type_at(1) == err_ty
-            },
+            (Self::Result(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(),
             _ => false,
         }
     }
@@ -301,9 +366,14 @@ impl<'tcx> LenOutput<'tcx> {
 }
 
 /// Checks if the given signature matches the expectations for `is_empty`
-fn check_is_empty_sig<'tcx>(sig: FnSig<'tcx>, self_kind: ImplicitSelfKind, len_output: LenOutput<'tcx>) -> bool {
+fn check_is_empty_sig<'tcx>(
+    cx: &LateContext<'tcx>,
+    sig: FnSig<'tcx>,
+    self_kind: ImplicitSelfKind,
+    len_output: LenOutput,
+) -> bool {
     match &**sig.inputs_and_output {
-        [arg, res] if len_output.matches_is_empty_output(*res) => {
+        [arg, res] if len_output.matches_is_empty_output(cx, *res) => {
             matches!(
                 (arg.kind(), self_kind),
                 (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::ImmRef)
@@ -315,11 +385,11 @@ fn check_is_empty_sig<'tcx>(sig: FnSig<'tcx>, self_kind: ImplicitSelfKind, len_o
 }
 
 /// Checks if the given type has an `is_empty` method with the appropriate signature.
-fn check_for_is_empty<'tcx>(
-    cx: &LateContext<'tcx>,
+fn check_for_is_empty(
+    cx: &LateContext<'_>,
     span: Span,
     self_kind: ImplicitSelfKind,
-    output: LenOutput<'tcx>,
+    output: LenOutput,
     impl_ty: DefId,
     item_name: Symbol,
     item_kind: &str,
@@ -352,6 +422,7 @@ fn check_for_is_empty<'tcx>(
         Some(is_empty)
             if !(is_empty.fn_has_self_parameter
                 && check_is_empty_sig(
+                    cx,
                     cx.tcx.fn_sig(is_empty.def_id).subst_identity().skip_binder(),
                     self_kind,
                     output,
@@ -431,7 +502,7 @@ fn check_len(
                 &format!("using `{op}is_empty` is clearer and more explicit"),
                 format!(
                     "{op}{}.is_empty()",
-                    snippet_with_applicability(cx, receiver.span, "_", &mut applicability)
+                    snippet_with_context(cx, receiver.span, span.ctxt(), "_", &mut applicability).0,
                 ),
                 applicability,
             );
@@ -444,13 +515,7 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex
         let mut applicability = Applicability::MachineApplicable;
 
         let lit1 = peel_ref_operators(cx, lit1);
-        let mut lit_str = snippet_with_applicability(cx, lit1.span, "_", &mut applicability);
-
-        // Wrap the expression in parentheses if it's a deref expression. Otherwise operator precedence will
-        // cause the code to dereference boolean(won't compile).
-        if let ExprKind::Unary(UnOp::Deref, _) = lit1.kind {
-            lit_str = Cow::from(format!("({lit_str})"));
-        }
+        let lit_str = Sugg::hir_with_context(cx, lit1, span.ctxt(), "_", &mut applicability).maybe_par();
 
         span_lint_and_sugg(
             cx,
diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs
index 7600777fab9..51b5de27de8 100644
--- a/src/tools/clippy/clippy_lints/src/let_underscore.rs
+++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs
@@ -124,7 +124,7 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "1.69.0"]
     pub LET_UNDERSCORE_UNTYPED,
-    pedantic,
+    restriction,
     "non-binding `let` without a type annotation"
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
new file mode 100644
index 00000000000..ba51973f2f9
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
@@ -0,0 +1,45 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use rustc_hir::*;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Detects when a variable is declared with an explicit type of `_`.
+    /// ### Why is this bad?
+    /// It adds noise, `: _` provides zero clarity or utility.
+    /// ### Example
+    /// ```rust,ignore
+    /// let my_number: _ = 1;
+    /// ```
+    /// Use instead:
+    /// ```rust,ignore
+    /// let my_number = 1;
+    /// ```
+    #[clippy::version = "1.69.0"]
+    pub LET_WITH_TYPE_UNDERSCORE,
+    complexity,
+    "unneeded underscore type (`_`) in a variable declaration"
+}
+declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]);
+
+impl LateLintPass<'_> for UnderscoreTyped {
+    fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
+        if_chain! {
+            if !in_external_macro(cx.tcx.sess, local.span);
+            if let Some(ty) = local.ty; // Ensure that it has a type defined
+            if let TyKind::Infer = &ty.kind; // that type is '_'
+            if local.span.ctxt() == ty.span.ctxt();
+            then {
+                span_lint_and_help(cx,
+                    LET_WITH_TYPE_UNDERSCORE,
+                    local.span,
+                    "variable declared with type underscore",
+                    Some(ty.span.with_lo(local.pat.span.hi())),
+                    "remove the explicit type `_` declaration"
+                )
+            }
+        };
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index c626e0bd998..491732be208 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -87,6 +87,7 @@ mod casts;
 mod checked_conversions;
 mod cognitive_complexity;
 mod collapsible_if;
+mod collection_is_never_read;
 mod comparison_chain;
 mod copies;
 mod copy_iterator;
@@ -166,6 +167,7 @@ mod large_stack_arrays;
 mod len_zero;
 mod let_if_seq;
 mod let_underscore;
+mod let_with_type_underscore;
 mod lifetimes;
 mod literal_representation;
 mod loops;
@@ -192,6 +194,7 @@ mod minmax;
 mod misc;
 mod misc_early;
 mod mismatching_type_param_order;
+mod missing_assert_message;
 mod missing_const_for_fn;
 mod missing_doc;
 mod missing_enforced_import_rename;
@@ -249,6 +252,7 @@ mod question_mark_used;
 mod ranges;
 mod rc_clone_in_vec_init;
 mod read_zero_byte_vec;
+mod redundant_async_block;
 mod redundant_clone;
 mod redundant_closure_call;
 mod redundant_else;
@@ -533,6 +537,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
                 .collect(),
         ))
     });
+    store.register_early_pass(|| Box::new(utils::format_args_collector::FormatArgsCollector));
     store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
     store.register_late_pass(|_| Box::new(utils::author::Author));
     let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
@@ -924,6 +929,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         ))
     });
     store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi));
+    store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead));
+    store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
+    store.register_early_pass(|| Box::new(redundant_async_block::RedundantAsyncBlock));
+    store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs
index 462d73cf0b9..bc815dc4a26 100644
--- a/src/tools/clippy/clippy_lints/src/manual_bits.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs
@@ -1,11 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::get_parent_expr;
 use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Ty};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::sym;
@@ -55,13 +56,17 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits {
         if_chain! {
             if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind;
             if let BinOpKind::Mul = &bin_op.node;
+            if !in_external_macro(cx.sess(), expr.span);
+            let ctxt = expr.span.ctxt();
+            if left_expr.span.ctxt() == ctxt;
+            if right_expr.span.ctxt() == ctxt;
             if let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr);
             if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_));
             if let ExprKind::Lit(lit) = &other_expr.kind;
             if let LitKind::Int(8, _) = lit.node;
             then {
                 let mut app = Applicability::MachineApplicable;
-                let ty_snip = snippet_with_applicability(cx, real_ty.span, "..", &mut app);
+                let ty_snip = snippet_with_context(cx, real_ty.span, ctxt, "..", &mut app).0;
                 let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS"));
 
                 span_lint_and_sugg(
diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
index 2fd32c009ea..31264261f5d 100644
--- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
@@ -1,5 +1,5 @@
 use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, source::snippet};
+use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, sugg::Sugg};
 use rustc_ast::ast::RangeLimits;
 use rustc_ast::LitKind::{Byte, Char};
 use rustc_errors::Applicability;
@@ -115,15 +115,8 @@ fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &Cha
         CharRange::Otherwise => None,
     } {
         let default_snip = "..";
-        // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for
-        // macro span, so we check applicability manually by comparing `recv` is not default.
-        let recv = snippet(cx, recv.span, default_snip);
-
-        let applicability = if recv == default_snip {
-            Applicability::HasPlaceholders
-        } else {
-            Applicability::MachineApplicable
-        };
+        let mut app = Applicability::MachineApplicable;
+        let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par();
 
         span_lint_and_sugg(
             cx,
@@ -132,7 +125,7 @@ fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &Cha
             "manual check for common ascii range",
             "try",
             format!("{recv}.{sugg}()"),
-            applicability,
+            app,
         );
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
index 38f41d077c1..aafee92713f 100644
--- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
@@ -1,7 +1,7 @@
 use clippy_utils::consts::{constant_full_int, FullInt};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::{in_constant, path_to_local};
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind};
@@ -60,12 +60,16 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
             return;
         }
 
+        // (x % c + c) % c
         if let ExprKind::Binary(op1, expr1, right) = expr.kind
             && op1.node == BinOpKind::Rem
+            && let ctxt = expr.span.ctxt()
+            && expr1.span.ctxt() == ctxt
             && let Some(const1) = check_for_unsigned_int_constant(cx, right)
             && let ExprKind::Binary(op2, left, right) = expr1.kind
             && op2.node == BinOpKind::Add
             && let Some((const2, expr2)) = check_for_either_unsigned_int_constant(cx, left, right)
+            && expr2.span.ctxt() == ctxt
             && let ExprKind::Binary(op3, expr3, right) = expr2.kind
             && op3.node == BinOpKind::Rem
             && let Some(const3) = check_for_unsigned_int_constant(cx, right)
@@ -86,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
                 };
 
                 let mut app = Applicability::MachineApplicable;
-                let rem_of = snippet_with_applicability(cx, expr3.span, "_", &mut app);
+                let rem_of = snippet_with_context(cx, expr3.span, ctxt, "_", &mut app).0;
                 span_lint_and_sugg(
                     cx,
                     MANUAL_REM_EUCLID,
diff --git a/src/tools/clippy/clippy_lints/src/match_result_ok.rs b/src/tools/clippy/clippy_lints/src/match_result_ok.rs
index a020282d234..6ec9784038c 100644
--- a/src/tools/clippy/clippy_lints/src/match_result_ok.rs
+++ b/src/tools/clippy/clippy_lints/src/match_result_ok.rs
@@ -1,11 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher;
-use clippy_utils::method_chain_args;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::is_res_lang_ctor;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::is_type_diagnostic_item;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, PatKind, QPath};
+use rustc_hir::{Expr, ExprKind, LangItem, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
@@ -58,17 +58,18 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
             };
 
         if_chain! {
-            if let ExprKind::MethodCall(ok_path, result_types_0, ..) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
-            if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _)  = let_pat.kind; //get operation
-            if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() method use std::marker::Sized;
-            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::Result);
-            if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
-
+            if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
+            if let PatKind::TupleStruct(ref pat_path, [ok_pat], _)  = let_pat.kind; //get operation
+            if ok_path.ident.as_str() == "ok";
+            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
+            if is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome);
+            let ctxt = expr.span.ctxt();
+            if let_expr.span.ctxt() == ctxt;
+            if let_pat.span.ctxt() == ctxt;
             then {
-
                 let mut applicability = Applicability::MachineApplicable;
-                let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability);
-                let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_path.ident.span), "", &mut applicability);
+                let some_expr_string = snippet_with_context(cx, ok_pat.span, ctxt, "", &mut applicability).0;
+                let trimmed_ok = snippet_with_context(cx, recv.span, ctxt, "", &mut applicability).0;
                 let sugg = format!(
                     "{ifwhile} let Ok({some_expr_string}) = {}",
                     trimmed_ok.trim().trim_end_matches('.'),
diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs
index 7b15a307fec..97ecca450fa 100644
--- a/src/tools/clippy/clippy_lints/src/matches/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs
@@ -925,7 +925,7 @@ declare_clippy_lint! {
     #[clippy::version = "1.66.0"]
     pub MANUAL_FILTER,
     complexity,
-    "reimplentation of `filter`"
+    "reimplementation of `filter`"
 }
 
 #[derive(Default)]
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 702df4b282b..56e3988bf09 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -340,8 +340,9 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for methods with certain name prefixes and which
-    /// doesn't match how self is taken. The actual rules are:
+    /// Checks for methods with certain name prefixes or suffixes, and which
+    /// do not adhere to standard conventions regarding how `self` is taken.
+    /// The actual rules are:
     ///
     /// |Prefix |Postfix     |`self` taken                   | `self` type  |
     /// |-------|------------|-------------------------------|--------------|
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
index df26b36b7b3..4c4c003ca46 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -369,10 +369,10 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
             Node::Item(item) => {
                 if let ItemKind::Fn(_, _, body_id) = &item.kind
                 && let output_ty = return_ty(cx, item.owner_id)
-                && Inherited::build(cx.tcx, item.owner_id.def_id).enter(|inherited| {
-                    let fn_ctxt = FnCtxt::new(inherited, cx.param_env, item.owner_id.def_id);
-                    fn_ctxt.can_coerce(ty, output_ty)
-                }) {
+                && let inherited = Inherited::new(cx.tcx, item.owner_id.def_id)
+                && let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.owner_id.def_id)
+                && fn_ctxt.can_coerce(ty, output_ty)
+                {
                     if has_lifetime(output_ty) && has_lifetime(ty) {
                         return false;
                     }
diff --git a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
new file mode 100644
index 00000000000..2214a568d9c
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
@@ -0,0 +1,82 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::macros::{find_assert_args, find_assert_eq_args, root_macro_call_first_node, PanicExpn};
+use clippy_utils::{is_in_cfg_test, is_in_test_function};
+use rustc_hir::Expr;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks assertions without a custom panic message.
+    ///
+    /// ### Why is this bad?
+    /// Without a good custom message, it'd be hard to understand what went wrong when the assertion fails.
+    /// A good custom message should be more about why the failure of the assertion is problematic
+    /// and not what is failed because the assertion already conveys that.
+    ///
+    /// ### Known problems
+    /// This lint cannot check the quality of the custom panic messages.
+    /// Hence, you can suppress this lint simply by adding placeholder messages
+    /// like "assertion failed". However, we recommend coming up with good messages
+    /// that provide useful information instead of placeholder messages that
+    /// don't provide any extra information.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # struct Service { ready: bool }
+    /// fn call(service: Service) {
+    ///     assert!(service.ready);
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # struct Service { ready: bool }
+    /// fn call(service: Service) {
+    ///     assert!(service.ready, "`service.poll_ready()` must be called first to ensure that service is ready to receive requests");
+    /// }
+    /// ```
+    #[clippy::version = "1.69.0"]
+    pub MISSING_ASSERT_MESSAGE,
+    restriction,
+    "checks assertions without a custom panic message"
+}
+
+declare_lint_pass!(MissingAssertMessage => [MISSING_ASSERT_MESSAGE]);
+
+impl<'tcx> LateLintPass<'tcx> for MissingAssertMessage {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
+        let single_argument = match cx.tcx.get_diagnostic_name(macro_call.def_id) {
+            Some(sym::assert_macro | sym::debug_assert_macro) => true,
+            Some(
+                sym::assert_eq_macro | sym::assert_ne_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro,
+            ) => false,
+            _ => return,
+        };
+
+        // This lint would be very noisy in tests, so just ignore if we're in test context
+        if is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id) {
+            return;
+        }
+
+        let panic_expn = if single_argument {
+            let Some((_, panic_expn)) = find_assert_args(cx, expr, macro_call.expn) else { return };
+            panic_expn
+        } else {
+            let Some((_, _, panic_expn)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return };
+            panic_expn
+        };
+
+        if let PanicExpn::Empty = panic_expn {
+            span_lint_and_help(
+                cx,
+                MISSING_ASSERT_MESSAGE,
+                macro_call.span,
+                "assert without any message",
+                None,
+                "consider describing why the failing assert is problematic",
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs
index 5b1f03fc16c..f2773cad400 100644
--- a/src/tools/clippy/clippy_lints/src/missing_doc.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs
@@ -8,10 +8,10 @@
 use clippy_utils::attrs::is_doc_hidden;
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::is_from_proc_macro;
-use hir::def_id::LocalDefId;
 use if_chain::if_chain;
 use rustc_ast::ast::{self, MetaItem, MetaItemKind};
 use rustc_hir as hir;
+use rustc_hir::def_id::LocalDefId;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::ty::Visibility;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -21,8 +21,7 @@ use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Warns if there is missing doc for any documentable item
-    /// (public or private).
+    /// Warns if there is missing doc for any private documentable item
     ///
     /// ### Why is this bad?
     /// Doc is good. *rustc* has a `MISSING_DOCS`
@@ -32,7 +31,7 @@ declare_clippy_lint! {
     #[clippy::version = "pre 1.29.0"]
     pub MISSING_DOCS_IN_PRIVATE_ITEMS,
     restriction,
-    "detects missing documentation for public and private members"
+    "detects missing documentation for private members"
 }
 
 pub struct MissingDoc {
@@ -107,11 +106,14 @@ impl MissingDoc {
             if vis == Visibility::Public || vis != Visibility::Restricted(CRATE_DEF_ID.into()) {
                 return;
             }
+        } else if def_id != CRATE_DEF_ID && cx.effective_visibilities.is_exported(def_id) {
+            return;
         }
 
         let has_doc = attrs
             .iter()
             .any(|a| a.doc_str().is_some() || Self::has_include(a.meta()));
+
         if !has_doc {
             span_lint(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
index 63c575fca30..5418616ded0 100644
--- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
+++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
@@ -11,6 +11,7 @@ use rustc_ast::Mutability;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::Span;
 
@@ -120,33 +121,15 @@ fn collect_unsafe_exprs<'tcx>(
                 unsafe_ops.push(("raw pointer dereference occurs here", expr.span));
             },
 
-            ExprKind::Call(path_expr, _) => match path_expr.kind {
-                ExprKind::Path(QPath::Resolved(
-                    _,
-                    hir::Path {
-                        res: Res::Def(kind, def_id),
-                        ..
-                    },
-                )) if kind.is_fn_like() => {
-                    let sig = cx.tcx.fn_sig(*def_id);
-                    if sig.0.unsafety() == Unsafety::Unsafe {
-                        unsafe_ops.push(("unsafe function call occurs here", expr.span));
-                    }
-                },
-
-                ExprKind::Path(QPath::TypeRelative(..)) => {
-                    if let Some(sig) = cx
-                        .typeck_results()
-                        .type_dependent_def_id(path_expr.hir_id)
-                        .map(|def_id| cx.tcx.fn_sig(def_id))
-                    {
-                        if sig.0.unsafety() == Unsafety::Unsafe {
-                            unsafe_ops.push(("unsafe function call occurs here", expr.span));
-                        }
-                    }
-                },
-
-                _ => {},
+            ExprKind::Call(path_expr, _) => {
+                let sig = match *cx.typeck_results().expr_ty(path_expr).kind() {
+                    ty::FnDef(id, _) => cx.tcx.fn_sig(id).skip_binder(),
+                    ty::FnPtr(sig) => sig,
+                    _ => return Continue(Descend::Yes),
+                };
+                if sig.unsafety() == Unsafety::Unsafe {
+                    unsafe_ops.push(("unsafe function call occurs here", expr.span));
+                }
             },
 
             ExprKind::MethodCall(..) => {
diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs
index fb9a4abd0b4..ed3e2c6e7f4 100644
--- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs
+++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs
@@ -1,6 +1,6 @@
 use clippy_utils::consts::{self, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::sugg::has_enclosing_paren;
 use if_chain::if_chain;
 use rustc_ast::util::parser::PREC_PREFIX;
@@ -60,8 +60,8 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
 
         then {
             let mut applicability = Applicability::MachineApplicable;
-            let snip = snippet_with_applicability(cx, exp.span, "..", &mut applicability);
-            let suggestion = if exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) {
+            let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability);
+            let suggestion = if !from_macro && exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) {
                 format!("-({snip})")
             } else {
                 format!("-{snip}")
diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
index 2ecb0487484..e1de494eb41 100644
--- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
+++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
@@ -53,6 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
                             || is_type_diagnostic_item(cx, obj_ty, sym::DirBuilder)))
                         || (path.ident.name == sym!(set_mode) && match_type(cx, obj_ty, &paths::PERMISSIONS));
                     if let ExprKind::Lit(_) = param.kind;
+                    if param.span.ctxt() == expr.span.ctxt();
 
                     then {
                         let Some(snip) = snippet_opt(cx, param.span) else {
@@ -71,6 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
                     if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id();
                     if match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE);
                     if let ExprKind::Lit(_) = param.kind;
+                    if param.span.ctxt() == expr.span.ctxt();
                     if let Some(snip) = snippet_opt(cx, param.span);
                     if !snip.starts_with("0o");
                     then {
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 87a8a2ed12b..25e8de94863 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
@@ -143,6 +143,10 @@ impl ArithmeticSideEffects {
             return;
         }
         let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) {
+            if let hir::BinOpKind::Shl | hir::BinOpKind::Shr = op.node {
+                // At least for integers, shifts are already handled by the CTFE
+                return;
+            }
             let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs);
             let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs);
             match (
@@ -151,10 +155,13 @@ impl ArithmeticSideEffects {
             ) {
                 (None, None) => false,
                 (None, Some(n)) | (Some(n), None) => match (&op.node, n) {
-                    (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false,
+                    // Division and module are always valid if applied to non-zero integers
+                    (hir::BinOpKind::Div | hir::BinOpKind::Rem, local_n) if local_n != 0 => true,
+                    // Addition or subtracting zeros is always a no-op
                     (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
-                    | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _)
-                    | (hir::BinOpKind::Mul, 0 | 1) => true,
+                    // Multiplication by 1 or 0 will never overflow
+                    | (hir::BinOpKind::Mul, 0 | 1)
+                    => true,
                     _ => false,
                 },
                 (Some(_), Some(_)) => {
diff --git a/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs b/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs
index e7095ec191f..664d44d6504 100644
--- a/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs
+++ b/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs
@@ -21,7 +21,7 @@ declare_clippy_lint! {
     /// let mut permissions = metadata.permissions();
     /// permissions.set_readonly(false);
     /// ```
-    #[clippy::version = "1.66.0"]
+    #[clippy::version = "1.68.0"]
     pub PERMISSIONS_SET_READONLY_FALSE,
     suspicious,
     "Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`"
diff --git a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
new file mode 100644
index 00000000000..27ad4308637
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
@@ -0,0 +1,84 @@
+use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet};
+use rustc_ast::ast::*;
+use rustc_ast::visit::Visitor as AstVisitor;
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `async` block that only returns `await` on a future.
+    ///
+    /// ### Why is this bad?
+    /// It is simpler and more efficient to use the future directly.
+    ///
+    /// ### Example
+    /// ```rust
+    /// async fn f() -> i32 {
+    ///     1 + 2
+    /// }
+    ///
+    /// let fut = async {
+    ///     f().await
+    /// };
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// async fn f() -> i32 {
+    ///     1 + 2
+    /// }
+    ///
+    /// let fut = f();
+    /// ```
+    #[clippy::version = "1.69.0"]
+    pub REDUNDANT_ASYNC_BLOCK,
+    complexity,
+    "`async { future.await }` can be replaced by `future`"
+}
+declare_lint_pass!(RedundantAsyncBlock => [REDUNDANT_ASYNC_BLOCK]);
+
+impl EarlyLintPass for RedundantAsyncBlock {
+    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
+        if expr.span.from_expansion() {
+            return;
+        }
+        if let ExprKind::Async(_, _, block) = &expr.kind && block.stmts.len() == 1 &&
+            let Some(Stmt { kind: StmtKind::Expr(last), .. }) = block.stmts.last() &&
+            let ExprKind::Await(future) = &last.kind &&
+            !future.span.from_expansion() &&
+            !await_in_expr(future)
+        {
+            span_lint_and_sugg(
+                cx,
+                REDUNDANT_ASYNC_BLOCK,
+                expr.span,
+                "this async expression only awaits a single future",
+                "you can reduce it to",
+                snippet(cx, future.span, "..").into_owned(),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
+
+/// Check whether an expression contains `.await`
+fn await_in_expr(expr: &Expr) -> bool {
+    let mut detector = AwaitDetector::default();
+    detector.visit_expr(expr);
+    detector.await_found
+}
+
+#[derive(Default)]
+struct AwaitDetector {
+    await_found: bool,
+}
+
+impl<'ast> AstVisitor<'ast> for AwaitDetector {
+    fn visit_expr(&mut self, ex: &'ast Expr) {
+        match (&ex.kind, self.await_found) {
+            (ExprKind::Await(_), _) => self.await_found = true,
+            (_, false) => rustc_ast::visit::walk_expr(self, ex),
+            _ => (),
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/size_of_ref.rs b/src/tools/clippy/clippy_lints/src/size_of_ref.rs
index 3fcdb4288ce..8abec06c641 100644
--- a/src/tools/clippy/clippy_lints/src/size_of_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/size_of_ref.rs
@@ -45,7 +45,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
-    #[clippy::version = "1.67.0"]
+    #[clippy::version = "1.68.0"]
     pub SIZE_OF_REF,
     suspicious,
     "Argument to `std::mem::size_of_val()` is a double-reference, which is almost certainly unintended"
diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs
index 0f062cecf88..1aeac724ab1 100644
--- a/src/tools/clippy/clippy_lints/src/swap.rs
+++ b/src/tools/clippy/clippy_lints/src/swap.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core};
@@ -10,6 +10,7 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Spanned;
+use rustc_span::SyntaxContext;
 use rustc_span::{sym, symbol::Ident, Span};
 
 declare_clippy_lint! {
@@ -80,43 +81,45 @@ impl<'tcx> LateLintPass<'tcx> for Swap {
 }
 
 fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, span: Span, is_xor_based: bool) {
+    let ctxt = span.ctxt();
     let mut applicability = Applicability::MachineApplicable;
 
     if !can_mut_borrow_both(cx, e1, e2) {
-        if let ExprKind::Index(lhs1, idx1) = e1.kind {
-            if let ExprKind::Index(lhs2, idx2) = e2.kind {
-                if eq_expr_value(cx, lhs1, lhs2) {
-                    let ty = cx.typeck_results().expr_ty(lhs1).peel_refs();
+        if let ExprKind::Index(lhs1, idx1) = e1.kind
+            && let ExprKind::Index(lhs2, idx2) = e2.kind
+            && eq_expr_value(cx, lhs1, lhs2)
+            && e1.span.ctxt() == ctxt
+            && e2.span.ctxt() == ctxt
+        {
+            let ty = cx.typeck_results().expr_ty(lhs1).peel_refs();
 
-                    if matches!(ty.kind(), ty::Slice(_))
-                        || matches!(ty.kind(), ty::Array(_, _))
-                        || is_type_diagnostic_item(cx, ty, sym::Vec)
-                        || is_type_diagnostic_item(cx, ty, sym::VecDeque)
-                    {
-                        let slice = Sugg::hir_with_applicability(cx, lhs1, "<slice>", &mut applicability);
-                        span_lint_and_sugg(
-                            cx,
-                            MANUAL_SWAP,
-                            span,
-                            &format!("this looks like you are swapping elements of `{slice}` manually"),
-                            "try",
-                            format!(
-                                "{}.swap({}, {})",
-                                slice.maybe_par(),
-                                snippet_with_applicability(cx, idx1.span, "..", &mut applicability),
-                                snippet_with_applicability(cx, idx2.span, "..", &mut applicability),
-                            ),
-                            applicability,
-                        );
-                    }
-                }
+            if matches!(ty.kind(), ty::Slice(_))
+                || matches!(ty.kind(), ty::Array(_, _))
+                || is_type_diagnostic_item(cx, ty, sym::Vec)
+                || is_type_diagnostic_item(cx, ty, sym::VecDeque)
+            {
+                let slice = Sugg::hir_with_applicability(cx, lhs1, "<slice>", &mut applicability);
+                span_lint_and_sugg(
+                    cx,
+                    MANUAL_SWAP,
+                    span,
+                    &format!("this looks like you are swapping elements of `{slice}` manually"),
+                    "try",
+                    format!(
+                        "{}.swap({}, {});",
+                        slice.maybe_par(),
+                        snippet_with_context(cx, idx1.span, ctxt, "..", &mut applicability).0,
+                        snippet_with_context(cx, idx2.span, ctxt, "..", &mut applicability).0,
+                    ),
+                    applicability,
+                );
             }
         }
         return;
     }
 
-    let first = Sugg::hir_with_applicability(cx, e1, "..", &mut applicability);
-    let second = Sugg::hir_with_applicability(cx, e2, "..", &mut applicability);
+    let first = Sugg::hir_with_context(cx, e1, ctxt, "..", &mut applicability);
+    let second = Sugg::hir_with_context(cx, e2, ctxt, "..", &mut applicability);
     let Some(sugg) = std_or_core(cx) else { return };
 
     span_lint_and_then(
@@ -128,7 +131,7 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa
             diag.span_suggestion(
                 span,
                 "try",
-                format!("{sugg}::mem::swap({}, {})", first.mut_addr(), second.mut_addr()),
+                format!("{sugg}::mem::swap({}, {});", first.mut_addr(), second.mut_addr()),
                 applicability,
             );
             if !is_xor_based {
@@ -144,19 +147,19 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
         return;
     }
 
-    for w in block.stmts.windows(3) {
+    for [s1, s2, s3] in block.stmts.array_windows::<3>() {
         if_chain! {
             // let t = foo();
-            if let StmtKind::Local(tmp) = w[0].kind;
+            if let StmtKind::Local(tmp) = s1.kind;
             if let Some(tmp_init) = tmp.init;
             if let PatKind::Binding(.., ident, None) = tmp.pat.kind;
 
             // foo() = bar();
-            if let StmtKind::Semi(first) = w[1].kind;
+            if let StmtKind::Semi(first) = s2.kind;
             if let ExprKind::Assign(lhs1, rhs1, _) = first.kind;
 
             // bar() = t;
-            if let StmtKind::Semi(second) = w[2].kind;
+            if let StmtKind::Semi(second) = s3.kind;
             if let ExprKind::Assign(lhs2, rhs2, _) = second.kind;
             if let ExprKind::Path(QPath::Resolved(None, rhs2)) = rhs2.kind;
             if rhs2.segments.len() == 1;
@@ -164,8 +167,15 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
             if ident.name == rhs2.segments[0].ident.name;
             if eq_expr_value(cx, tmp_init, lhs1);
             if eq_expr_value(cx, rhs1, lhs2);
+
+            let ctxt = s1.span.ctxt();
+            if s2.span.ctxt() == ctxt;
+            if s3.span.ctxt() == ctxt;
+            if first.span.ctxt() == ctxt;
+            if second.span.ctxt() == ctxt;
+
             then {
-                let span = w[0].span.to(second.span);
+                let span = s1.span.to(s3.span);
                 generate_swap_warning(cx, lhs1, lhs2, span, false);
             }
         }
@@ -246,17 +256,20 @@ fn parse<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(ExprOrIdent<'hir>, &'a Expr<
 
 /// Implementation of the xor case for `MANUAL_SWAP` lint.
 fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) {
-    for window in block.stmts.windows(3) {
+    for [s1, s2, s3] in block.stmts.array_windows::<3>() {
         if_chain! {
-            if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(&window[0]);
-            if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(&window[1]);
-            if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(&window[2]);
+            let ctxt = s1.span.ctxt();
+            if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(s1, ctxt);
+            if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(s2, ctxt);
+            if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(s3, ctxt);
             if eq_expr_value(cx, lhs0, rhs1);
             if eq_expr_value(cx, lhs2, rhs1);
             if eq_expr_value(cx, lhs1, rhs0);
             if eq_expr_value(cx, lhs1, rhs2);
+            if s2.span.ctxt() == ctxt;
+            if s3.span.ctxt() == ctxt;
             then {
-                let span = window[0].span.to(window[2].span);
+                let span = s1.span.to(s3.span);
                 generate_swap_warning(cx, lhs0, rhs0, span, true);
             }
         };
@@ -264,9 +277,12 @@ fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) {
 }
 
 /// Returns the lhs and rhs of an xor assignment statement.
-fn extract_sides_of_xor_assign<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(&'a Expr<'hir>, &'a Expr<'hir>)> {
-    if let StmtKind::Semi(expr) = stmt.kind {
-        if let ExprKind::AssignOp(
+fn extract_sides_of_xor_assign<'a, 'hir>(
+    stmt: &'a Stmt<'hir>,
+    ctxt: SyntaxContext,
+) -> Option<(&'a Expr<'hir>, &'a Expr<'hir>)> {
+    if let StmtKind::Semi(expr) = stmt.kind
+        && let ExprKind::AssignOp(
             Spanned {
                 node: BinOpKind::BitXor,
                 ..
@@ -274,9 +290,10 @@ fn extract_sides_of_xor_assign<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(&'a Ex
             lhs,
             rhs,
         ) = expr.kind
-        {
-            return Some((lhs, rhs));
-        }
+        && expr.span.ctxt() == ctxt
+    {
+        Some((lhs, rhs))
+    } else {
+        None
     }
-    None
 }
diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
index c01cbe5090f..0dc30f7a935 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
@@ -458,7 +458,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let null_fn: Option<fn()> = None;
     /// ```
-    #[clippy::version = "1.67.0"]
+    #[clippy::version = "1.68.0"]
     pub TRANSMUTE_NULL_TO_FN,
     correctness,
     "transmute results in a null function pointer, which is undefined behavior"
diff --git a/src/tools/clippy/clippy_lints/src/transmute/utils.rs b/src/tools/clippy/clippy_lints/src/transmute/utils.rs
index cddaf9450ea..62efd13b8d9 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/utils.rs
@@ -33,38 +33,37 @@ pub(super) fn check_cast<'tcx>(
     let hir_id = e.hir_id;
     let local_def_id = hir_id.owner.def_id;
 
-    Inherited::build(cx.tcx, local_def_id).enter(|inherited| {
-        let fn_ctxt = FnCtxt::new(inherited, cx.param_env, local_def_id);
+    let inherited = Inherited::new(cx.tcx, local_def_id);
+    let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, local_def_id);
 
-        // If we already have errors, we can't be sure we can pointer cast.
+    // If we already have errors, we can't be sure we can pointer cast.
+    assert!(
+        !fn_ctxt.errors_reported_since_creation(),
+        "Newly created FnCtxt contained errors"
+    );
+
+    if let Ok(check) = cast::CastCheck::new(
+        &fn_ctxt,
+        e,
+        from_ty,
+        to_ty,
+        // We won't show any error to the user, so we don't care what the span is here.
+        DUMMY_SP,
+        DUMMY_SP,
+        hir::Constness::NotConst,
+    ) {
+        let res = check.do_check(&fn_ctxt);
+
+        // do_check's documentation says that it might return Ok and create
+        // errors in the fcx instead of returning Err in some cases. Those cases
+        // should be filtered out before getting here.
         assert!(
             !fn_ctxt.errors_reported_since_creation(),
-            "Newly created FnCtxt contained errors"
+            "`fn_ctxt` contained errors after cast check!"
         );
 
-        if let Ok(check) = cast::CastCheck::new(
-            &fn_ctxt,
-            e,
-            from_ty,
-            to_ty,
-            // We won't show any error to the user, so we don't care what the span is here.
-            DUMMY_SP,
-            DUMMY_SP,
-            hir::Constness::NotConst,
-        ) {
-            let res = check.do_check(&fn_ctxt);
-
-            // do_check's documentation says that it might return Ok and create
-            // errors in the fcx instead of returning Err in some cases. Those cases
-            // should be filtered out before getting here.
-            assert!(
-                !fn_ctxt.errors_reported_since_creation(),
-                "`fn_ctxt` contained errors after cast check!"
-            );
-
-            res.ok()
-        } else {
-            None
-        }
-    })
+        res.ok()
+    } else {
+        None
+    }
 }
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
index d6167a62169..3430b6e3734 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
@@ -5,7 +5,7 @@ use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source};
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, Node, PatKind, QPath, TyKind};
+use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, MatchSource, Node, PatKind, QPath, TyKind};
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty;
@@ -41,6 +41,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
                 );
             }
         } else {
+            if let ExprKind::Match(_, _, MatchSource::AwaitDesugar) = init.kind {
+                return
+            }
+
             span_lint_and_then(
                 cx,
                 LET_UNIT_VALUE,
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index e7c54000684..8ea5475fb25 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -10,8 +10,8 @@ use rustc_hir::{
     def::{CtorOf, DefKind, Res},
     def_id::LocalDefId,
     intravisit::{walk_inf, walk_ty, Visitor},
-    Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath,
-    TyKind,
+    Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericParam, GenericParamKind, HirId, Impl, ImplItemKind, Item,
+    ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind,
 };
 use rustc_hir_analysis::hir_ty_to_ty;
 use rustc_lint::{LateContext, LateLintPass};
@@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
         // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that
         // we're in an `impl` or nested item, that we don't want to lint
         let stack_item = if_chain! {
-            if let ItemKind::Impl(Impl { self_ty, .. }) = item.kind;
+            if let ItemKind::Impl(Impl { self_ty, generics,.. }) = item.kind;
             if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind;
             let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
             if parameters.as_ref().map_or(true, |params| {
@@ -105,10 +105,17 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
             if !item.span.from_expansion();
             if !is_from_proc_macro(cx, item); // expensive, should be last check
             then {
+                // Self cannot be used inside const generic parameters
+                let types_to_skip = generics.params.iter().filter_map(|param| {
+                    match param {
+                        GenericParam { kind: GenericParamKind::Const { ty: Ty { hir_id, ..}, ..}, ..} => Some(*hir_id),
+                        _ => None,
+                    }
+                }).chain(std::iter::once(self_ty.hir_id)).collect();
                 StackItem::Check {
                     impl_id: item.owner_id.def_id,
                     in_body: 0,
-                    types_to_skip: std::iter::once(self_ty.hir_id).collect(),
+                    types_to_skip,
                 }
             } else {
                 StackItem::NoCheck
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index c37e5bb6716..f31c3fdb095 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -588,7 +588,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
                     },
                 }
             },
-            ExprKind::Err(_) => kind!("Err"),
+            ExprKind::Err(_) => kind!("Err(_)"),
             ExprKind::DropTemps(expr) => {
                 bind!(self, expr);
                 kind!("DropTemps({expr})");
diff --git a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
new file mode 100644
index 00000000000..be56b842b98
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
@@ -0,0 +1,23 @@
+use clippy_utils::macros::collect_ast_format_args;
+use rustc_ast::{Expr, ExprKind};
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Collects [`rustc_ast::FormatArgs`] so that future late passes can call
+    /// [`clippy_utils::macros::find_format_args`]
+    pub FORMAT_ARGS_COLLECTOR,
+    internal_warn,
+    "collects `format_args` AST nodes for use in later lints"
+}
+
+declare_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]);
+
+impl EarlyLintPass for FormatArgsCollector {
+    fn check_expr(&mut self, _: &EarlyContext<'_>, expr: &Expr) {
+        if let ExprKind::FormatArgs(args) = &expr.kind {
+            collect_ast_format_args(expr.span, args);
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index b1b5164ffb3..3d0d4a52511 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -26,7 +26,7 @@ use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol::Ident;
 use rustc_span::{sym, Loc, Span, Symbol};
 use serde::{ser::SerializeStruct, Serialize, Serializer};
-use std::collections::BinaryHeap;
+use std::collections::{BTreeSet, BinaryHeap};
 use std::fmt;
 use std::fmt::Write as _;
 use std::fs::{self, OpenOptions};
@@ -264,6 +264,9 @@ struct LintMetadata {
     /// This field is only used in the output and will only be
     /// mapped shortly before the actual output.
     applicability: Option<ApplicabilityInfo>,
+    /// All the past names of lints which have been renamed.
+    #[serde(skip_serializing_if = "BTreeSet::is_empty")]
+    former_ids: BTreeSet<String>,
 }
 
 impl LintMetadata {
@@ -283,6 +286,7 @@ impl LintMetadata {
             version,
             docs,
             applicability: None,
+            former_ids: BTreeSet::new(),
         }
     }
 }
@@ -901,6 +905,7 @@ fn collect_renames(lints: &mut Vec<LintMetadata>) {
                         if name == lint_name;
                         if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX);
                         then {
+                            lint.former_ids.insert(past_name.to_owned());
                             writeln!(collected, "* `{past_name}`").unwrap();
                             names.push(past_name.to_string());
                         }
diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs
index 787e9fd982c..dc647af264c 100644
--- a/src/tools/clippy/clippy_lints/src/utils/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs
@@ -1,5 +1,6 @@
 pub mod author;
 pub mod conf;
 pub mod dump_hir;
+pub mod format_args_collector;
 #[cfg(feature = "internal")]
 pub mod internal_lints;
diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
index e4d1ee195c4..e105452e1c5 100644
--- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
@@ -155,14 +155,10 @@ impl LateLintPass<'_> for WildcardImports {
                     )
                 };
 
-                let imports_string = if used_imports.len() == 1 {
-                    used_imports.iter().next().unwrap().to_string()
+                let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(false);
+                let imports_string = if imports.len() == 1 {
+                    imports.pop().unwrap()
                 } else {
-                    let mut imports = used_imports
-                        .iter()
-                        .map(ToString::to_string)
-                        .collect::<Vec<_>>();
-                    imports.sort();
                     if braced_glob {
                         imports.join(", ")
                     } else {
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index df335038881..8114a8463fa 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -1,10 +1,11 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
-use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn, MacroCall};
+use clippy_utils::macros::{find_format_args, format_arg_removal_span, root_macro_call_first_node, MacroCall};
 use clippy_utils::source::{expand_past_previous_comma, snippet_opt};
 use clippy_utils::{is_in_cfg_test, is_in_test_function};
-use rustc_ast::LitKind;
+use rustc_ast::token::LitKind;
+use rustc_ast::{FormatArgPosition, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, FormatTrait};
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, HirIdMap, Impl, Item, ItemKind};
+use rustc_hir::{Expr, Impl, Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{sym, BytePos};
@@ -297,34 +298,40 @@ impl<'tcx> LateLintPass<'tcx> for Write {
             _ => return,
         }
 
-        let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn) else { return };
-
-        // ignore `writeln!(w)` and `write!(v, some_macro!())`
-        if format_args.format_string.span.from_expansion() {
-            return;
-        }
+        find_format_args(cx, expr, macro_call.expn, |format_args| {
+            // ignore `writeln!(w)` and `write!(v, some_macro!())`
+            if format_args.span.from_expansion() {
+                return;
+            }
 
-        match diag_name {
-            sym::print_macro | sym::eprint_macro | sym::write_macro => {
-                check_newline(cx, &format_args, &macro_call, name);
-            },
-            sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
-                check_empty_string(cx, &format_args, &macro_call, name);
-            },
-            _ => {},
-        }
+            match diag_name {
+                sym::print_macro | sym::eprint_macro | sym::write_macro => {
+                    check_newline(cx, format_args, &macro_call, name);
+                },
+                sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
+                    check_empty_string(cx, format_args, &macro_call, name);
+                },
+                _ => {},
+            }
 
-        check_literal(cx, &format_args, name);
+            check_literal(cx, format_args, name);
 
-        if !self.in_debug_impl {
-            for arg in &format_args.args {
-                if arg.format.r#trait == sym::Debug {
-                    span_lint(cx, USE_DEBUG, arg.span, "use of `Debug`-based formatting");
+            if !self.in_debug_impl {
+                for piece in &format_args.template {
+                    if let &FormatArgsPiece::Placeholder(FormatPlaceholder {
+                        span: Some(span),
+                        format_trait: FormatTrait::Debug,
+                        ..
+                    }) = piece
+                    {
+                        span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting");
+                    }
                 }
             }
-        }
+        });
     }
 }
+
 fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
     if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), .. }) = &item.kind
         && let Some(trait_id) = trait_ref.trait_def_id()
@@ -335,16 +342,18 @@ fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
     }
 }
 
-fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) {
-    let format_string_parts = &format_args.format_string.parts;
-    let mut format_string_span = format_args.format_string.span;
-
-    let Some(last) = format_string_parts.last() else { return };
+fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) {
+    let Some(FormatArgsPiece::Literal(last)) = format_args.template.last() else { return };
 
     let count_vertical_whitespace = || {
-        format_string_parts
+        format_args
+            .template
             .iter()
-            .flat_map(|part| part.as_str().chars())
+            .filter_map(|piece| match piece {
+                FormatArgsPiece::Literal(literal) => Some(literal),
+                FormatArgsPiece::Placeholder(_) => None,
+            })
+            .flat_map(|literal| literal.as_str().chars())
             .filter(|ch| matches!(ch, '\r' | '\n'))
             .count()
     };
@@ -352,10 +361,9 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
     if last.as_str().ends_with('\n')
         // ignore format strings with other internal vertical whitespace
         && count_vertical_whitespace() == 1
-
-        // ignore trailing arguments: `print!("Issue\n{}", 1265);`
-        && format_string_parts.len() > format_args.args.len()
     {
+        let mut format_string_span = format_args.span;
+
         let lint = if name == "write" {
             format_string_span = expand_past_previous_comma(cx, format_string_span);
 
@@ -373,7 +381,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
                 let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!');
                 let Some(format_snippet) = snippet_opt(cx, format_string_span) else { return };
 
-                if format_string_parts.len() == 1 && last.as_str() == "\n" {
+                if format_args.template.len() == 1 && last.as_str() == "\n" {
                     // print!("\n"), write!(f, "\n")
 
                     diag.multipart_suggestion(
@@ -398,11 +406,12 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
     }
 }
 
-fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) {
-    if let [part] = &format_args.format_string.parts[..]
-        && let mut span = format_args.format_string.span
-        && part.as_str() == "\n"
+fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) {
+    if let [FormatArgsPiece::Literal(literal)] = &format_args.template[..]
+        && literal.as_str() == "\n"
     {
+        let mut span = format_args.span;
+
         let lint = if name == "writeln" {
             span = expand_past_previous_comma(cx, span);
 
@@ -428,33 +437,43 @@ fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, ma
     }
 }
 
-fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &str) {
-    let mut counts = HirIdMap::<usize>::default();
-    for param in format_args.params() {
-        *counts.entry(param.value.hir_id).or_default() += 1;
+fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
+    let arg_index = |argument: &FormatArgPosition| argument.index.unwrap_or_else(|pos| pos);
+
+    let mut counts = vec![0u32; format_args.arguments.all_args().len()];
+    for piece in &format_args.template {
+        if let FormatArgsPiece::Placeholder(placeholder) = piece {
+            counts[arg_index(&placeholder.argument)] += 1;
+        }
     }
 
-    for arg in &format_args.args {
-        let value = arg.param.value;
-
-        if counts[&value.hir_id] == 1
-            && arg.format.is_default()
-            && let ExprKind::Lit(lit) = &value.kind
-            && !value.span.from_expansion()
-            && let Some(value_string) = snippet_opt(cx, value.span)
-        {
-            let (replacement, replace_raw) = match lit.node {
-                LitKind::Str(..) => extract_str_literal(&value_string),
-                LitKind::Char(ch) => (
-                    match ch {
-                        '"' => "\\\"",
-                        '\'' => "'",
+    for piece in &format_args.template {
+        if let FormatArgsPiece::Placeholder(FormatPlaceholder {
+            argument,
+            span: Some(placeholder_span),
+            format_trait: FormatTrait::Display,
+            format_options,
+        }) = piece
+            && *format_options == FormatOptions::default()
+            && let index = arg_index(argument)
+            && counts[index] == 1
+            && let Some(arg) = format_args.arguments.by_index(index)
+            && let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind
+            && !arg.expr.span.from_expansion()
+            && let Some(value_string) = snippet_opt(cx, arg.expr.span)
+    {
+            let (replacement, replace_raw) = match lit.kind {
+                LitKind::Str | LitKind::StrRaw(_)  => extract_str_literal(&value_string),
+                LitKind::Char => (
+                    match lit.symbol.as_str() {
+                        "\"" => "\\\"",
+                        "\\'" => "'",
                         _ => &value_string[1..value_string.len() - 1],
                     }
                     .to_string(),
                     false,
                 ),
-                LitKind::Bool(b) => (b.to_string(), false),
+                LitKind::Bool => (lit.symbol.to_string(), false),
                 _ => continue,
             };
 
@@ -464,7 +483,9 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &
                 PRINT_LITERAL
             };
 
-            let format_string_is_raw = format_args.format_string.style.is_some();
+            let Some(format_string_snippet) = snippet_opt(cx, format_args.span) else { continue };
+            let format_string_is_raw = format_string_snippet.starts_with('r');
+
             let replacement = match (format_string_is_raw, replace_raw) {
                 (false, false) => Some(replacement),
                 (false, true) => Some(replacement.replace('"', "\\\"").replace('\\', "\\\\")),
@@ -485,23 +506,24 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &
             span_lint_and_then(
                 cx,
                 lint,
-                value.span,
+                arg.expr.span,
                 "literal with an empty format string",
                 |diag| {
                     if let Some(replacement) = replacement
                         // `format!("{}", "a")`, `format!("{named}", named = "b")
                         //              ~~~~~                      ~~~~~~~~~~~~~
-                        && let Some(value_span) = format_args.value_with_prev_comma_span(value.hir_id)
+                        && let Some(removal_span) = format_arg_removal_span(format_args, index)
                     {
                         let replacement = replacement.replace('{', "{{").replace('}', "}}");
                         diag.multipart_suggestion(
                             "try this",
-                            vec![(arg.span, replacement), (value_span, String::new())],
+                            vec![(*placeholder_span, replacement), (removal_span, String::new())],
                             Applicability::MachineApplicable,
                         );
                     }
                 },
             );
+
         }
     }
 }
diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml
index 173469f6cdc..124ebd164e6 100644
--- a/src/tools/clippy/clippy_utils/Cargo.toml
+++ b/src/tools/clippy/clippy_utils/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_utils"
-version = "0.1.69"
+version = "0.1.70"
 edition = "2021"
 publish = false
 
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index bcfedd07ed1..44b6b9f7b0b 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -617,7 +617,7 @@ fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Re
 /// Can return multiple resolutions when there are multiple versions of the same crate, e.g.
 /// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0.
 ///
-/// Also returns multiple results when there are mulitple paths under the same name e.g. `std::vec`
+/// Also returns multiple results when there are multiple paths under the same name e.g. `std::vec`
 /// would have both a [`DefKind::Mod`] and [`DefKind::Macro`].
 ///
 /// This function is expensive and should be used sparingly.
diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs
index be6133d3202..e135bd9feee 100644
--- a/src/tools/clippy/clippy_utils/src/macros.rs
+++ b/src/tools/clippy/clippy_utils/src/macros.rs
@@ -6,6 +6,8 @@ use crate::visitors::{for_each_expr, Descend};
 use arrayvec::ArrayVec;
 use itertools::{izip, Either, Itertools};
 use rustc_ast::ast::LitKind;
+use rustc_ast::FormatArgs;
+use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::intravisit::{walk_expr, Visitor};
 use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, LangItem, Node, QPath, TyKind};
 use rustc_lexer::unescape::unescape_literal;
@@ -15,8 +17,10 @@ use rustc_parse_format::{self as rpf, Alignment};
 use rustc_span::def_id::DefId;
 use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
 use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol};
+use std::cell::RefCell;
 use std::iter::{once, zip};
 use std::ops::ControlFlow;
+use std::sync::atomic::{AtomicBool, Ordering};
 
 const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
     sym::assert_eq_macro,
@@ -213,6 +217,7 @@ pub fn is_assert_macro(cx: &LateContext<'_>, def_id: DefId) -> bool {
     matches!(name, sym::assert_macro | sym::debug_assert_macro)
 }
 
+#[derive(Debug)]
 pub enum PanicExpn<'a> {
     /// No arguments - `panic!()`
     Empty,
@@ -226,10 +231,7 @@ pub enum PanicExpn<'a> {
 
 impl<'a> PanicExpn<'a> {
     pub fn parse(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Self> {
-        if !macro_backtrace(expr.span).any(|macro_call| is_panic(cx, macro_call.def_id)) {
-            return None;
-        }
-        let ExprKind::Call(callee, [arg]) = &expr.kind else { return None };
+        let ExprKind::Call(callee, [arg, rest @ ..]) = &expr.kind else { return None };
         let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None };
         let result = match path.segments.last().unwrap().ident.as_str() {
             "panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty,
@@ -239,6 +241,21 @@ impl<'a> PanicExpn<'a> {
                 Self::Display(e)
             },
             "panic_fmt" => Self::Format(FormatArgsExpn::parse(cx, arg)?),
+            // Since Rust 1.52, `assert_{eq,ne}` macros expand to use:
+            // `core::panicking::assert_failed(.., left_val, right_val, None | Some(format_args!(..)));`
+            "assert_failed" => {
+                // It should have 4 arguments in total (we already matched with the first argument,
+                // so we're just checking for 3)
+                if rest.len() != 3 {
+                    return None;
+                }
+                // `msg_arg` is either `None` (no custom message) or `Some(format_args!(..))` (custom message)
+                let msg_arg = &rest[2];
+                match msg_arg.kind {
+                    ExprKind::Call(_, [fmt_arg]) => Self::Format(FormatArgsExpn::parse(cx, fmt_arg)?),
+                    _ => Self::Empty,
+                }
+            },
             _ => return None,
         };
         Some(result)
@@ -251,7 +268,17 @@ pub fn find_assert_args<'a>(
     expr: &'a Expr<'a>,
     expn: ExpnId,
 ) -> Option<(&'a Expr<'a>, PanicExpn<'a>)> {
-    find_assert_args_inner(cx, expr, expn).map(|([e], p)| (e, p))
+    find_assert_args_inner(cx, expr, expn).map(|([e], mut p)| {
+        // `assert!(..)` expands to `core::panicking::panic("assertion failed: ...")` (which we map to
+        // `PanicExpn::Str(..)`) and `assert!(.., "..")` expands to
+        // `core::panicking::panic_fmt(format_args!(".."))` (which we map to `PanicExpn::Format(..)`).
+        // So even we got `PanicExpn::Str(..)` that means there is no custom message provided
+        if let PanicExpn::Str(_) = p {
+            p = PanicExpn::Empty;
+        }
+
+        (e, p)
+    })
 }
 
 /// Finds the arguments of an `assert_eq!` or `debug_assert_eq!` macro call within the macro
@@ -275,13 +302,12 @@ fn find_assert_args_inner<'a, const N: usize>(
         Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?,
     };
     let mut args = ArrayVec::new();
-    let mut panic_expn = None;
-    let _: Option<!> = for_each_expr(expr, |e| {
+    let panic_expn = for_each_expr(expr, |e| {
         if args.is_full() {
-            if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() {
-                panic_expn = PanicExpn::parse(cx, e);
+            match PanicExpn::parse(cx, e) {
+                Some(expn) => ControlFlow::Break(expn),
+                None => ControlFlow::Continue(Descend::Yes),
             }
-            ControlFlow::Continue(Descend::from(panic_expn.is_none()))
         } else if is_assert_arg(cx, e, expn) {
             args.push(e);
             ControlFlow::Continue(Descend::No)
@@ -339,6 +365,77 @@ fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) ->
     }
 }
 
+thread_local! {
+    /// We preserve the [`FormatArgs`] structs from the early pass for use in the late pass to be
+    /// able to access the many features of a [`LateContext`].
+    ///
+    /// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an
+    /// assumption that the early pass the populates the map and the later late passes will all be
+    /// running on the same thread.
+    static AST_FORMAT_ARGS: RefCell<FxHashMap<Span, FormatArgs>> = {
+        static CALLED: AtomicBool = AtomicBool::new(false);
+        debug_assert!(
+            !CALLED.swap(true, Ordering::SeqCst),
+            "incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread",
+        );
+
+        RefCell::default()
+    };
+}
+
+/// Record [`rustc_ast::FormatArgs`] for use in late lint passes, this should only be called by
+/// `FormatArgsCollector`
+pub fn collect_ast_format_args(span: Span, format_args: &FormatArgs) {
+    AST_FORMAT_ARGS.with(|ast_format_args| {
+        ast_format_args.borrow_mut().insert(span, format_args.clone());
+    });
+}
+
+/// Calls `callback` with an AST [`FormatArgs`] node if one is found
+pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, callback: impl FnOnce(&FormatArgs)) {
+    let format_args_expr = for_each_expr(start, |expr| {
+        let ctxt = expr.span.ctxt();
+        if ctxt == start.span.ctxt() {
+            ControlFlow::Continue(Descend::Yes)
+        } else if ctxt.outer_expn().is_descendant_of(expn_id)
+            && macro_backtrace(expr.span)
+                .map(|macro_call| cx.tcx.item_name(macro_call.def_id))
+                .any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))
+        {
+            ControlFlow::Break(expr)
+        } else {
+            ControlFlow::Continue(Descend::No)
+        }
+    });
+
+    if let Some(format_args_expr) = format_args_expr {
+        AST_FORMAT_ARGS.with(|ast_format_args| {
+            ast_format_args.borrow().get(&format_args_expr.span).map(callback);
+        });
+    }
+}
+
+/// Returns the [`Span`] of the value at `index` extended to the previous comma, e.g. for the value
+/// `10`
+///
+/// ```ignore
+/// format("{}.{}", 10, 11)
+/// //            ^^^^
+/// ```
+pub fn format_arg_removal_span(format_args: &FormatArgs, index: usize) -> Option<Span> {
+    let ctxt = format_args.span.ctxt();
+
+    let current = hygiene::walk_chain(format_args.arguments.by_index(index)?.expr.span, ctxt);
+
+    let prev = if index == 0 {
+        format_args.span
+    } else {
+        hygiene::walk_chain(format_args.arguments.by_index(index - 1)?.expr.span, ctxt)
+    };
+
+    Some(current.with_lo(prev.hi()))
+}
+
 /// The format string doesn't exist in the HIR, so we reassemble it from source code
 #[derive(Debug)]
 pub struct FormatString {
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index 1a35fe05067..24403e8b6f3 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -241,6 +241,7 @@ fn check_statement<'tcx>(
         | StatementKind::StorageDead(_)
         | StatementKind::Retag { .. }
         | StatementKind::AscribeUserType(..)
+        | StatementKind::PlaceMention(..)
         | StatementKind::Coverage(..)
         | StatementKind::ConstEvalCounter
         | StatementKind::Nop => Ok(()),
@@ -299,10 +300,6 @@ fn check_terminator<'tcx>(
         | TerminatorKind::Unreachable => Ok(()),
 
         TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
-        TerminatorKind::DropAndReplace { place, value, .. } => {
-            check_place(tcx, *place, span, body)?;
-            check_operand(tcx, value, span, body)
-        },
 
         TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body),
 
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 41e34eba0ad..e0ea3952785 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -16,9 +16,9 @@ use rustc_infer::infer::{
 use rustc_lint::LateContext;
 use rustc_middle::mir::interpret::{ConstValue, Scalar};
 use rustc_middle::ty::{
-    self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv, Predicate,
-    PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
-    TypeVisitor, UintTy, VariantDef, VariantDiscr,
+    self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv, Predicate, PredicateKind,
+    Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
+    UintTy, VariantDef, VariantDiscr,
 };
 use rustc_middle::ty::{GenericArg, GenericArgKind};
 use rustc_span::symbol::Ident;
diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml
index 80eee368178..5c9f76dbbc6 100644
--- a/src/tools/clippy/declare_clippy_lint/Cargo.toml
+++ b/src/tools/clippy/declare_clippy_lint/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "declare_clippy_lint"
-version = "0.1.69"
+version = "0.1.70"
 edition = "2021"
 publish = false
 
diff --git a/src/tools/clippy/lintcheck/Cargo.toml b/src/tools/clippy/lintcheck/Cargo.toml
index 653121af54d..27d32f39003 100644
--- a/src/tools/clippy/lintcheck/Cargo.toml
+++ b/src/tools/clippy/lintcheck/Cargo.toml
@@ -8,12 +8,16 @@ repository = "https://github.com/rust-lang/rust-clippy"
 categories = ["development-tools"]
 edition = "2021"
 publish = false
+default-run = "lintcheck"
 
 [dependencies]
+anyhow = "1.0.69"
 cargo_metadata = "0.15.3"
-clap = "4.1.4"
+clap = { version = "4.1.8", features = ["derive", "env"] }
+crates_io_api = "0.8.1"
 crossbeam-channel = "0.5.6"
 flate2 = "1.0"
+indicatif = "0.17.3"
 rayon = "1.5.1"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0.85"
@@ -24,3 +28,11 @@ walkdir = "2.3"
 
 [features]
 deny-warnings = []
+
+[[bin]]
+name = "lintcheck"
+path = "src/main.rs"
+
+[[bin]]
+name = "popular-crates"
+path = "src/popular-crates.rs"
diff --git a/src/tools/clippy/lintcheck/README.md b/src/tools/clippy/lintcheck/README.md
index 6142de5e313..e997eb47e32 100644
--- a/src/tools/clippy/lintcheck/README.md
+++ b/src/tools/clippy/lintcheck/README.md
@@ -25,6 +25,15 @@ the repo root.
 
 The results will then be saved to `lintcheck-logs/custom_logs.toml`.
 
+The `custom.toml` file may be built using <https://crates.io> recently most
+downloaded crates by using the `popular-crates` binary from the `lintcheck`
+directory. For example, to retrieve the 100 recently most downloaded crates:
+
+```
+cargo run --release --bin popular-crates -- -n 100 custom.toml
+```
+
+
 ### Configuring the Crate Sources
 
 The sources to check are saved in a `toml` file. There are three types of
diff --git a/src/tools/clippy/lintcheck/src/config.rs b/src/tools/clippy/lintcheck/src/config.rs
index e0244ddcecb..3f01e9bb0a7 100644
--- a/src/tools/clippy/lintcheck/src/config.rs
+++ b/src/tools/clippy/lintcheck/src/config.rs
@@ -1,131 +1,79 @@
-use clap::{Arg, ArgAction, ArgMatches, Command};
-use std::env;
-use std::path::PathBuf;
+use clap::Parser;
+use std::{num::NonZeroUsize, path::PathBuf};
 
-fn get_clap_config() -> ArgMatches {
-    Command::new("lintcheck")
-        .about("run clippy on a set of crates and check output")
-        .args([
-            Arg::new("only")
-                .action(ArgAction::Set)
-                .value_name("CRATE")
-                .long("only")
-                .help("Only process a single crate of the list"),
-            Arg::new("crates-toml")
-                .action(ArgAction::Set)
-                .value_name("CRATES-SOURCES-TOML-PATH")
-                .long("crates-toml")
-                .help("Set the path for a crates.toml where lintcheck should read the sources from"),
-            Arg::new("threads")
-                .action(ArgAction::Set)
-                .value_name("N")
-                .value_parser(clap::value_parser!(usize))
-                .short('j')
-                .long("jobs")
-                .help("Number of threads to use, 0 automatic choice"),
-            Arg::new("fix")
-                .long("fix")
-                .help("Runs cargo clippy --fix and checks if all suggestions apply"),
-            Arg::new("filter")
-                .long("filter")
-                .action(ArgAction::Append)
-                .value_name("clippy_lint_name")
-                .help("Apply a filter to only collect specified lints, this also overrides `allow` attributes"),
-            Arg::new("markdown")
-                .long("markdown")
-                .help("Change the reports table to use markdown links"),
-            Arg::new("recursive")
-                .long("recursive")
-                .help("Run clippy on the dependencies of crates specified in crates-toml")
-                .conflicts_with("threads")
-                .conflicts_with("fix"),
-        ])
-        .get_matches()
-}
-
-#[derive(Debug, Clone)]
+#[derive(Clone, Debug, Parser)]
 pub(crate) struct LintcheckConfig {
-    /// max number of jobs to spawn (default 1)
+    /// Number of threads to use (default: all unless --fix or --recursive)
+    #[clap(
+        long = "jobs",
+        short = 'j',
+        value_name = "N",
+        default_value_t = 0,
+        hide_default_value = true
+    )]
     pub max_jobs: usize,
-    /// we read the sources to check from here
+    /// Set the path for a crates.toml where lintcheck should read the sources from
+    #[clap(
+        long = "crates-toml",
+        value_name = "CRATES-SOURCES-TOML-PATH",
+        default_value = "lintcheck/lintcheck_crates.toml",
+        hide_default_value = true,
+        env = "LINTCHECK_TOML",
+        hide_env = true
+    )]
     pub sources_toml_path: PathBuf,
-    /// we save the clippy lint results here
-    pub lintcheck_results_path: PathBuf,
-    /// Check only a specified package
+    /// File to save the clippy lint results here
+    #[clap(skip = "")]
+    pub lintcheck_results_path: PathBuf, // Overridden in new()
+    /// Only process a single crate on the list
+    #[clap(long, value_name = "CRATE")]
     pub only: Option<String>,
-    /// whether to just run --fix and not collect all the warnings
+    /// Runs cargo clippy --fix and checks if all suggestions apply
+    #[clap(long, conflicts_with("max_jobs"))]
     pub fix: bool,
-    /// A list of lints that this lintcheck run should focus on
+    /// Apply a filter to only collect specified lints, this also overrides `allow` attributes
+    #[clap(long = "filter", value_name = "clippy_lint_name", use_value_delimiter = true)]
     pub lint_filter: Vec<String>,
-    /// Indicate if the output should support markdown syntax
+    /// Change the reports table to use markdown links
+    #[clap(long)]
     pub markdown: bool,
-    /// Run clippy on the dependencies of crates
+    /// Run clippy on the dependencies of crates specified in crates-toml
+    #[clap(long, conflicts_with("max_jobs"))]
     pub recursive: bool,
 }
 
 impl LintcheckConfig {
     pub fn new() -> Self {
-        let clap_config = get_clap_config();
-
-        // first, check if we got anything passed via the LINTCHECK_TOML env var,
-        // if not, ask clap if we got any value for --crates-toml  <foo>
-        // if not, use the default "lintcheck/lintcheck_crates.toml"
-        let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| {
-            clap_config
-                .get_one::<String>("crates-toml")
-                .map_or("lintcheck/lintcheck_crates.toml", |s| &**s)
-                .into()
-        });
-
-        let markdown = clap_config.contains_id("markdown");
-        let sources_toml_path = PathBuf::from(sources_toml);
+        let mut config = LintcheckConfig::parse();
 
         // for the path where we save the lint results, get the filename without extension (so for
         // wasd.toml, use "wasd"...)
-        let filename: PathBuf = sources_toml_path.file_stem().unwrap().into();
-        let lintcheck_results_path = PathBuf::from(format!(
+        let filename: PathBuf = config.sources_toml_path.file_stem().unwrap().into();
+        config.lintcheck_results_path = PathBuf::from(format!(
             "lintcheck-logs/{}_logs.{}",
             filename.display(),
-            if markdown { "md" } else { "txt" }
+            if config.markdown { "md" } else { "txt" }
         ));
 
-        // look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and
-        // use half of that for the physical core count
-        // by default use a single thread
-        let max_jobs = match clap_config.get_one::<usize>("threads") {
-            Some(&0) => {
-                // automatic choice
-                // Rayon seems to return thread count so half that for core count
-                rayon::current_num_threads() / 2
-            },
-            Some(&threads) => threads,
-            // no -j passed, use a single thread
-            None => 1,
+        // look at the --threads arg, if 0 is passed, use the threads count
+        if config.max_jobs == 0 {
+            config.max_jobs = if config.fix || config.recursive {
+                1
+            } else {
+                std::thread::available_parallelism().map_or(1, NonZeroUsize::get)
+            };
         };
 
-        let lint_filter: Vec<String> = clap_config
-            .get_many::<String>("filter")
-            .map(|iter| {
-                iter.map(|lint_name| {
-                    let mut filter = lint_name.replace('_', "-");
-                    if !filter.starts_with("clippy::") {
-                        filter.insert_str(0, "clippy::");
-                    }
-                    filter
-                })
-                .collect()
-            })
-            .unwrap_or_default();
-
-        LintcheckConfig {
-            max_jobs,
-            sources_toml_path,
-            lintcheck_results_path,
-            only: clap_config.get_one::<String>("only").map(String::from),
-            fix: clap_config.contains_id("fix"),
-            lint_filter,
-            markdown,
-            recursive: clap_config.contains_id("recursive"),
+        for lint_name in &mut config.lint_filter {
+            *lint_name = format!(
+                "clippy::{}",
+                lint_name
+                    .strip_prefix("clippy::")
+                    .unwrap_or(lint_name)
+                    .replace('_', "-")
+            );
         }
+
+        config
     }
 }
diff --git a/src/tools/clippy/lintcheck/src/popular-crates.rs b/src/tools/clippy/lintcheck/src/popular-crates.rs
new file mode 100644
index 00000000000..fdab984ad86
--- /dev/null
+++ b/src/tools/clippy/lintcheck/src/popular-crates.rs
@@ -0,0 +1,65 @@
+#![deny(clippy::pedantic)]
+
+use clap::Parser;
+use crates_io_api::{CratesQueryBuilder, Sort, SyncClient};
+use indicatif::ProgressBar;
+use std::collections::HashSet;
+use std::fs::File;
+use std::io::{BufWriter, Write};
+use std::path::PathBuf;
+use std::time::Duration;
+
+#[derive(Parser)]
+struct Opts {
+    /// Output TOML file name
+    output: PathBuf,
+    /// Number of crate names to download
+    #[clap(short, long, default_value_t = 100)]
+    number: usize,
+    /// Do not output progress
+    #[clap(short, long)]
+    quiet: bool,
+}
+
+fn main() -> anyhow::Result<()> {
+    let opts = Opts::parse();
+    let mut output = BufWriter::new(File::create(opts.output)?);
+    output.write_all(b"[crates]\n")?;
+    let client = SyncClient::new(
+        "clippy/lintcheck (github.com/rust-lang/rust-clippy/)",
+        Duration::from_secs(1),
+    )?;
+    let mut seen_crates = HashSet::new();
+    let pb = if opts.quiet {
+        None
+    } else {
+        Some(ProgressBar::new(opts.number as u64))
+    };
+    let mut query = CratesQueryBuilder::new()
+        .sort(Sort::RecentDownloads)
+        .page_size(100)
+        .build();
+    while seen_crates.len() < opts.number {
+        let retrieved = client.crates(query.clone())?.crates;
+        if retrieved.is_empty() {
+            eprintln!("No more than {} crates available from API", seen_crates.len());
+            break;
+        }
+        for c in retrieved {
+            if seen_crates.insert(c.name.clone()) {
+                output.write_all(
+                    format!(
+                        "{} = {{ name = '{}', versions = ['{}'] }}\n",
+                        c.name, c.name, c.max_version
+                    )
+                    .as_bytes(),
+                )?;
+                if let Some(pb) = &pb {
+                    pb.inc(1);
+                }
+            }
+        }
+        query.set_page(query.page() + 1);
+    }
+    Ok(())
+}
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index cfe845ec78f..d788c6359d7 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2023-02-25"
+channel = "nightly-2023-03-10"
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
diff --git a/src/tools/clippy/rustc_tools_util/README.md b/src/tools/clippy/rustc_tools_util/README.md
index eefc661f963..e197ea048a0 100644
--- a/src/tools/clippy/rustc_tools_util/README.md
+++ b/src/tools/clippy/rustc_tools_util/README.md
@@ -49,6 +49,8 @@ The changelog for `rustc_tools_util` is available under:
 
 ## License
 
+<!-- REUSE-IgnoreStart -->
+
 Copyright 2014-2022 The Rust Project Developers
 
 Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
@@ -56,3 +58,5 @@ http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 option. All files in the project carrying such notice may not be
 copied, modified, or distributed except according to those terms.
+
+<!-- REUSE-IgnoreEnd -->
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index dd183362f27..f08393c303e 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -176,7 +176,7 @@ Common options:
         --rustc              Pass all args to rustc
     -V, --version            Print version info and exit
 
-Other options are the same as `cargo check`.
+For the other options see `cargo check --help`.
 
 To allow or deny a lint from the command line you can use `cargo clippy --`
 with:
diff --git a/src/tools/clippy/src/main.rs b/src/tools/clippy/src/main.rs
index 82147eba33f..c5e9b96cf3f 100644
--- a/src/tools/clippy/src/main.rs
+++ b/src/tools/clippy/src/main.rs
@@ -18,7 +18,7 @@ Common options:
     -V, --version            Print version info and exit
     --explain LINT           Print the documentation for a given lint
 
-Other options are the same as `cargo check`.
+For the other options see `cargo check --help`.
 
 To allow or deny a lint from the command line you can use `cargo clippy --`
 with:
diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs
index 6d0022f7a5c..9643c2c9707 100644
--- a/src/tools/clippy/tests/dogfood.rs
+++ b/src/tools/clippy/tests/dogfood.rs
@@ -7,6 +7,7 @@
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
+use itertools::Itertools;
 use std::path::PathBuf;
 use std::process::Command;
 use test_utils::IS_RUSTC_TEST_SUITE;
@@ -19,8 +20,10 @@ fn dogfood_clippy() {
         return;
     }
 
+    let mut failed_packages = Vec::new();
+
     // "" is the root package
-    for package in &[
+    for package in [
         "",
         "clippy_dev",
         "clippy_lints",
@@ -28,8 +31,16 @@ fn dogfood_clippy() {
         "lintcheck",
         "rustc_tools_util",
     ] {
-        run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]);
+        if !run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]) {
+            failed_packages.push(if package.is_empty() { "root" } else { package });
+        }
     }
+
+    assert!(
+        !failed_packages.is_empty(),
+        "Dogfood failed for packages `{}`",
+        failed_packages.iter().format(", "),
+    )
 }
 
 #[test]
@@ -71,7 +82,7 @@ fn run_metadata_collection_lint() {
     run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]);
 }
 
-fn run_clippy_for_package(project: &str, args: &[&str]) {
+fn run_clippy_for_package(project: &str, args: &[&str]) -> bool {
     let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
 
     let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH);
@@ -107,5 +118,5 @@ fn run_clippy_for_package(project: &str, args: &[&str]) {
     println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
     println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
 
-    assert!(output.status.success());
+    output.status.success()
 }
diff --git a/tests/ui-toml/array_size_threshold/array_size_threshold.rs b/src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.rs
index 7f623c7a9ec..7f623c7a9ec 100644
--- a/tests/ui-toml/array_size_threshold/array_size_threshold.rs
+++ b/src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.rs
diff --git a/tests/ui-toml/array_size_threshold/array_size_threshold.stderr b/src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.stderr
index ac017b20916..ac017b20916 100644
--- a/tests/ui-toml/array_size_threshold/array_size_threshold.stderr
+++ b/src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.stderr
diff --git a/tests/ui-toml/array_size_threshold/clippy.toml b/src/tools/clippy/tests/ui-toml/array_size_threshold/clippy.toml
index 3f1fe9a1209..3f1fe9a1209 100644
--- a/tests/ui-toml/array_size_threshold/clippy.toml
+++ b/src/tools/clippy/tests/ui-toml/array_size_threshold/clippy.toml
diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
index 2611e3a785f..ee7d2ba444b 100644
--- a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
+++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
@@ -45,24 +45,32 @@ impl_arith!(
     Div, Custom, Custom, div;
     Mul, Custom, Custom, mul;
     Rem, Custom, Custom, rem;
+    Shl, Custom, Custom, shl;
+    Shr, Custom, Custom, shr;
     Sub, Custom, Custom, sub;
 
     Add, Custom, &Custom, add;
     Div, Custom, &Custom, div;
     Mul, Custom, &Custom, mul;
     Rem, Custom, &Custom, rem;
+    Shl, Custom, &Custom, shl;
+    Shr, Custom, &Custom, shr;
     Sub, Custom, &Custom, sub;
 
     Add, &Custom, Custom, add;
     Div, &Custom, Custom, div;
     Mul, &Custom, Custom, mul;
     Rem, &Custom, Custom, rem;
+    Shl, &Custom, Custom, shl;
+    Shr, &Custom, Custom, shr;
     Sub, &Custom, Custom, sub;
 
     Add, &Custom, &Custom, add;
     Div, &Custom, &Custom, div;
     Mul, &Custom, &Custom, mul;
     Rem, &Custom, &Custom, rem;
+    Shl, &Custom, &Custom, shl;
+    Shr, &Custom, &Custom, shr;
     Sub, &Custom, &Custom, sub;
 );
 
@@ -71,24 +79,32 @@ impl_assign_arith!(
     DivAssign, Custom, Custom, div_assign;
     MulAssign, Custom, Custom, mul_assign;
     RemAssign, Custom, Custom, rem_assign;
+    ShlAssign, Custom, Custom, shl_assign;
+    ShrAssign, Custom, Custom, shr_assign;
     SubAssign, Custom, Custom, sub_assign;
 
     AddAssign, Custom, &Custom, add_assign;
     DivAssign, Custom, &Custom, div_assign;
     MulAssign, Custom, &Custom, mul_assign;
     RemAssign, Custom, &Custom, rem_assign;
+    ShlAssign, Custom, &Custom, shl_assign;
+    ShrAssign, Custom, &Custom, shr_assign;
     SubAssign, Custom, &Custom, sub_assign;
 
     AddAssign, &Custom, Custom, add_assign;
     DivAssign, &Custom, Custom, div_assign;
     MulAssign, &Custom, Custom, mul_assign;
     RemAssign, &Custom, Custom, rem_assign;
+    ShlAssign, &Custom, Custom, shl_assign;
+    ShrAssign, &Custom, Custom, shr_assign;
     SubAssign, &Custom, Custom, sub_assign;
 
     AddAssign, &Custom, &Custom, add_assign;
     DivAssign, &Custom, &Custom, div_assign;
     MulAssign, &Custom, &Custom, mul_assign;
     RemAssign, &Custom, &Custom, rem_assign;
+    ShlAssign, &Custom, &Custom, shl_assign;
+    ShrAssign, &Custom, &Custom, shr_assign;
     SubAssign, &Custom, &Custom, sub_assign;
 );
 
@@ -297,6 +313,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() {
     _custom %= &Custom;
     _custom *= Custom;
     _custom *= &Custom;
+    _custom >>= Custom;
+    _custom >>= &Custom;
+    _custom <<= Custom;
+    _custom <<= &Custom;
     _custom += -Custom;
     _custom += &-Custom;
     _custom -= -Custom;
@@ -307,6 +327,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() {
     _custom %= &-Custom;
     _custom *= -Custom;
     _custom *= &-Custom;
+    _custom >>= -Custom;
+    _custom >>= &-Custom;
+    _custom <<= -Custom;
+    _custom <<= &-Custom;
 
     // Binary
     _n = _n + 1;
@@ -347,6 +371,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() {
     _custom = Custom + &Custom;
     _custom = &Custom + Custom;
     _custom = &Custom + &Custom;
+    _custom = _custom >> _custom;
+    _custom = _custom >> &_custom;
+    _custom = Custom << _custom;
+    _custom = &Custom << _custom;
 
     // Unary
     _n = -_n;
diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
index 17a2448fbfc..3895f08964c 100644
--- a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
+++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
@@ -1,5 +1,5 @@
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:270:5
+  --> $DIR/arithmetic_side_effects.rs:286:5
    |
 LL |     _n += 1;
    |     ^^^^^^^
@@ -7,592 +7,640 @@ LL |     _n += 1;
    = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings`
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:271:5
+  --> $DIR/arithmetic_side_effects.rs:287:5
    |
 LL |     _n += &1;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:272:5
+  --> $DIR/arithmetic_side_effects.rs:288:5
    |
 LL |     _n -= 1;
    |     ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:273:5
+  --> $DIR/arithmetic_side_effects.rs:289:5
    |
 LL |     _n -= &1;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:274:5
+  --> $DIR/arithmetic_side_effects.rs:290:5
    |
 LL |     _n /= 0;
    |     ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:275:5
+  --> $DIR/arithmetic_side_effects.rs:291:5
    |
 LL |     _n /= &0;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:276:5
+  --> $DIR/arithmetic_side_effects.rs:292:5
    |
 LL |     _n %= 0;
    |     ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:277:5
+  --> $DIR/arithmetic_side_effects.rs:293:5
    |
 LL |     _n %= &0;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:278:5
+  --> $DIR/arithmetic_side_effects.rs:294:5
    |
 LL |     _n *= 2;
    |     ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:279:5
+  --> $DIR/arithmetic_side_effects.rs:295:5
    |
 LL |     _n *= &2;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:280:5
+  --> $DIR/arithmetic_side_effects.rs:296:5
    |
 LL |     _n += -1;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:281:5
+  --> $DIR/arithmetic_side_effects.rs:297:5
    |
 LL |     _n += &-1;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:282:5
+  --> $DIR/arithmetic_side_effects.rs:298:5
    |
 LL |     _n -= -1;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:283:5
+  --> $DIR/arithmetic_side_effects.rs:299:5
    |
 LL |     _n -= &-1;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:284:5
+  --> $DIR/arithmetic_side_effects.rs:300:5
    |
 LL |     _n /= -0;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:285:5
+  --> $DIR/arithmetic_side_effects.rs:301:5
    |
 LL |     _n /= &-0;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:286:5
+  --> $DIR/arithmetic_side_effects.rs:302:5
    |
 LL |     _n %= -0;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:287:5
+  --> $DIR/arithmetic_side_effects.rs:303:5
    |
 LL |     _n %= &-0;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:288:5
+  --> $DIR/arithmetic_side_effects.rs:304:5
    |
 LL |     _n *= -2;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:289:5
+  --> $DIR/arithmetic_side_effects.rs:305:5
    |
 LL |     _n *= &-2;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:290:5
+  --> $DIR/arithmetic_side_effects.rs:306:5
    |
 LL |     _custom += Custom;
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:291:5
+  --> $DIR/arithmetic_side_effects.rs:307:5
    |
 LL |     _custom += &Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:292:5
+  --> $DIR/arithmetic_side_effects.rs:308:5
    |
 LL |     _custom -= Custom;
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:293:5
+  --> $DIR/arithmetic_side_effects.rs:309:5
    |
 LL |     _custom -= &Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:294:5
+  --> $DIR/arithmetic_side_effects.rs:310:5
    |
 LL |     _custom /= Custom;
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:295:5
+  --> $DIR/arithmetic_side_effects.rs:311:5
    |
 LL |     _custom /= &Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:296:5
+  --> $DIR/arithmetic_side_effects.rs:312:5
    |
 LL |     _custom %= Custom;
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:297:5
+  --> $DIR/arithmetic_side_effects.rs:313:5
    |
 LL |     _custom %= &Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:298:5
+  --> $DIR/arithmetic_side_effects.rs:314:5
    |
 LL |     _custom *= Custom;
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:299:5
+  --> $DIR/arithmetic_side_effects.rs:315:5
    |
 LL |     _custom *= &Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:300:5
+  --> $DIR/arithmetic_side_effects.rs:316:5
+   |
+LL |     _custom >>= Custom;
+   |     ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:317:5
+   |
+LL |     _custom >>= &Custom;
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:318:5
+   |
+LL |     _custom <<= Custom;
+   |     ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:319:5
+   |
+LL |     _custom <<= &Custom;
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:320:5
    |
 LL |     _custom += -Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:301:5
+  --> $DIR/arithmetic_side_effects.rs:321:5
    |
 LL |     _custom += &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:302:5
+  --> $DIR/arithmetic_side_effects.rs:322:5
    |
 LL |     _custom -= -Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:303:5
+  --> $DIR/arithmetic_side_effects.rs:323:5
    |
 LL |     _custom -= &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:304:5
+  --> $DIR/arithmetic_side_effects.rs:324:5
    |
 LL |     _custom /= -Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:305:5
+  --> $DIR/arithmetic_side_effects.rs:325:5
    |
 LL |     _custom /= &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:306:5
+  --> $DIR/arithmetic_side_effects.rs:326:5
    |
 LL |     _custom %= -Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:307:5
+  --> $DIR/arithmetic_side_effects.rs:327:5
    |
 LL |     _custom %= &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:308:5
+  --> $DIR/arithmetic_side_effects.rs:328:5
    |
 LL |     _custom *= -Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:309:5
+  --> $DIR/arithmetic_side_effects.rs:329:5
    |
 LL |     _custom *= &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:312:10
+  --> $DIR/arithmetic_side_effects.rs:330:5
+   |
+LL |     _custom >>= -Custom;
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:331:5
+   |
+LL |     _custom >>= &-Custom;
+   |     ^^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:332:5
+   |
+LL |     _custom <<= -Custom;
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:333:5
+   |
+LL |     _custom <<= &-Custom;
+   |     ^^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:336:10
    |
 LL |     _n = _n + 1;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:313:10
+  --> $DIR/arithmetic_side_effects.rs:337:10
    |
 LL |     _n = _n + &1;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:314:10
+  --> $DIR/arithmetic_side_effects.rs:338:10
    |
 LL |     _n = 1 + _n;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:315:10
+  --> $DIR/arithmetic_side_effects.rs:339:10
    |
 LL |     _n = &1 + _n;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:316:10
+  --> $DIR/arithmetic_side_effects.rs:340:10
    |
 LL |     _n = _n - 1;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:317:10
+  --> $DIR/arithmetic_side_effects.rs:341:10
    |
 LL |     _n = _n - &1;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:318:10
+  --> $DIR/arithmetic_side_effects.rs:342:10
    |
 LL |     _n = 1 - _n;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:319:10
+  --> $DIR/arithmetic_side_effects.rs:343:10
    |
 LL |     _n = &1 - _n;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:320:10
+  --> $DIR/arithmetic_side_effects.rs:344:10
    |
 LL |     _n = _n / 0;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:321:10
+  --> $DIR/arithmetic_side_effects.rs:345:10
    |
 LL |     _n = _n / &0;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:322:10
+  --> $DIR/arithmetic_side_effects.rs:346:10
    |
 LL |     _n = _n % 0;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:323:10
+  --> $DIR/arithmetic_side_effects.rs:347:10
    |
 LL |     _n = _n % &0;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:324:10
+  --> $DIR/arithmetic_side_effects.rs:348:10
    |
 LL |     _n = _n * 2;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:325:10
+  --> $DIR/arithmetic_side_effects.rs:349:10
    |
 LL |     _n = _n * &2;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:326:10
+  --> $DIR/arithmetic_side_effects.rs:350:10
    |
 LL |     _n = 2 * _n;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:327:10
+  --> $DIR/arithmetic_side_effects.rs:351:10
    |
 LL |     _n = &2 * _n;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:328:10
+  --> $DIR/arithmetic_side_effects.rs:352:10
    |
 LL |     _n = 23 + &85;
    |          ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:329:10
+  --> $DIR/arithmetic_side_effects.rs:353:10
    |
 LL |     _n = &23 + 85;
    |          ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:330:10
+  --> $DIR/arithmetic_side_effects.rs:354:10
    |
 LL |     _n = &23 + &85;
    |          ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:331:15
+  --> $DIR/arithmetic_side_effects.rs:355:15
    |
 LL |     _custom = _custom + _custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:332:15
+  --> $DIR/arithmetic_side_effects.rs:356:15
    |
 LL |     _custom = _custom + &_custom;
    |               ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:333:15
+  --> $DIR/arithmetic_side_effects.rs:357:15
    |
 LL |     _custom = Custom + _custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:334:15
+  --> $DIR/arithmetic_side_effects.rs:358:15
    |
 LL |     _custom = &Custom + _custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:335:15
+  --> $DIR/arithmetic_side_effects.rs:359:15
    |
 LL |     _custom = _custom - Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:336:15
+  --> $DIR/arithmetic_side_effects.rs:360:15
    |
 LL |     _custom = _custom - &Custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:337:15
+  --> $DIR/arithmetic_side_effects.rs:361:15
    |
 LL |     _custom = Custom - _custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:338:15
+  --> $DIR/arithmetic_side_effects.rs:362:15
    |
 LL |     _custom = &Custom - _custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:339:15
+  --> $DIR/arithmetic_side_effects.rs:363:15
    |
 LL |     _custom = _custom / Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:340:15
+  --> $DIR/arithmetic_side_effects.rs:364:15
    |
 LL |     _custom = _custom / &Custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:341:15
+  --> $DIR/arithmetic_side_effects.rs:365:15
    |
 LL |     _custom = _custom % Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:342:15
+  --> $DIR/arithmetic_side_effects.rs:366:15
    |
 LL |     _custom = _custom % &Custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:343:15
+  --> $DIR/arithmetic_side_effects.rs:367:15
    |
 LL |     _custom = _custom * Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:344:15
+  --> $DIR/arithmetic_side_effects.rs:368:15
    |
 LL |     _custom = _custom * &Custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:345:15
+  --> $DIR/arithmetic_side_effects.rs:369:15
    |
 LL |     _custom = Custom * _custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:346:15
+  --> $DIR/arithmetic_side_effects.rs:370:15
    |
 LL |     _custom = &Custom * _custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:347:15
+  --> $DIR/arithmetic_side_effects.rs:371:15
    |
 LL |     _custom = Custom + &Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:348:15
+  --> $DIR/arithmetic_side_effects.rs:372:15
    |
 LL |     _custom = &Custom + Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:349:15
+  --> $DIR/arithmetic_side_effects.rs:373:15
    |
 LL |     _custom = &Custom + &Custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:352:10
+  --> $DIR/arithmetic_side_effects.rs:374:15
+   |
+LL |     _custom = _custom >> _custom;
+   |               ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:375:15
+   |
+LL |     _custom = _custom >> &_custom;
+   |               ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:376:15
+   |
+LL |     _custom = Custom << _custom;
+   |               ^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:377:15
+   |
+LL |     _custom = &Custom << _custom;
+   |               ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:380:10
    |
 LL |     _n = -_n;
    |          ^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:353:10
+  --> $DIR/arithmetic_side_effects.rs:381:10
    |
 LL |     _n = -&_n;
    |          ^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:354:15
+  --> $DIR/arithmetic_side_effects.rs:382:15
    |
 LL |     _custom = -_custom;
    |               ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:355:15
+  --> $DIR/arithmetic_side_effects.rs:383:15
    |
 LL |     _custom = -&_custom;
    |               ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:364:5
+  --> $DIR/arithmetic_side_effects.rs:392:5
    |
 LL |     1 + i;
    |     ^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:365:5
+  --> $DIR/arithmetic_side_effects.rs:393:5
    |
 LL |     i * 2;
    |     ^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:367:5
+  --> $DIR/arithmetic_side_effects.rs:395:5
    |
 LL |     i - 2 + 2 - i;
    |     ^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:368:5
+  --> $DIR/arithmetic_side_effects.rs:396:5
    |
 LL |     -i;
    |     ^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:369:5
-   |
-LL |     i >> 1;
-   |     ^^^^^^
-
-error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:370:5
-   |
-LL |     i << 1;
-   |     ^^^^^^
-
-error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:379:5
+  --> $DIR/arithmetic_side_effects.rs:407:5
    |
 LL |     i += 1;
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:380:5
+  --> $DIR/arithmetic_side_effects.rs:408:5
    |
 LL |     i -= 1;
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:381:5
+  --> $DIR/arithmetic_side_effects.rs:409:5
    |
 LL |     i *= 2;
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:383:5
+  --> $DIR/arithmetic_side_effects.rs:411:5
    |
 LL |     i /= 0;
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:385:5
+  --> $DIR/arithmetic_side_effects.rs:413:5
    |
 LL |     i /= var1;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:386:5
+  --> $DIR/arithmetic_side_effects.rs:414:5
    |
 LL |     i /= var2;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:388:5
+  --> $DIR/arithmetic_side_effects.rs:416:5
    |
 LL |     i %= 0;
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:390:5
+  --> $DIR/arithmetic_side_effects.rs:418:5
    |
 LL |     i %= var1;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:391:5
+  --> $DIR/arithmetic_side_effects.rs:419:5
    |
 LL |     i %= var2;
    |     ^^^^^^^^^
 
-error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:392:5
-   |
-LL |     i <<= 3;
-   |     ^^^^^^^
-
-error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:393:5
-   |
-LL |     i >>= 2;
-   |     ^^^^^^^
-
-error: aborting due to 99 previous errors
+error: aborting due to 107 previous errors
 
diff --git a/src/tools/clippy/tests/ui/async_yields_async.fixed b/src/tools/clippy/tests/ui/async_yields_async.fixed
index 3cf380d2b95..579a63ea477 100644
--- a/src/tools/clippy/tests/ui/async_yields_async.fixed
+++ b/src/tools/clippy/tests/ui/async_yields_async.fixed
@@ -2,6 +2,7 @@
 #![feature(lint_reasons)]
 #![feature(async_closure)]
 #![warn(clippy::async_yields_async)]
+#![allow(clippy::redundant_async_block)]
 
 use core::future::Future;
 use core::pin::Pin;
diff --git a/src/tools/clippy/tests/ui/async_yields_async.rs b/src/tools/clippy/tests/ui/async_yields_async.rs
index dd4131b60ab..5aec2fb50f6 100644
--- a/src/tools/clippy/tests/ui/async_yields_async.rs
+++ b/src/tools/clippy/tests/ui/async_yields_async.rs
@@ -2,6 +2,7 @@
 #![feature(lint_reasons)]
 #![feature(async_closure)]
 #![warn(clippy::async_yields_async)]
+#![allow(clippy::redundant_async_block)]
 
 use core::future::Future;
 use core::pin::Pin;
diff --git a/src/tools/clippy/tests/ui/async_yields_async.stderr b/src/tools/clippy/tests/ui/async_yields_async.stderr
index 22ce1c6f647..7f72534832b 100644
--- a/src/tools/clippy/tests/ui/async_yields_async.stderr
+++ b/src/tools/clippy/tests/ui/async_yields_async.stderr
@@ -1,5 +1,5 @@
 error: an async construct yields a type which is itself awaitable
-  --> $DIR/async_yields_async.rs:39:9
+  --> $DIR/async_yields_async.rs:40:9
    |
 LL |        let _h = async {
    |  _____________________-
@@ -19,7 +19,7 @@ LL +         }.await
    |
 
 error: an async construct yields a type which is itself awaitable
-  --> $DIR/async_yields_async.rs:44:9
+  --> $DIR/async_yields_async.rs:45:9
    |
 LL |       let _i = async {
    |  ____________________-
@@ -32,7 +32,7 @@ LL | |     };
    | |_____- outer async construct
 
 error: an async construct yields a type which is itself awaitable
-  --> $DIR/async_yields_async.rs:50:9
+  --> $DIR/async_yields_async.rs:51:9
    |
 LL |        let _j = async || {
    |  ________________________-
@@ -51,7 +51,7 @@ LL +         }.await
    |
 
 error: an async construct yields a type which is itself awaitable
-  --> $DIR/async_yields_async.rs:55:9
+  --> $DIR/async_yields_async.rs:56:9
    |
 LL |       let _k = async || {
    |  _______________________-
@@ -64,7 +64,7 @@ LL | |     };
    | |_____- outer async construct
 
 error: an async construct yields a type which is itself awaitable
-  --> $DIR/async_yields_async.rs:57:23
+  --> $DIR/async_yields_async.rs:58:23
    |
 LL |     let _l = async || CustomFutureType;
    |                       ^^^^^^^^^^^^^^^^
@@ -74,7 +74,7 @@ LL |     let _l = async || CustomFutureType;
    |                       help: consider awaiting this value: `CustomFutureType.await`
 
 error: an async construct yields a type which is itself awaitable
-  --> $DIR/async_yields_async.rs:63:9
+  --> $DIR/async_yields_async.rs:64:9
    |
 LL |       let _m = async || {
    |  _______________________-
diff --git a/src/tools/clippy/tests/ui/collection_is_never_read.rs b/src/tools/clippy/tests/ui/collection_is_never_read.rs
new file mode 100644
index 00000000000..068a49486cf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/collection_is_never_read.rs
@@ -0,0 +1,165 @@
+#![allow(unused)]
+#![warn(clippy::collection_is_never_read)]
+
+use std::collections::{HashMap, HashSet};
+
+fn main() {}
+
+fn not_a_collection() {
+    // TODO: Expand `collection_is_never_read` beyond collections?
+    let mut x = 10; // Ok
+    x += 1;
+}
+
+fn no_access_at_all() {
+    // Other lints should catch this.
+    let x = vec![1, 2, 3]; // Ok
+}
+
+fn write_without_read() {
+    // The main use case for `collection_is_never_read`.
+    let mut x = HashMap::new(); // WARNING
+    x.insert(1, 2);
+}
+
+fn read_without_write() {
+    let mut x = vec![1, 2, 3]; // Ok
+    let _ = x.len();
+}
+
+fn write_and_read() {
+    let mut x = vec![1, 2, 3]; // Ok
+    x.push(4);
+    let _ = x.len();
+}
+
+fn write_after_read() {
+    // TODO: Warn here, but this requires more extensive data flow analysis.
+    let mut x = vec![1, 2, 3]; // Ok
+    let _ = x.len();
+    x.push(4); // Pointless
+}
+
+fn write_before_reassign() {
+    // TODO: Warn here, but this requires more extensive data flow analysis.
+    let mut x = HashMap::new(); // Ok
+    x.insert(1, 2); // Pointless
+    x = HashMap::new();
+    let _ = x.len();
+}
+
+fn read_in_closure() {
+    let mut x = HashMap::new(); // Ok
+    x.insert(1, 2);
+    let _ = || {
+        let _ = x.len();
+    };
+}
+
+fn write_in_closure() {
+    let mut x = vec![1, 2, 3]; // WARNING
+    let _ = || {
+        x.push(4);
+    };
+}
+
+fn read_in_format() {
+    let mut x = HashMap::new(); // Ok
+    x.insert(1, 2);
+    format!("{x:?}");
+}
+
+fn shadowing_1() {
+    let x = HashMap::<usize, usize>::new(); // Ok
+    let _ = x.len();
+    let mut x = HashMap::new(); // WARNING
+    x.insert(1, 2);
+}
+
+fn shadowing_2() {
+    let mut x = HashMap::new(); // WARNING
+    x.insert(1, 2);
+    let x = HashMap::<usize, usize>::new(); // Ok
+    let _ = x.len();
+}
+
+#[allow(clippy::let_unit_value)]
+fn fake_read() {
+    let mut x = vec![1, 2, 3]; // Ok
+    x.reverse();
+    // `collection_is_never_read` gets fooled, but other lints should catch this.
+    let _: () = x.clear();
+}
+
+fn assignment() {
+    let mut x = vec![1, 2, 3]; // WARNING
+    let y = vec![4, 5, 6]; // Ok
+    x = y;
+}
+
+#[allow(clippy::self_assignment)]
+fn self_assignment() {
+    let mut x = vec![1, 2, 3]; // WARNING
+    x = x;
+}
+
+fn method_argument_but_not_target() {
+    struct MyStruct;
+    impl MyStruct {
+        fn my_method(&self, _argument: &[usize]) {}
+    }
+    let my_struct = MyStruct;
+
+    let mut x = vec![1, 2, 3]; // Ok
+    x.reverse();
+    my_struct.my_method(&x);
+}
+
+fn insert_is_not_a_read() {
+    let mut x = HashSet::new(); // WARNING
+    x.insert(5);
+}
+
+fn insert_is_a_read() {
+    let mut x = HashSet::new(); // Ok
+    if x.insert(5) {
+        println!("5 was inserted");
+    }
+}
+
+fn not_read_if_return_value_not_used() {
+    // `is_empty` does not modify the set, so it's a query. But since the return value is not used, the
+    // lint does not consider it a read here.
+    let x = vec![1, 2, 3]; // WARNING
+    x.is_empty();
+}
+
+fn extension_traits() {
+    trait VecExt<T> {
+        fn method_with_side_effect(&self);
+        fn method_without_side_effect(&self);
+    }
+
+    impl<T> VecExt<T> for Vec<T> {
+        fn method_with_side_effect(&self) {
+            println!("my length: {}", self.len());
+        }
+        fn method_without_side_effect(&self) {}
+    }
+
+    let x = vec![1, 2, 3]; // Ok
+    x.method_with_side_effect();
+
+    let y = vec![1, 2, 3]; // Ok (false negative)
+    y.method_without_side_effect();
+}
+
+fn function_argument() {
+    #[allow(clippy::ptr_arg)]
+    fn foo<T>(v: &Vec<T>) -> usize {
+        v.len()
+    }
+
+    let x = vec![1, 2, 3]; // Ok
+    foo(&x);
+}
diff --git a/src/tools/clippy/tests/ui/collection_is_never_read.stderr b/src/tools/clippy/tests/ui/collection_is_never_read.stderr
new file mode 100644
index 00000000000..7654b74be3d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/collection_is_never_read.stderr
@@ -0,0 +1,52 @@
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:21:5
+   |
+LL |     let mut x = HashMap::new(); // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::collection-is-never-read` implied by `-D warnings`
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:60:5
+   |
+LL |     let mut x = vec![1, 2, 3]; // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:75:5
+   |
+LL |     let mut x = HashMap::new(); // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:80:5
+   |
+LL |     let mut x = HashMap::new(); // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:95:5
+   |
+LL |     let mut x = vec![1, 2, 3]; // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:102:5
+   |
+LL |     let mut x = vec![1, 2, 3]; // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:119:5
+   |
+LL |     let mut x = HashSet::new(); // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:133:5
+   |
+LL |     let x = vec![1, 2, 3]; // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-10148.rs b/src/tools/clippy/tests/ui/crashes/ice-10148.rs
new file mode 100644
index 00000000000..af33b10c693
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-10148.rs
@@ -0,0 +1,9 @@
+// aux-build:../../auxiliary/proc_macro_with_span.rs
+
+extern crate proc_macro_with_span;
+
+use proc_macro_with_span::with_span;
+
+fn main() {
+    println!(with_span!(""something ""));
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-10148.stderr b/src/tools/clippy/tests/ui/crashes/ice-10148.stderr
new file mode 100644
index 00000000000..f23e4433f95
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-10148.stderr
@@ -0,0 +1,12 @@
+error: empty string literal in `println!`
+  --> $DIR/ice-10148.rs:8:5
+   |
+LL |     println!(with_span!(""something ""));
+   |     ^^^^^^^^^^^^^^^^^^^^-----------^^^^^
+   |                         |
+   |                         help: remove the empty string
+   |
+   = note: `-D clippy::println-empty-string` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6179.rs b/src/tools/clippy/tests/ui/crashes/ice-6179.rs
index 4fe92d356c4..ce1895851e2 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-6179.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-6179.rs
@@ -2,7 +2,7 @@
 //! The ICE is mainly caused by using `hir_ty_to_ty`. See the discussion in the PR for details.
 
 #![warn(clippy::use_self)]
-#![allow(dead_code)]
+#![allow(dead_code, clippy::let_with_type_underscore)]
 
 struct Foo;
 
diff --git a/src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs b/src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs
new file mode 100644
index 00000000000..7f5bae60d55
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs
@@ -0,0 +1,17 @@
+#![allow(dead_code)]
+
+struct Foo;
+
+impl<'a> std::convert::TryFrom<&'a String> for Foo {
+    type Error = std::convert::Infallible;
+
+    fn try_from(_: &'a String) -> Result<Self, Self::Error> {
+        Ok(Foo)
+    }
+}
+
+fn find<E>(_: impl std::convert::TryInto<Foo, Error = E>) {}
+
+fn main() {
+    find(&String::new());
+}
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed
index a370ccc7696..a9e5fd159af 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed
@@ -9,7 +9,8 @@
     clippy::unnecessary_operation,
     clippy::branches_sharing_code,
     clippy::match_single_binding,
-    clippy::let_unit_value
+    clippy::let_unit_value,
+    clippy::let_with_type_underscore
 )]
 
 #[macro_use]
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs
index 2476fe95141..085f8f452b2 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs
@@ -9,7 +9,8 @@
     clippy::unnecessary_operation,
     clippy::branches_sharing_code,
     clippy::match_single_binding,
-    clippy::let_unit_value
+    clippy::let_unit_value,
+    clippy::let_with_type_underscore
 )]
 
 #[macro_use]
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr
index 5df2f642388..44c6f1a9bea 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr
@@ -1,5 +1,5 @@
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:21:17
+  --> $DIR/default_numeric_fallback_f64.rs:22:17
    |
 LL |         let x = 0.12;
    |                 ^^^^ help: consider adding suffix: `0.12_f64`
@@ -7,139 +7,139 @@ LL |         let x = 0.12;
    = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:22:18
+  --> $DIR/default_numeric_fallback_f64.rs:23:18
    |
 LL |         let x = [1., 2., 3.];
    |                  ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:22:22
+  --> $DIR/default_numeric_fallback_f64.rs:23:22
    |
 LL |         let x = [1., 2., 3.];
    |                      ^^ help: consider adding suffix: `2.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:22:26
+  --> $DIR/default_numeric_fallback_f64.rs:23:26
    |
 LL |         let x = [1., 2., 3.];
    |                          ^^ help: consider adding suffix: `3.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:23:28
+  --> $DIR/default_numeric_fallback_f64.rs:24:28
    |
 LL |         let x = if true { (1., 2.) } else { (3., 4.) };
    |                            ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:23:32
+  --> $DIR/default_numeric_fallback_f64.rs:24:32
    |
 LL |         let x = if true { (1., 2.) } else { (3., 4.) };
    |                                ^^ help: consider adding suffix: `2.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:23:46
+  --> $DIR/default_numeric_fallback_f64.rs:24:46
    |
 LL |         let x = if true { (1., 2.) } else { (3., 4.) };
    |                                              ^^ help: consider adding suffix: `3.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:23:50
+  --> $DIR/default_numeric_fallback_f64.rs:24:50
    |
 LL |         let x = if true { (1., 2.) } else { (3., 4.) };
    |                                                  ^^ help: consider adding suffix: `4.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:24:23
+  --> $DIR/default_numeric_fallback_f64.rs:25:23
    |
 LL |         let x = match 1. {
    |                       ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:25:18
+  --> $DIR/default_numeric_fallback_f64.rs:26:18
    |
 LL |             _ => 1.,
    |                  ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:44:21
+  --> $DIR/default_numeric_fallback_f64.rs:45:21
    |
 LL |             let y = 1.;
    |                     ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:52:21
+  --> $DIR/default_numeric_fallback_f64.rs:53:21
    |
 LL |             let y = 1.;
    |                     ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:58:21
+  --> $DIR/default_numeric_fallback_f64.rs:59:21
    |
 LL |             let y = 1.;
    |                     ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:66:21
+  --> $DIR/default_numeric_fallback_f64.rs:67:21
    |
 LL |             let y = 1.;
    |                     ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:78:9
+  --> $DIR/default_numeric_fallback_f64.rs:79:9
    |
 LL |         1.
    |         ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:84:27
+  --> $DIR/default_numeric_fallback_f64.rs:85:27
    |
 LL |         let f = || -> _ { 1. };
    |                           ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:88:29
+  --> $DIR/default_numeric_fallback_f64.rs:89:29
    |
 LL |         let f = || -> f64 { 1. };
    |                             ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:102:21
+  --> $DIR/default_numeric_fallback_f64.rs:103:21
    |
 LL |         generic_arg(1.);
    |                     ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:105:32
+  --> $DIR/default_numeric_fallback_f64.rs:106:32
    |
 LL |         let x: _ = generic_arg(1.);
    |                                ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:123:28
+  --> $DIR/default_numeric_fallback_f64.rs:124:28
    |
 LL |         GenericStruct { x: 1. };
    |                            ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:126:36
+  --> $DIR/default_numeric_fallback_f64.rs:127:36
    |
 LL |         let _ = GenericStruct { x: 1. };
    |                                    ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:144:24
+  --> $DIR/default_numeric_fallback_f64.rs:145:24
    |
 LL |         GenericEnum::X(1.);
    |                        ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:164:23
+  --> $DIR/default_numeric_fallback_f64.rs:165:23
    |
 LL |         s.generic_arg(1.);
    |                       ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:171:21
+  --> $DIR/default_numeric_fallback_f64.rs:172:21
    |
 LL |             let x = 22.;
    |                     ^^^ help: consider adding suffix: `22.0_f64`
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed
index 3f4994f0453..63ac4d5aeb6 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed
@@ -9,7 +9,8 @@
     clippy::no_effect,
     clippy::unnecessary_operation,
     clippy::branches_sharing_code,
-    clippy::let_unit_value
+    clippy::let_unit_value,
+    clippy::let_with_type_underscore
 )]
 
 #[macro_use]
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs
index 2df0e09787f..28e6eceb80e 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs
@@ -9,7 +9,8 @@
     clippy::no_effect,
     clippy::unnecessary_operation,
     clippy::branches_sharing_code,
-    clippy::let_unit_value
+    clippy::let_unit_value,
+    clippy::let_with_type_underscore
 )]
 
 #[macro_use]
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr
index 6f219c3fc2b..dd91574d5b3 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr
@@ -1,5 +1,5 @@
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:21:17
+  --> $DIR/default_numeric_fallback_i32.rs:22:17
    |
 LL |         let x = 22;
    |                 ^^ help: consider adding suffix: `22_i32`
@@ -7,151 +7,151 @@ LL |         let x = 22;
    = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:22:18
+  --> $DIR/default_numeric_fallback_i32.rs:23:18
    |
 LL |         let x = [1, 2, 3];
    |                  ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:22:21
+  --> $DIR/default_numeric_fallback_i32.rs:23:21
    |
 LL |         let x = [1, 2, 3];
    |                     ^ help: consider adding suffix: `2_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:22:24
+  --> $DIR/default_numeric_fallback_i32.rs:23:24
    |
 LL |         let x = [1, 2, 3];
    |                        ^ help: consider adding suffix: `3_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:23:28
+  --> $DIR/default_numeric_fallback_i32.rs:24:28
    |
 LL |         let x = if true { (1, 2) } else { (3, 4) };
    |                            ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:23:31
+  --> $DIR/default_numeric_fallback_i32.rs:24:31
    |
 LL |         let x = if true { (1, 2) } else { (3, 4) };
    |                               ^ help: consider adding suffix: `2_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:23:44
+  --> $DIR/default_numeric_fallback_i32.rs:24:44
    |
 LL |         let x = if true { (1, 2) } else { (3, 4) };
    |                                            ^ help: consider adding suffix: `3_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:23:47
+  --> $DIR/default_numeric_fallback_i32.rs:24:47
    |
 LL |         let x = if true { (1, 2) } else { (3, 4) };
    |                                               ^ help: consider adding suffix: `4_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:24:23
+  --> $DIR/default_numeric_fallback_i32.rs:25:23
    |
 LL |         let x = match 1 {
    |                       ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:25:13
+  --> $DIR/default_numeric_fallback_i32.rs:26:13
    |
 LL |             1 => 1,
    |             ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:25:18
+  --> $DIR/default_numeric_fallback_i32.rs:26:18
    |
 LL |             1 => 1,
    |                  ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:26:18
+  --> $DIR/default_numeric_fallback_i32.rs:27:18
    |
 LL |             _ => 2,
    |                  ^ help: consider adding suffix: `2_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:45:21
+  --> $DIR/default_numeric_fallback_i32.rs:46:21
    |
 LL |             let y = 1;
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:53:21
+  --> $DIR/default_numeric_fallback_i32.rs:54:21
    |
 LL |             let y = 1;
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:59:21
+  --> $DIR/default_numeric_fallback_i32.rs:60:21
    |
 LL |             let y = 1;
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:67:21
+  --> $DIR/default_numeric_fallback_i32.rs:68:21
    |
 LL |             let y = 1;
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:79:9
+  --> $DIR/default_numeric_fallback_i32.rs:80:9
    |
 LL |         1
    |         ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:85:27
+  --> $DIR/default_numeric_fallback_i32.rs:86:27
    |
 LL |         let f = || -> _ { 1 };
    |                           ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:89:29
+  --> $DIR/default_numeric_fallback_i32.rs:90:29
    |
 LL |         let f = || -> i32 { 1 };
    |                             ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:103:21
+  --> $DIR/default_numeric_fallback_i32.rs:104:21
    |
 LL |         generic_arg(1);
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:106:32
+  --> $DIR/default_numeric_fallback_i32.rs:107:32
    |
 LL |         let x: _ = generic_arg(1);
    |                                ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:124:28
+  --> $DIR/default_numeric_fallback_i32.rs:125:28
    |
 LL |         GenericStruct { x: 1 };
    |                            ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:127:36
+  --> $DIR/default_numeric_fallback_i32.rs:128:36
    |
 LL |         let _ = GenericStruct { x: 1 };
    |                                    ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:145:24
+  --> $DIR/default_numeric_fallback_i32.rs:146:24
    |
 LL |         GenericEnum::X(1);
    |                        ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:165:23
+  --> $DIR/default_numeric_fallback_i32.rs:166:23
    |
 LL |         s.generic_arg(1);
    |                       ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:172:21
+  --> $DIR/default_numeric_fallback_i32.rs:173:21
    |
 LL |             let x = 22;
    |                     ^^ help: consider adding suffix: `22_i32`
diff --git a/src/tools/clippy/tests/ui/derivable_impls.fixed b/src/tools/clippy/tests/ui/derivable_impls.fixed
index ee8456f5deb..89ec33a0d8f 100644
--- a/src/tools/clippy/tests/ui/derivable_impls.fixed
+++ b/src/tools/clippy/tests/ui/derivable_impls.fixed
@@ -231,4 +231,41 @@ impl Default for NonExhaustiveEnum {
     }
 }
 
+// https://github.com/rust-lang/rust-clippy/issues/10396
+
+#[derive(Default)]
+struct DefaultType;
+
+struct GenericType<T = DefaultType> {
+    t: T,
+}
+
+impl Default for GenericType {
+    fn default() -> Self {
+        Self { t: Default::default() }
+    }
+}
+
+struct InnerGenericType<T> {
+    t: T,
+}
+
+impl Default for InnerGenericType<DefaultType> {
+    fn default() -> Self {
+        Self { t: Default::default() }
+    }
+}
+
+struct OtherGenericType<T = DefaultType> {
+    inner: InnerGenericType<T>,
+}
+
+impl Default for OtherGenericType {
+    fn default() -> Self {
+        Self {
+            inner: Default::default(),
+        }
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/derivable_impls.rs b/src/tools/clippy/tests/ui/derivable_impls.rs
index 14af419bcad..def6e41162f 100644
--- a/src/tools/clippy/tests/ui/derivable_impls.rs
+++ b/src/tools/clippy/tests/ui/derivable_impls.rs
@@ -267,4 +267,41 @@ impl Default for NonExhaustiveEnum {
     }
 }
 
+// https://github.com/rust-lang/rust-clippy/issues/10396
+
+#[derive(Default)]
+struct DefaultType;
+
+struct GenericType<T = DefaultType> {
+    t: T,
+}
+
+impl Default for GenericType {
+    fn default() -> Self {
+        Self { t: Default::default() }
+    }
+}
+
+struct InnerGenericType<T> {
+    t: T,
+}
+
+impl Default for InnerGenericType<DefaultType> {
+    fn default() -> Self {
+        Self { t: Default::default() }
+    }
+}
+
+struct OtherGenericType<T = DefaultType> {
+    inner: InnerGenericType<T>,
+}
+
+impl Default for OtherGenericType {
+    fn default() -> Self {
+        Self {
+            inner: Default::default(),
+        }
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/format.fixed b/src/tools/clippy/tests/ui/format.fixed
index cd2f70ee8b0..beedf2c1db2 100644
--- a/src/tools/clippy/tests/ui/format.fixed
+++ b/src/tools/clippy/tests/ui/format.fixed
@@ -1,5 +1,4 @@
 // run-rustfix
-// aux-build: proc_macro_with_span.rs
 #![warn(clippy::useless_format)]
 #![allow(
     unused_tuple_struct_fields,
@@ -10,8 +9,6 @@
     clippy::uninlined_format_args
 )]
 
-extern crate proc_macro_with_span;
-
 struct Foo(pub String);
 
 macro_rules! foo {
@@ -90,7 +87,4 @@ fn main() {
     let _ = abc.to_string();
     let xx = "xx";
     let _ = xx.to_string();
-
-    // Issue #10148
-    println!(proc_macro_with_span::with_span!(""something ""));
 }
diff --git a/src/tools/clippy/tests/ui/format.rs b/src/tools/clippy/tests/ui/format.rs
index c22345a79d4..e805f181889 100644
--- a/src/tools/clippy/tests/ui/format.rs
+++ b/src/tools/clippy/tests/ui/format.rs
@@ -1,5 +1,4 @@
 // run-rustfix
-// aux-build: proc_macro_with_span.rs
 #![warn(clippy::useless_format)]
 #![allow(
     unused_tuple_struct_fields,
@@ -10,8 +9,6 @@
     clippy::uninlined_format_args
 )]
 
-extern crate proc_macro_with_span;
-
 struct Foo(pub String);
 
 macro_rules! foo {
@@ -92,7 +89,4 @@ fn main() {
     let _ = format!("{abc}");
     let xx = "xx";
     let _ = format!("{xx}");
-
-    // Issue #10148
-    println!(proc_macro_with_span::with_span!(""something ""));
 }
diff --git a/src/tools/clippy/tests/ui/format.stderr b/src/tools/clippy/tests/ui/format.stderr
index a0e5d5c8ad2..0ef0ac655d3 100644
--- a/src/tools/clippy/tests/ui/format.stderr
+++ b/src/tools/clippy/tests/ui/format.stderr
@@ -1,5 +1,5 @@
 error: useless use of `format!`
-  --> $DIR/format.rs:22:5
+  --> $DIR/format.rs:19:5
    |
 LL |     format!("foo");
    |     ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
@@ -7,19 +7,19 @@ LL |     format!("foo");
    = note: `-D clippy::useless-format` implied by `-D warnings`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:23:5
+  --> $DIR/format.rs:20:5
    |
 LL |     format!("{{}}");
    |     ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:24:5
+  --> $DIR/format.rs:21:5
    |
 LL |     format!("{{}} abc {{}}");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:25:5
+  --> $DIR/format.rs:22:5
    |
 LL | /     format!(
 LL | |         r##"foo {{}}
@@ -34,67 +34,67 @@ LL ~ " bar"##.to_string();
    |
 
 error: useless use of `format!`
-  --> $DIR/format.rs:30:13
+  --> $DIR/format.rs:27:13
    |
 LL |     let _ = format!("");
    |             ^^^^^^^^^^^ help: consider using `String::new()`: `String::new()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:32:5
+  --> $DIR/format.rs:29:5
    |
 LL |     format!("{}", "foo");
    |     ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:40:5
+  --> $DIR/format.rs:37:5
    |
 LL |     format!("{}", arg);
    |     ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:70:5
+  --> $DIR/format.rs:67:5
    |
 LL |     format!("{}", 42.to_string());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:72:5
+  --> $DIR/format.rs:69:5
    |
 LL |     format!("{}", x.display().to_string());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:76:18
+  --> $DIR/format.rs:73:18
    |
 LL |     let _ = Some(format!("{}", a + "bar"));
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:80:22
+  --> $DIR/format.rs:77:22
    |
 LL |     let _s: String = format!("{}", &*v.join("/n"));
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:86:13
+  --> $DIR/format.rs:83:13
    |
 LL |     let _ = format!("{x}");
    |             ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:88:13
+  --> $DIR/format.rs:85:13
    |
 LL |     let _ = format!("{y}", y = x);
    |             ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:92:13
+  --> $DIR/format.rs:89:13
    |
 LL |     let _ = format!("{abc}");
    |             ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:94:13
+  --> $DIR/format.rs:91:13
    |
 LL |     let _ = format!("{xx}");
    |             ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()`
diff --git a/src/tools/clippy/tests/ui/impl_trait_in_params.stderr b/src/tools/clippy/tests/ui/impl_trait_in_params.stderr
index acfcc21445e..80383743525 100644
--- a/src/tools/clippy/tests/ui/impl_trait_in_params.stderr
+++ b/src/tools/clippy/tests/ui/impl_trait_in_params.stderr
@@ -5,7 +5,7 @@ LL | pub fn a(_: impl Trait) {}
    |             ^^^^^^^^^^
    |
    = note: `-D clippy::impl-trait-in-params` implied by `-D warnings`
-help: add a type paremeter
+help: add a type parameter
    |
 LL | pub fn a<{ /* Generic name */ }: Trait>(_: impl Trait) {}
    |         +++++++++++++++++++++++++++++++
@@ -16,7 +16,7 @@ error: '`impl Trait` used as a function parameter'
 LL | pub fn c<C: Trait>(_: C, _: impl Trait) {}
    |                             ^^^^^^^^^^
    |
-help: add a type paremeter
+help: add a type parameter
    |
 LL | pub fn c<C: Trait, { /* Generic name */ }: Trait>(_: C, _: impl Trait) {}
    |                  +++++++++++++++++++++++++++++++
diff --git a/src/tools/clippy/tests/ui/implicit_clone.fixed b/src/tools/clippy/tests/ui/implicit_clone.fixed
index 51b1afbe5ac..8ccc3da7b47 100644
--- a/src/tools/clippy/tests/ui/implicit_clone.fixed
+++ b/src/tools/clippy/tests/ui/implicit_clone.fixed
@@ -87,7 +87,7 @@ fn main() {
     let kitten = Kitten {};
     let _ = kitten.clone();
     let _ = own_same_from_ref(&kitten);
-    // this shouln't lint
+    // this shouldn't lint
     let _ = kitten.to_vec();
 
     // we expect no lints for this
diff --git a/src/tools/clippy/tests/ui/implicit_clone.rs b/src/tools/clippy/tests/ui/implicit_clone.rs
index 8a9027433d9..59333312607 100644
--- a/src/tools/clippy/tests/ui/implicit_clone.rs
+++ b/src/tools/clippy/tests/ui/implicit_clone.rs
@@ -87,7 +87,7 @@ fn main() {
     let kitten = Kitten {};
     let _ = kitten.to_owned();
     let _ = own_same_from_ref(&kitten);
-    // this shouln't lint
+    // this shouldn't lint
     let _ = kitten.to_vec();
 
     // we expect no lints for this
diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.rs b/src/tools/clippy/tests/ui/len_without_is_empty.rs
index b5dec6c46bd..52aabefaed2 100644
--- a/src/tools/clippy/tests/ui/len_without_is_empty.rs
+++ b/src/tools/clippy/tests/ui/len_without_is_empty.rs
@@ -282,6 +282,87 @@ impl AsyncLen {
     }
 }
 
+// issue #7232
+pub struct AsyncLenWithoutIsEmpty;
+impl AsyncLenWithoutIsEmpty {
+    pub async fn async_task(&self) -> bool {
+        true
+    }
+
+    pub async fn len(&self) -> usize {
+        usize::from(!self.async_task().await)
+    }
+}
+
+// issue #7232
+pub struct AsyncOptionLenWithoutIsEmpty;
+impl AsyncOptionLenWithoutIsEmpty {
+    async fn async_task(&self) -> bool {
+        true
+    }
+
+    pub async fn len(&self) -> Option<usize> {
+        None
+    }
+}
+
+// issue #7232
+pub struct AsyncOptionLenNonIntegral;
+impl AsyncOptionLenNonIntegral {
+    // don't lint
+    pub async fn len(&self) -> Option<String> {
+        None
+    }
+}
+
+// issue #7232
+pub struct AsyncResultLenWithoutIsEmpty;
+impl AsyncResultLenWithoutIsEmpty {
+    async fn async_task(&self) -> bool {
+        true
+    }
+
+    pub async fn len(&self) -> Result<usize, ()> {
+        Err(())
+    }
+}
+
+// issue #7232
+pub struct AsyncOptionLen;
+impl AsyncOptionLen {
+    async fn async_task(&self) -> bool {
+        true
+    }
+
+    pub async fn len(&self) -> Result<usize, ()> {
+        Err(())
+    }
+
+    pub async fn is_empty(&self) -> bool {
+        true
+    }
+}
+
+pub struct AsyncLenSyncIsEmpty;
+impl AsyncLenSyncIsEmpty {
+    pub async fn len(&self) -> u32 {
+        0
+    }
+
+    pub fn is_empty(&self) -> bool {
+        true
+    }
+}
+
+// issue #9520
+pub struct NonStandardLen;
+impl NonStandardLen {
+    // don't lint
+    pub fn len(&self, something: usize) -> usize {
+        something
+    }
+}
+
 // issue #9520
 pub struct NonStandardLenAndIsEmptySignature;
 impl NonStandardLenAndIsEmptySignature {
@@ -328,4 +409,15 @@ impl NonStandardSignatureWithGenerics {
     }
 }
 
+pub struct DifferingErrors;
+impl DifferingErrors {
+    pub fn len(&self) -> Result<usize, u8> {
+        Ok(0)
+    }
+
+    pub fn is_empty(&self) -> Result<bool, u16> {
+        Ok(true)
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.stderr b/src/tools/clippy/tests/ui/len_without_is_empty.stderr
index 8e890e2e259..1bce1734b81 100644
--- a/src/tools/clippy/tests/ui/len_without_is_empty.stderr
+++ b/src/tools/clippy/tests/ui/len_without_is_empty.stderr
@@ -119,5 +119,23 @@ LL |     pub fn len(&self) -> Result<usize, ()> {
    |
    = help: use a custom `Error` type instead
 
-error: aborting due to 12 previous errors
+error: struct `AsyncLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method
+  --> $DIR/len_without_is_empty.rs:292:5
+   |
+LL |     pub async fn len(&self) -> usize {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: struct `AsyncOptionLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method
+  --> $DIR/len_without_is_empty.rs:304:5
+   |
+LL |     pub async fn len(&self) -> Option<usize> {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: struct `AsyncResultLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method
+  --> $DIR/len_without_is_empty.rs:325:5
+   |
+LL |     pub async fn len(&self) -> Result<usize, ()> {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 15 previous errors
 
diff --git a/src/tools/clippy/tests/ui/let_unit.fixed b/src/tools/clippy/tests/ui/let_unit.fixed
index 6343cff0f7f..76ff0645f41 100644
--- a/src/tools/clippy/tests/ui/let_unit.fixed
+++ b/src/tools/clippy/tests/ui/let_unit.fixed
@@ -175,3 +175,7 @@ fn attributes() {
     #[expect(clippy::let_unit_value)]
     let _ = f();
 }
+
+async fn issue10433() {
+    let _pending: () = std::future::pending().await;
+}
diff --git a/src/tools/clippy/tests/ui/let_unit.rs b/src/tools/clippy/tests/ui/let_unit.rs
index c9bb2849f5c..895ccfe366a 100644
--- a/src/tools/clippy/tests/ui/let_unit.rs
+++ b/src/tools/clippy/tests/ui/let_unit.rs
@@ -175,3 +175,7 @@ fn attributes() {
     #[expect(clippy::let_unit_value)]
     let _ = f();
 }
+
+async fn issue10433() {
+    let _pending: () = std::future::pending().await;
+}
diff --git a/src/tools/clippy/tests/ui/let_with_type_underscore.rs b/src/tools/clippy/tests/ui/let_with_type_underscore.rs
new file mode 100644
index 00000000000..175718b94c8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_with_type_underscore.rs
@@ -0,0 +1,19 @@
+#![allow(unused)]
+#![warn(clippy::let_with_type_underscore)]
+#![allow(clippy::let_unit_value)]
+
+fn func() -> &'static str {
+    ""
+}
+
+fn main() {
+    // Will lint
+    let x: _ = 1;
+    let _: _ = 2;
+    let x: _ = func();
+
+    let x = 1; // Will not lint, Rust inferres this to an integer before Clippy
+    let x = func();
+    let x: Vec<_> = Vec::<u32>::new();
+    let x: [_; 1] = [1];
+}
diff --git a/src/tools/clippy/tests/ui/let_with_type_underscore.stderr b/src/tools/clippy/tests/ui/let_with_type_underscore.stderr
new file mode 100644
index 00000000000..16bf83c708f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_with_type_underscore.stderr
@@ -0,0 +1,39 @@
+error: variable declared with type underscore
+  --> $DIR/let_with_type_underscore.rs:11:5
+   |
+LL |     let x: _ = 1;
+   |     ^^^^^^^^^^^^^
+   |
+help: remove the explicit type `_` declaration
+  --> $DIR/let_with_type_underscore.rs:11:10
+   |
+LL |     let x: _ = 1;
+   |          ^^^
+   = note: `-D clippy::let-with-type-underscore` implied by `-D warnings`
+
+error: variable declared with type underscore
+  --> $DIR/let_with_type_underscore.rs:12:5
+   |
+LL |     let _: _ = 2;
+   |     ^^^^^^^^^^^^^
+   |
+help: remove the explicit type `_` declaration
+  --> $DIR/let_with_type_underscore.rs:12:10
+   |
+LL |     let _: _ = 2;
+   |          ^^^
+
+error: variable declared with type underscore
+  --> $DIR/let_with_type_underscore.rs:13:5
+   |
+LL |     let x: _ = func();
+   |     ^^^^^^^^^^^^^^^^^^
+   |
+help: remove the explicit type `_` declaration
+  --> $DIR/let_with_type_underscore.rs:13:10
+   |
+LL |     let x: _ = func();
+   |          ^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.fixed b/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
index 4cdc0546a74..6916a284a20 100644
--- a/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
@@ -2,6 +2,7 @@
 // aux-build:macro_rules.rs
 
 #![warn(clippy::manual_rem_euclid)]
+#![allow(clippy::let_with_type_underscore)]
 
 #[macro_use]
 extern crate macro_rules;
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.rs b/src/tools/clippy/tests/ui/manual_rem_euclid.rs
index 58a9e20f38b..412dbddb426 100644
--- a/src/tools/clippy/tests/ui/manual_rem_euclid.rs
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.rs
@@ -2,6 +2,7 @@
 // aux-build:macro_rules.rs
 
 #![warn(clippy::manual_rem_euclid)]
+#![allow(clippy::let_with_type_underscore)]
 
 #[macro_use]
 extern crate macro_rules;
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.stderr b/src/tools/clippy/tests/ui/manual_rem_euclid.stderr
index e3122a588b6..6d06654638b 100644
--- a/src/tools/clippy/tests/ui/manual_rem_euclid.stderr
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.stderr
@@ -1,5 +1,5 @@
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:19:18
+  --> $DIR/manual_rem_euclid.rs:20:18
    |
 LL |     let _: i32 = ((value % 4) + 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
@@ -7,31 +7,31 @@ LL |     let _: i32 = ((value % 4) + 4) % 4;
    = note: `-D clippy::manual-rem-euclid` implied by `-D warnings`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:20:18
+  --> $DIR/manual_rem_euclid.rs:21:18
    |
 LL |     let _: i32 = (4 + (value % 4)) % 4;
    |                  ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:21:18
+  --> $DIR/manual_rem_euclid.rs:22:18
    |
 LL |     let _: i32 = (value % 4 + 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:22:18
+  --> $DIR/manual_rem_euclid.rs:23:18
    |
 LL |     let _: i32 = (4 + value % 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:23:22
+  --> $DIR/manual_rem_euclid.rs:24:22
    |
 LL |     let _: i32 = 1 + (4 + value % 4) % 4;
    |                      ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:12:22
+  --> $DIR/manual_rem_euclid.rs:13:22
    |
 LL |         let _: i32 = ((value % 4) + 4) % 4;
    |                      ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
@@ -42,25 +42,25 @@ LL |     internal_rem_euclid!();
    = note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:49:5
+  --> $DIR/manual_rem_euclid.rs:50:5
    |
 LL |     ((num % 4) + 4) % 4
    |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:54:5
+  --> $DIR/manual_rem_euclid.rs:55:5
    |
 LL |     ((num % 4) + 4) % 4
    |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:66:18
+  --> $DIR/manual_rem_euclid.rs:67:18
    |
 LL |     let _: i32 = ((x % 4) + 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:79:18
+  --> $DIR/manual_rem_euclid.rs:80:18
    |
 LL |     let _: i32 = ((x % 4) + 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)`
diff --git a/src/tools/clippy/tests/ui/match_result_ok.fixed b/src/tools/clippy/tests/ui/match_result_ok.fixed
index 8b91b9854a0..10ae1ee5245 100644
--- a/src/tools/clippy/tests/ui/match_result_ok.fixed
+++ b/src/tools/clippy/tests/ui/match_result_ok.fixed
@@ -16,7 +16,7 @@ fn str_to_int_ok(x: &str) -> i32 {
 #[rustfmt::skip]
 fn strange_some_no_else(x: &str) -> i32 {
     {
-        if let Ok(y) = x   .   parse()       {
+        if let Ok(y) = x   .   parse()    {
             return y;
         };
         0
diff --git a/src/tools/clippy/tests/ui/match_result_ok.stderr b/src/tools/clippy/tests/ui/match_result_ok.stderr
index 98a95705ca5..cbdc56aa28c 100644
--- a/src/tools/clippy/tests/ui/match_result_ok.stderr
+++ b/src/tools/clippy/tests/ui/match_result_ok.stderr
@@ -18,7 +18,7 @@ LL |         if let Some(y) = x   .   parse()   .   ok   ()    {
    |
 help: consider matching on `Ok(y)` and removing the call to `ok` instead
    |
-LL |         if let Ok(y) = x   .   parse()       {
+LL |         if let Ok(y) = x   .   parse()    {
    |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: matching on `Some` with `ok()` is redundant
diff --git a/src/tools/clippy/tests/ui/missing_assert_message.rs b/src/tools/clippy/tests/ui/missing_assert_message.rs
new file mode 100644
index 00000000000..89404ca8827
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_assert_message.rs
@@ -0,0 +1,84 @@
+#![allow(unused)]
+#![warn(clippy::missing_assert_message)]
+
+macro_rules! bar {
+    ($( $x:expr ),*) => {
+        foo()
+    };
+}
+
+fn main() {}
+
+// Should trigger warning
+fn asserts_without_message() {
+    assert!(foo());
+    assert_eq!(foo(), foo());
+    assert_ne!(foo(), foo());
+    debug_assert!(foo());
+    debug_assert_eq!(foo(), foo());
+    debug_assert_ne!(foo(), foo());
+}
+
+// Should trigger warning
+fn asserts_without_message_but_with_macro_calls() {
+    assert!(bar!(true));
+    assert!(bar!(true, false));
+    assert_eq!(bar!(true), foo());
+    assert_ne!(bar!(true, true), bar!(true));
+}
+
+// Should trigger warning
+fn asserts_with_trailing_commas() {
+    assert!(foo(),);
+    assert_eq!(foo(), foo(),);
+    assert_ne!(foo(), foo(),);
+    debug_assert!(foo(),);
+    debug_assert_eq!(foo(), foo(),);
+    debug_assert_ne!(foo(), foo(),);
+}
+
+// Should not trigger warning
+fn asserts_with_message_and_with_macro_calls() {
+    assert!(bar!(true), "msg");
+    assert!(bar!(true, false), "msg");
+    assert_eq!(bar!(true), foo(), "msg");
+    assert_ne!(bar!(true, true), bar!(true), "msg");
+}
+
+// Should not trigger warning
+fn asserts_with_message() {
+    assert!(foo(), "msg");
+    assert_eq!(foo(), foo(), "msg");
+    assert_ne!(foo(), foo(), "msg");
+    debug_assert!(foo(), "msg");
+    debug_assert_eq!(foo(), foo(), "msg");
+    debug_assert_ne!(foo(), foo(), "msg");
+}
+
+// Should not trigger warning
+#[test]
+fn asserts_without_message_but_inside_a_test_function() {
+    assert!(foo());
+    assert_eq!(foo(), foo());
+    assert_ne!(foo(), foo());
+    debug_assert!(foo());
+    debug_assert_eq!(foo(), foo());
+    debug_assert_ne!(foo(), foo());
+}
+
+// Should not trigger warning
+#[cfg(test)]
+mod tests {
+    fn asserts_without_message_but_inside_a_test_module() {
+        assert!(foo());
+        assert_eq!(foo(), foo());
+        assert_ne!(foo(), foo());
+        debug_assert!(foo());
+        debug_assert_eq!(foo(), foo());
+        debug_assert_ne!(foo(), foo());
+    }
+}
+
+fn foo() -> bool {
+    true
+}
diff --git a/src/tools/clippy/tests/ui/missing_assert_message.stderr b/src/tools/clippy/tests/ui/missing_assert_message.stderr
new file mode 100644
index 00000000000..ecd03801277
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_assert_message.stderr
@@ -0,0 +1,131 @@
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:14:5
+   |
+LL |     assert!(foo());
+   |     ^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+   = note: `-D clippy::missing-assert-message` implied by `-D warnings`
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:15:5
+   |
+LL |     assert_eq!(foo(), foo());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:16:5
+   |
+LL |     assert_ne!(foo(), foo());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:17:5
+   |
+LL |     debug_assert!(foo());
+   |     ^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:18:5
+   |
+LL |     debug_assert_eq!(foo(), foo());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:19:5
+   |
+LL |     debug_assert_ne!(foo(), foo());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:24:5
+   |
+LL |     assert!(bar!(true));
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:25:5
+   |
+LL |     assert!(bar!(true, false));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:26:5
+   |
+LL |     assert_eq!(bar!(true), foo());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:27:5
+   |
+LL |     assert_ne!(bar!(true, true), bar!(true));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:32:5
+   |
+LL |     assert!(foo(),);
+   |     ^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:33:5
+   |
+LL |     assert_eq!(foo(), foo(),);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:34:5
+   |
+LL |     assert_ne!(foo(), foo(),);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:35:5
+   |
+LL |     debug_assert!(foo(),);
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:36:5
+   |
+LL |     debug_assert_eq!(foo(), foo(),);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:37:5
+   |
+LL |     debug_assert_ne!(foo(), foo(),);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: aborting due to 16 previous errors
+
diff --git a/src/tools/clippy/tests/ui/missing_doc.stderr b/src/tools/clippy/tests/ui/missing_doc.stderr
index d3bef28bf64..4e8a49bf1cd 100644
--- a/src/tools/clippy/tests/ui/missing_doc.stderr
+++ b/src/tools/clippy/tests/ui/missing_doc.stderr
@@ -6,30 +6,12 @@ LL | type Typedef = String;
    |
    = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings`
 
-error: missing documentation for a type alias
-  --> $DIR/missing_doc.rs:17:1
-   |
-LL | pub type PubTypedef = String;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
 error: missing documentation for a module
   --> $DIR/missing_doc.rs:19:1
    |
 LL | mod module_no_dox {}
    | ^^^^^^^^^^^^^^^^^^^^
 
-error: missing documentation for a module
-  --> $DIR/missing_doc.rs:20:1
-   |
-LL | pub mod pub_module_no_dox {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for a function
-  --> $DIR/missing_doc.rs:24:1
-   |
-LL | pub fn foo2() {}
-   | ^^^^^^^^^^^^^^^^
-
 error: missing documentation for a function
   --> $DIR/missing_doc.rs:25:1
    |
@@ -69,50 +51,18 @@ error: missing documentation for a variant
 LL |     BarB,
    |     ^^^^
 
-error: missing documentation for an enum
-  --> $DIR/missing_doc.rs:44:1
-   |
-LL | / pub enum PubBaz {
-LL | |     PubBazA { a: isize },
-LL | | }
-   | |_^
-
-error: missing documentation for a variant
-  --> $DIR/missing_doc.rs:45:5
-   |
-LL |     PubBazA { a: isize },
-   |     ^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for a struct field
-  --> $DIR/missing_doc.rs:45:15
-   |
-LL |     PubBazA { a: isize },
-   |               ^^^^^^^^
-
 error: missing documentation for a constant
   --> $DIR/missing_doc.rs:65:1
    |
 LL | const FOO: u32 = 0;
    | ^^^^^^^^^^^^^^^^^^^
 
-error: missing documentation for a constant
-  --> $DIR/missing_doc.rs:72:1
-   |
-LL | pub const FOO4: u32 = 0;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^
-
 error: missing documentation for a static
   --> $DIR/missing_doc.rs:74:1
    |
 LL | static BAR: u32 = 0;
    | ^^^^^^^^^^^^^^^^^^^^
 
-error: missing documentation for a static
-  --> $DIR/missing_doc.rs:81:1
-   |
-LL | pub static BAR4: u32 = 0;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
-
 error: missing documentation for a module
   --> $DIR/missing_doc.rs:83:1
    |
@@ -126,34 +76,16 @@ LL | | }
    | |_^
 
 error: missing documentation for a function
-  --> $DIR/missing_doc.rs:86:5
-   |
-LL |     pub fn undocumented1() {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for a function
-  --> $DIR/missing_doc.rs:87:5
-   |
-LL |     pub fn undocumented2() {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for a function
   --> $DIR/missing_doc.rs:88:5
    |
 LL |     fn undocumented3() {}
    |     ^^^^^^^^^^^^^^^^^^^^^
 
 error: missing documentation for a function
-  --> $DIR/missing_doc.rs:93:9
-   |
-LL |         pub fn also_undocumented1() {}
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for a function
   --> $DIR/missing_doc.rs:94:9
    |
 LL |         fn also_undocumented2() {}
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 24 previous errors
+error: aborting due to 13 previous errors
 
diff --git a/src/tools/clippy/tests/ui/missing_doc_impl.stderr b/src/tools/clippy/tests/ui/missing_doc_impl.stderr
index b410f56e167..111d6546966 100644
--- a/src/tools/clippy/tests/ui/missing_doc_impl.stderr
+++ b/src/tools/clippy/tests/ui/missing_doc_impl.stderr
@@ -21,60 +21,12 @@ error: missing documentation for a struct field
 LL |     b: isize,
    |     ^^^^^^^^
 
-error: missing documentation for a struct
-  --> $DIR/missing_doc_impl.rs:18:1
-   |
-LL | / pub struct PubFoo {
-LL | |     pub a: isize,
-LL | |     b: isize,
-LL | | }
-   | |_^
-
-error: missing documentation for a struct field
-  --> $DIR/missing_doc_impl.rs:19:5
-   |
-LL |     pub a: isize,
-   |     ^^^^^^^^^^^^
-
 error: missing documentation for a struct field
   --> $DIR/missing_doc_impl.rs:20:5
    |
 LL |     b: isize,
    |     ^^^^^^^^
 
-error: missing documentation for a trait
-  --> $DIR/missing_doc_impl.rs:43:1
-   |
-LL | / pub trait C {
-LL | |     fn foo(&self);
-LL | |     fn foo_with_impl(&self) {}
-LL | | }
-   | |_^
-
-error: missing documentation for a method
-  --> $DIR/missing_doc_impl.rs:44:5
-   |
-LL |     fn foo(&self);
-   |     ^^^^^^^^^^^^^^
-
-error: missing documentation for a method
-  --> $DIR/missing_doc_impl.rs:45:5
-   |
-LL |     fn foo_with_impl(&self) {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for an associated type
-  --> $DIR/missing_doc_impl.rs:55:5
-   |
-LL |     type AssociatedType;
-   |     ^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for an associated type
-  --> $DIR/missing_doc_impl.rs:56:5
-   |
-LL |     type AssociatedTypeDef = Self;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
 error: missing documentation for an associated function
   --> $DIR/missing_doc_impl.rs:67:5
    |
@@ -90,12 +42,6 @@ LL |     fn bar() {}
    |     ^^^^^^^^^^^
 
 error: missing documentation for an associated function
-  --> $DIR/missing_doc_impl.rs:74:5
-   |
-LL |     pub fn foo() {}
-   |     ^^^^^^^^^^^^^^^
-
-error: missing documentation for an associated function
   --> $DIR/missing_doc_impl.rs:78:5
    |
 LL | /     fn foo2() -> u32 {
@@ -103,5 +49,5 @@ LL | |         1
 LL | |     }
    | |_____^
 
-error: aborting due to 15 previous errors
+error: aborting due to 7 previous errors
 
diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
index 4511bc99c3c..5073685c9f0 100644
--- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
+++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
@@ -116,4 +116,32 @@ fn issue10259() {
     unsafe_macro!();
 }
 
+fn _fn_ptr(x: unsafe fn()) {
+    unsafe {
+        x();
+        x();
+    }
+}
+
+fn _assoc_const() {
+    trait X {
+        const X: unsafe fn();
+    }
+    fn _f<T: X>() {
+        unsafe {
+            T::X();
+            T::X();
+        }
+    }
+}
+
+fn _field_fn_ptr(x: unsafe fn()) {
+    struct X(unsafe fn());
+    let x = X(x);
+    unsafe {
+        x.0();
+        x.0();
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr
index 303aeb7aee0..e0c1d3801f7 100644
--- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr
+++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr
@@ -125,5 +125,65 @@ note: raw pointer dereference occurs here
 LL |     unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
    |                                       ^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 5 previous errors
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+  --> $DIR/multiple_unsafe_ops_per_block.rs:120:5
+   |
+LL | /     unsafe {
+LL | |         x();
+LL | |         x();
+LL | |     }
+   | |_____^
+   |
+note: unsafe function call occurs here
+  --> $DIR/multiple_unsafe_ops_per_block.rs:121:9
+   |
+LL |         x();
+   |         ^^^
+note: unsafe function call occurs here
+  --> $DIR/multiple_unsafe_ops_per_block.rs:122:9
+   |
+LL |         x();
+   |         ^^^
+
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+  --> $DIR/multiple_unsafe_ops_per_block.rs:131:9
+   |
+LL | /         unsafe {
+LL | |             T::X();
+LL | |             T::X();
+LL | |         }
+   | |_________^
+   |
+note: unsafe function call occurs here
+  --> $DIR/multiple_unsafe_ops_per_block.rs:132:13
+   |
+LL |             T::X();
+   |             ^^^^^^
+note: unsafe function call occurs here
+  --> $DIR/multiple_unsafe_ops_per_block.rs:133:13
+   |
+LL |             T::X();
+   |             ^^^^^^
+
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+  --> $DIR/multiple_unsafe_ops_per_block.rs:141:5
+   |
+LL | /     unsafe {
+LL | |         x.0();
+LL | |         x.0();
+LL | |     }
+   | |_____^
+   |
+note: unsafe function call occurs here
+  --> $DIR/multiple_unsafe_ops_per_block.rs:142:9
+   |
+LL |         x.0();
+   |         ^^^^^
+note: unsafe function call occurs here
+  --> $DIR/multiple_unsafe_ops_per_block.rs:143:9
+   |
+LL |         x.0();
+   |         ^^^^^
+
+error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.rs b/src/tools/clippy/tests/ui/new_ret_no_self.rs
index beec42f08bb..a2a30c8b931 100644
--- a/src/tools/clippy/tests/ui/new_ret_no_self.rs
+++ b/src/tools/clippy/tests/ui/new_ret_no_self.rs
@@ -406,7 +406,7 @@ mod issue10041 {
     struct Bomb;
 
     impl Bomb {
-        // Hidden <Rhs = Self> default generic paramter.
+        // Hidden <Rhs = Self> default generic parameter.
         pub fn new() -> impl PartialOrd {
             0i32
         }
diff --git a/src/tools/clippy/tests/ui/redundant_async_block.fixed b/src/tools/clippy/tests/ui/redundant_async_block.fixed
new file mode 100644
index 00000000000..5f9931df45e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_async_block.fixed
@@ -0,0 +1,64 @@
+// run-rustfix
+
+#![allow(unused)]
+#![warn(clippy::redundant_async_block)]
+
+async fn func1(n: usize) -> usize {
+    n + 1
+}
+
+async fn func2() -> String {
+    let s = String::from("some string");
+    let f = async { (*s).to_owned() };
+    let x = f;
+    x.await
+}
+
+macro_rules! await_in_macro {
+    ($e:expr) => {
+        std::convert::identity($e).await
+    };
+}
+
+async fn func3(n: usize) -> usize {
+    // Do not lint (suggestion would be `std::convert::identity(func1(n))`
+    // which copies code from inside the macro)
+    async move { await_in_macro!(func1(n)) }.await
+}
+
+// This macro should never be linted as `$e` might contain `.await`
+macro_rules! async_await_parameter_in_macro {
+    ($e:expr) => {
+        async { $e.await }
+    };
+}
+
+// MISSED OPPORTUNITY: this macro could be linted as the `async` block does not
+// contain code coming from the parameters
+macro_rules! async_await_in_macro {
+    ($f:expr) => {
+        ($f)(async { func2().await })
+    };
+}
+
+fn main() {
+    let fut1 = async { 17 };
+    let fut2 = fut1;
+
+    let fut1 = async { 25 };
+    let fut2 = fut1;
+
+    let fut = async { 42 };
+
+    // Do not lint: not a single expression
+    let fut = async {
+        func1(10).await;
+        func2().await
+    };
+
+    // Do not lint: expression contains `.await`
+    let fut = async { func1(func2().await.len()).await };
+
+    let fut = async_await_parameter_in_macro!(func2());
+    let fut = async_await_in_macro!(std::convert::identity);
+}
diff --git a/src/tools/clippy/tests/ui/redundant_async_block.rs b/src/tools/clippy/tests/ui/redundant_async_block.rs
new file mode 100644
index 00000000000..de3c9970c65
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_async_block.rs
@@ -0,0 +1,64 @@
+// run-rustfix
+
+#![allow(unused)]
+#![warn(clippy::redundant_async_block)]
+
+async fn func1(n: usize) -> usize {
+    n + 1
+}
+
+async fn func2() -> String {
+    let s = String::from("some string");
+    let f = async { (*s).to_owned() };
+    let x = async { f.await };
+    x.await
+}
+
+macro_rules! await_in_macro {
+    ($e:expr) => {
+        std::convert::identity($e).await
+    };
+}
+
+async fn func3(n: usize) -> usize {
+    // Do not lint (suggestion would be `std::convert::identity(func1(n))`
+    // which copies code from inside the macro)
+    async move { await_in_macro!(func1(n)) }.await
+}
+
+// This macro should never be linted as `$e` might contain `.await`
+macro_rules! async_await_parameter_in_macro {
+    ($e:expr) => {
+        async { $e.await }
+    };
+}
+
+// MISSED OPPORTUNITY: this macro could be linted as the `async` block does not
+// contain code coming from the parameters
+macro_rules! async_await_in_macro {
+    ($f:expr) => {
+        ($f)(async { func2().await })
+    };
+}
+
+fn main() {
+    let fut1 = async { 17 };
+    let fut2 = async { fut1.await };
+
+    let fut1 = async { 25 };
+    let fut2 = async move { fut1.await };
+
+    let fut = async { async { 42 }.await };
+
+    // Do not lint: not a single expression
+    let fut = async {
+        func1(10).await;
+        func2().await
+    };
+
+    // Do not lint: expression contains `.await`
+    let fut = async { func1(func2().await.len()).await };
+
+    let fut = async_await_parameter_in_macro!(func2());
+    let fut = async_await_in_macro!(std::convert::identity);
+}
diff --git a/src/tools/clippy/tests/ui/redundant_async_block.stderr b/src/tools/clippy/tests/ui/redundant_async_block.stderr
new file mode 100644
index 00000000000..b16d96dce84
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_async_block.stderr
@@ -0,0 +1,28 @@
+error: this async expression only awaits a single future
+  --> $DIR/redundant_async_block.rs:13:13
+   |
+LL |     let x = async { f.await };
+   |             ^^^^^^^^^^^^^^^^^ help: you can reduce it to: `f`
+   |
+   = note: `-D clippy::redundant-async-block` implied by `-D warnings`
+
+error: this async expression only awaits a single future
+  --> $DIR/redundant_async_block.rs:46:16
+   |
+LL |     let fut2 = async { fut1.await };
+   |                ^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut1`
+
+error: this async expression only awaits a single future
+  --> $DIR/redundant_async_block.rs:49:16
+   |
+LL |     let fut2 = async move { fut1.await };
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut1`
+
+error: this async expression only awaits a single future
+  --> $DIR/redundant_async_block.rs:51:15
+   |
+LL |     let fut = async { async { 42 }.await };
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `async { 42 }`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
index c0e49ff4caa..b987fd2ce6f 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
@@ -2,6 +2,7 @@
 
 #![feature(async_closure)]
 #![warn(clippy::redundant_closure_call)]
+#![allow(clippy::redundant_async_block)]
 #![allow(unused)]
 
 async fn something() -> u32 {
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
index 9e6e54348a8..633a2979d5d 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
@@ -2,6 +2,7 @@
 
 #![feature(async_closure)]
 #![warn(clippy::redundant_closure_call)]
+#![allow(clippy::redundant_async_block)]
 #![allow(unused)]
 
 async fn something() -> u32 {
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
index d71bcba2a82..8a1f0771659 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
@@ -1,5 +1,5 @@
 error: try not to call a closure in the expression where it is declared
-  --> $DIR/redundant_closure_call_fixable.rs:16:13
+  --> $DIR/redundant_closure_call_fixable.rs:17:13
    |
 LL |     let a = (|| 42)();
    |             ^^^^^^^^^ help: try doing something like: `42`
@@ -7,7 +7,7 @@ LL |     let a = (|| 42)();
    = note: `-D clippy::redundant-closure-call` implied by `-D warnings`
 
 error: try not to call a closure in the expression where it is declared
-  --> $DIR/redundant_closure_call_fixable.rs:17:13
+  --> $DIR/redundant_closure_call_fixable.rs:18:13
    |
 LL |       let b = (async || {
    |  _____________^
@@ -27,7 +27,7 @@ LL ~     };
    |
 
 error: try not to call a closure in the expression where it is declared
-  --> $DIR/redundant_closure_call_fixable.rs:22:13
+  --> $DIR/redundant_closure_call_fixable.rs:23:13
    |
 LL |       let c = (|| {
    |  _____________^
@@ -47,13 +47,13 @@ LL ~     };
    |
 
 error: try not to call a closure in the expression where it is declared
-  --> $DIR/redundant_closure_call_fixable.rs:27:13
+  --> $DIR/redundant_closure_call_fixable.rs:28:13
    |
 LL |     let d = (async || something().await)();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async { something().await }`
 
 error: try not to call a closure in the expression where it is declared
-  --> $DIR/redundant_closure_call_fixable.rs:36:13
+  --> $DIR/redundant_closure_call_fixable.rs:37:13
    |
 LL |             (|| m!())()
    |             ^^^^^^^^^^^ help: try doing something like: `m!()`
@@ -64,7 +64,7 @@ LL |     m2!();
    = note: this error originates in the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: try not to call a closure in the expression where it is declared
-  --> $DIR/redundant_closure_call_fixable.rs:31:13
+  --> $DIR/redundant_closure_call_fixable.rs:32:13
    |
 LL |             (|| 0)()
    |             ^^^^^^^^ help: try doing something like: `0`
diff --git a/src/tools/clippy/tests/ui/swap.fixed b/src/tools/clippy/tests/ui/swap.fixed
index fa89706a815..04008c0d9b3 100644
--- a/src/tools/clippy/tests/ui/swap.fixed
+++ b/src/tools/clippy/tests/ui/swap.fixed
@@ -65,19 +65,19 @@ fn xor_swap_locals() {
     // This is an xor-based swap of local variables.
     let mut a = 0;
     let mut b = 1;
-    std::mem::swap(&mut a, &mut b)
+    std::mem::swap(&mut a, &mut b);
 }
 
 fn xor_field_swap() {
     // This is an xor-based swap of fields in a struct.
     let mut bar = Bar { a: 0, b: 1 };
-    std::mem::swap(&mut bar.a, &mut bar.b)
+    std::mem::swap(&mut bar.a, &mut bar.b);
 }
 
 fn xor_slice_swap() {
     // This is an xor-based swap of a slice
     let foo = &mut [1, 2];
-    foo.swap(0, 1)
+    foo.swap(0, 1);
 }
 
 fn xor_no_swap() {
diff --git a/src/tools/clippy/tests/ui/swap.stderr b/src/tools/clippy/tests/ui/swap.stderr
index f0acbfe253f..825c9261e19 100644
--- a/src/tools/clippy/tests/ui/swap.stderr
+++ b/src/tools/clippy/tests/ui/swap.stderr
@@ -4,7 +4,7 @@ error: this looks like you are swapping `bar.a` and `bar.b` manually
 LL | /     let temp = bar.a;
 LL | |     bar.a = bar.b;
 LL | |     bar.b = temp;
-   | |________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b)`
+   | |_________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b);`
    |
    = note: or maybe you should use `std::mem::replace`?
    = note: `-D clippy::manual-swap` implied by `-D warnings`
@@ -15,7 +15,7 @@ error: this looks like you are swapping elements of `foo` manually
 LL | /     let temp = foo[0];
 LL | |     foo[0] = foo[1];
 LL | |     foo[1] = temp;
-   | |_________________^ help: try: `foo.swap(0, 1)`
+   | |__________________^ help: try: `foo.swap(0, 1);`
 
 error: this looks like you are swapping elements of `foo` manually
   --> $DIR/swap.rs:46:5
@@ -23,7 +23,7 @@ error: this looks like you are swapping elements of `foo` manually
 LL | /     let temp = foo[0];
 LL | |     foo[0] = foo[1];
 LL | |     foo[1] = temp;
-   | |_________________^ help: try: `foo.swap(0, 1)`
+   | |__________________^ help: try: `foo.swap(0, 1);`
 
 error: this looks like you are swapping elements of `foo` manually
   --> $DIR/swap.rs:65:5
@@ -31,7 +31,7 @@ error: this looks like you are swapping elements of `foo` manually
 LL | /     let temp = foo[0];
 LL | |     foo[0] = foo[1];
 LL | |     foo[1] = temp;
-   | |_________________^ help: try: `foo.swap(0, 1)`
+   | |__________________^ help: try: `foo.swap(0, 1);`
 
 error: this looks like you are swapping `a` and `b` manually
   --> $DIR/swap.rs:76:5
@@ -39,7 +39,7 @@ error: this looks like you are swapping `a` and `b` manually
 LL | /     a ^= b;
 LL | |     b ^= a;
 LL | |     a ^= b;
-   | |___________^ help: try: `std::mem::swap(&mut a, &mut b)`
+   | |___________^ help: try: `std::mem::swap(&mut a, &mut b);`
 
 error: this looks like you are swapping `bar.a` and `bar.b` manually
   --> $DIR/swap.rs:84:5
@@ -47,7 +47,7 @@ error: this looks like you are swapping `bar.a` and `bar.b` manually
 LL | /     bar.a ^= bar.b;
 LL | |     bar.b ^= bar.a;
 LL | |     bar.a ^= bar.b;
-   | |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b)`
+   | |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b);`
 
 error: this looks like you are swapping elements of `foo` manually
   --> $DIR/swap.rs:92:5
@@ -55,7 +55,7 @@ error: this looks like you are swapping elements of `foo` manually
 LL | /     foo[0] ^= foo[1];
 LL | |     foo[1] ^= foo[0];
 LL | |     foo[0] ^= foo[1];
-   | |_____________________^ help: try: `foo.swap(0, 1)`
+   | |_____________________^ help: try: `foo.swap(0, 1);`
 
 error: this looks like you are swapping `foo[0][1]` and `bar[1][0]` manually
   --> $DIR/swap.rs:121:5
@@ -63,7 +63,7 @@ error: this looks like you are swapping `foo[0][1]` and `bar[1][0]` manually
 LL | /     let temp = foo[0][1];
 LL | |     foo[0][1] = bar[1][0];
 LL | |     bar[1][0] = temp;
-   | |____________________^ help: try: `std::mem::swap(&mut foo[0][1], &mut bar[1][0])`
+   | |_____________________^ help: try: `std::mem::swap(&mut foo[0][1], &mut bar[1][0]);`
    |
    = note: or maybe you should use `std::mem::replace`?
 
@@ -74,7 +74,7 @@ LL |       ; let t = a;
    |  _______^
 LL | |     a = b;
 LL | |     b = t;
-   | |_________^ help: try: `std::mem::swap(&mut a, &mut b)`
+   | |__________^ help: try: `std::mem::swap(&mut a, &mut b);`
    |
    = note: or maybe you should use `std::mem::replace`?
 
@@ -85,7 +85,7 @@ LL |       ; let t = c.0;
    |  _______^
 LL | |     c.0 = a;
 LL | |     a = t;
-   | |_________^ help: try: `std::mem::swap(&mut c.0, &mut a)`
+   | |__________^ help: try: `std::mem::swap(&mut c.0, &mut a);`
    |
    = note: or maybe you should use `std::mem::replace`?
 
@@ -95,7 +95,7 @@ error: this looks like you are swapping `b` and `a` manually
 LL | /     let t = b;
 LL | |     b = a;
 LL | |     a = t;
-   | |_________^ help: try: `std::mem::swap(&mut b, &mut a)`
+   | |__________^ help: try: `std::mem::swap(&mut b, &mut a);`
    |
    = note: or maybe you should use `std::mem::replace`?
 
@@ -151,7 +151,7 @@ error: this looks like you are swapping `s.0.x` and `s.0.y` manually
 LL | /     let t = s.0.x;
 LL | |     s.0.x = s.0.y;
 LL | |     s.0.y = t;
-   | |_____________^ help: try: `std::mem::swap(&mut s.0.x, &mut s.0.y)`
+   | |______________^ help: try: `std::mem::swap(&mut s.0.x, &mut s.0.y);`
    |
    = note: or maybe you should use `std::mem::replace`?
 
diff --git a/src/tools/clippy/tests/ui/trailing_empty_array.rs b/src/tools/clippy/tests/ui/trailing_empty_array.rs
index c39b0bcaf22..8e3749eef35 100644
--- a/src/tools/clippy/tests/ui/trailing_empty_array.rs
+++ b/src/tools/clippy/tests/ui/trailing_empty_array.rs
@@ -155,7 +155,6 @@ struct TupleStructReprC(i32, [usize; 0]);
 
 type NamedTuple = (i32, [usize; 0]);
 
-#[rustfmt::skip] // [rustfmt#4995](https://github.com/rust-lang/rustfmt/issues/4995)
 struct ConstParamZeroDefault<const N: usize = 0> {
     field: i32,
     last: [usize; N],
@@ -166,7 +165,6 @@ struct ConstParamNoDefault<const N: usize> {
     last: [usize; N],
 }
 
-#[rustfmt::skip] 
 struct ConstParamNonZeroDefault<const N: usize = 1> {
     field: i32,
     last: [usize; N],
diff --git a/src/tools/clippy/tests/ui/use_self.fixed b/src/tools/clippy/tests/ui/use_self.fixed
index 0a6166571eb..3ac6217312a 100644
--- a/src/tools/clippy/tests/ui/use_self.fixed
+++ b/src/tools/clippy/tests/ui/use_self.fixed
@@ -647,3 +647,13 @@ fn msrv_1_37() {
         }
     }
 }
+
+mod issue_10371 {
+    struct Val<const V: i32> {}
+
+    impl<const V: i32> From<Val<V>> for i32 {
+        fn from(_: Val<V>) -> Self {
+            todo!()
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/use_self.rs b/src/tools/clippy/tests/ui/use_self.rs
index 39c2b431f7f..9dc5d1e3f9b 100644
--- a/src/tools/clippy/tests/ui/use_self.rs
+++ b/src/tools/clippy/tests/ui/use_self.rs
@@ -647,3 +647,13 @@ fn msrv_1_37() {
         }
     }
 }
+
+mod issue_10371 {
+    struct Val<const V: i32> {}
+
+    impl<const V: i32> From<Val<V>> for i32 {
+        fn from(_: Val<V>) -> Self {
+            todo!()
+        }
+    }
+}
diff --git a/src/tools/collect-license-metadata/src/licenses.rs b/src/tools/collect-license-metadata/src/licenses.rs
index 1c95b1bc8e9..2855069db0d 100644
--- a/src/tools/collect-license-metadata/src/licenses.rs
+++ b/src/tools/collect-license-metadata/src/licenses.rs
@@ -42,6 +42,7 @@ pub(crate) struct License {
 impl License {
     fn simplify(&mut self) {
         self.remove_copyright_prefixes();
+        self.remove_trailing_dots();
         self.copyright.sort();
         self.copyright.dedup();
     }
@@ -62,4 +63,12 @@ impl License {
             *copyright = stripped.into();
         }
     }
+
+    fn remove_trailing_dots(&mut self) {
+        for copyright in &mut self.copyright {
+            if copyright.ends_with('.') {
+                *copyright = copyright.trim_end_matches('.').to_string();
+            }
+        }
+    }
 }
diff --git a/src/tools/collect-license-metadata/src/path_tree.rs b/src/tools/collect-license-metadata/src/path_tree.rs
index 133ff683737..7a2a440636d 100644
--- a/src/tools/collect-license-metadata/src/path_tree.rs
+++ b/src/tools/collect-license-metadata/src/path_tree.rs
@@ -13,7 +13,7 @@ pub(crate) enum Node<L> {
     Root { childs: Vec<Node<L>> },
     Directory { name: PathBuf, childs: Vec<Node<L>>, license: Option<L> },
     File { name: PathBuf, license: L },
-    FileGroup { names: Vec<PathBuf>, license: L },
+    Group { files: Vec<PathBuf>, directories: Vec<PathBuf>, license: L },
     Empty,
 }
 
@@ -22,7 +22,7 @@ impl Node<LicenseId> {
         self.merge_directories();
         self.collapse_in_licensed_directories();
         self.merge_directory_licenses();
-        self.merge_file_groups();
+        self.merge_groups();
         self.remove_empty();
     }
 
@@ -64,8 +64,8 @@ impl Node<LicenseId> {
                         Node::Root { .. } => {
                             panic!("can't have a root inside another element");
                         }
-                        Node::FileGroup { .. } => {
-                            panic!("FileGroup should not be present at this stage");
+                        Node::Group { .. } => {
+                            panic!("Group should not be present at this stage");
                         }
                         Node::Directory { license: Some(_), .. } => {
                             panic!("license should not be set at this stage");
@@ -86,8 +86,8 @@ impl Node<LicenseId> {
             }
             Node::Empty => {}
             Node::File { .. } => {}
-            Node::FileGroup { .. } => {
-                panic!("FileGroup should not be present at this stage");
+            Node::Group { .. } => {
+                panic!("Group should not be present at this stage");
             }
             Node::Directory { license: Some(_), .. } => {
                 panic!("license should not be set at this stage");
@@ -132,7 +132,7 @@ impl Node<LicenseId> {
                 }
             }
             Node::File { .. } => {}
-            Node::FileGroup { .. } => {}
+            Node::Group { .. } => panic!("group should not be present at this stage"),
             Node::Empty => {}
         }
     }
@@ -165,8 +165,8 @@ impl Node<LicenseId> {
                             Node::Root { .. } => {
                                 panic!("can't have a root inside another element");
                             }
-                            Node::FileGroup { .. } => {
-                                panic!("FileGroup should not be present at this stage");
+                            Node::Group { .. } => {
+                                panic!("Group should not be present at this stage");
                             }
                             Node::Directory { name: child_child_name, .. } => {
                                 *child_child_name = child_name.join(&child_child_name);
@@ -185,38 +185,74 @@ impl Node<LicenseId> {
             }
             Node::Empty => {}
             Node::File { .. } => {}
-            Node::FileGroup { .. } => {}
+            Node::Group { .. } => panic!("Group should not be present at this stage"),
         }
     }
 
     /// This pass groups multiple files in a directory with the same license into a single
-    /// "FileGroup", so that the license of all those files can be reported as a group.
+    /// "Group", so that the license of all those files can be reported as a group.
+    ///
+    /// This also merges directories *without exceptions*.
     ///
     /// Crucially this pass runs after collapse_in_licensed_directories, so the most common license
     /// will already be marked as the directory's license and won't be turned into a group.
-    fn merge_file_groups(&mut self) {
+    fn merge_groups(&mut self) {
+        #[derive(Default)]
+        struct Grouped {
+            files: Vec<PathBuf>,
+            directories: Vec<PathBuf>,
+        }
         match self {
             Node::Root { childs } | Node::Directory { childs, .. } => {
-                let mut grouped = BTreeMap::new();
+                let mut grouped: BTreeMap<LicenseId, Grouped> = BTreeMap::new();
 
                 for child in &mut *childs {
-                    child.merge_file_groups();
-                    if let Node::File { name, license } = child {
-                        grouped.entry(*license).or_insert_with(Vec::new).push(name.clone());
-                        *child = Node::Empty;
+                    child.merge_groups();
+                    match child {
+                        Node::Directory { name, childs, license: Some(license) } => {
+                            if childs.is_empty() {
+                                grouped
+                                    .entry(*license)
+                                    .or_insert_with(Grouped::default)
+                                    .directories
+                                    .push(name.clone());
+                                *child = Node::Empty;
+                            }
+                        }
+                        Node::File { name, license } => {
+                            grouped
+                                .entry(*license)
+                                .or_insert_with(Grouped::default)
+                                .files
+                                .push(name.clone());
+                            *child = Node::Empty;
+                        }
+                        _ => {}
                     }
                 }
 
-                for (license, mut names) in grouped.into_iter() {
-                    if names.len() == 1 {
-                        childs.push(Node::File { license, name: names.pop().unwrap() });
+                for (license, mut grouped) in grouped.into_iter() {
+                    if grouped.files.len() + grouped.directories.len() <= 1 {
+                        if let Some(name) = grouped.files.pop() {
+                            childs.push(Node::File { license, name });
+                        } else if let Some(name) = grouped.directories.pop() {
+                            childs.push(Node::Directory {
+                                name,
+                                childs: Vec::new(),
+                                license: Some(license),
+                            });
+                        }
                     } else {
-                        childs.push(Node::FileGroup { license, names });
+                        childs.push(Node::Group {
+                            license,
+                            files: grouped.files,
+                            directories: grouped.directories,
+                        });
                     }
                 }
             }
             Node::File { .. } => {}
-            Node::FileGroup { .. } => panic!("FileGroup should not be present at this stage"),
+            Node::Group { .. } => panic!("FileGroup should not be present at this stage"),
             Node::Empty => {}
         }
     }
@@ -231,7 +267,7 @@ impl Node<LicenseId> {
                 }
                 childs.retain(|child| !matches!(child, Node::Empty));
             }
-            Node::FileGroup { .. } => {}
+            Node::Group { .. } => {}
             Node::File { .. } => {}
             Node::Empty => {}
         }
@@ -278,16 +314,22 @@ pub(crate) fn expand_interned_licenses(
 ) -> Node<&License> {
     match node {
         Node::Root { childs } => Node::Root {
-            childs: childs.into_iter().map(|child| strip_interning(child, interner)).collect(),
+            childs: childs
+                .into_iter()
+                .map(|child| expand_interned_licenses(child, interner))
+                .collect(),
         },
         Node::Directory { name, childs, license } => Node::Directory {
-            childs: childs.into_iter().map(|child| strip_interning(child, interner)).collect(),
+            childs: childs
+                .into_iter()
+                .map(|child| expand_interned_licenses(child, interner))
+                .collect(),
             license: license.map(|license| interner.resolve(license)),
             name,
         },
         Node::File { name, license } => Node::File { name, license: interner.resolve(license) },
-        Node::FileGroup { names, license } => {
-            Node::FileGroup { names, license: interner.resolve(license) }
+        Node::Group { files, directories, license } => {
+            Node::Group { files, directories, license: interner.resolve(license) }
         }
         Node::Empty => Node::Empty,
     }
diff --git a/src/tools/generate-copyright/src/main.rs b/src/tools/generate-copyright/src/main.rs
index d172c9e157b..4d116c7da65 100644
--- a/src/tools/generate-copyright/src/main.rs
+++ b/src/tools/generate-copyright/src/main.rs
@@ -36,8 +36,8 @@ fn render_recursive(node: &Node, buffer: &mut Vec<u8>, depth: usize) -> Result<(
                 }
             }
         }
-        Node::FileGroup { names, license } => {
-            render_license(&prefix, names.iter(), license, buffer)?;
+        Node::Group { files, directories, license } => {
+            render_license(&prefix, directories.iter().chain(files.iter()), license, buffer)?;
         }
         Node::File { name, license } => {
             render_license(&prefix, std::iter::once(name), license, buffer)?;
@@ -76,7 +76,7 @@ pub(crate) enum Node {
     Root { childs: Vec<Node> },
     Directory { name: String, childs: Vec<Node>, license: License },
     File { name: String, license: License },
-    FileGroup { names: Vec<String>, license: License },
+    Group { files: Vec<String>, directories: Vec<String>, license: License },
 }
 
 #[derive(serde::Deserialize)]
diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs
index c0267956aab..a2caeb97297 100644
--- a/src/tools/miri/src/bin/miri.rs
+++ b/src/tools/miri/src/bin/miri.rs
@@ -109,11 +109,14 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls {
                 // an empty result if `tcx.sess.opts.output_types.should_codegen()` is false.
                 local_providers.exported_symbols = |tcx, cnum| {
                     assert_eq!(cnum, LOCAL_CRATE);
+                    let reachable_set = tcx.with_stable_hashing_context(|hcx| {
+                        tcx.reachable_set(()).to_sorted(&hcx, true)
+                    });
                     tcx.arena.alloc_from_iter(
                         // This is based on:
                         // https://github.com/rust-lang/rust/blob/2962e7c0089d5c136f4e9600b7abccfbbde4973d/compiler/rustc_codegen_ssa/src/back/symbol_export.rs#L62-L63
                         // https://github.com/rust-lang/rust/blob/2962e7c0089d5c136f4e9600b7abccfbbde4973d/compiler/rustc_codegen_ssa/src/back/symbol_export.rs#L174
-                        tcx.reachable_set(()).iter().filter_map(|&local_def_id| {
+                        reachable_set.into_iter().filter_map(|&local_def_id| {
                             // Do the same filtering that rustc does:
                             // https://github.com/rust-lang/rust/blob/2962e7c0089d5c136f4e9600b7abccfbbde4973d/compiler/rustc_codegen_ssa/src/back/symbol_export.rs#L84-L102
                             // Otherwise it may cause unexpected behaviours and ICEs
diff --git a/src/tools/miri/tests/pass/intrinsics-integer.rs b/src/tools/miri/tests/pass/intrinsics-integer.rs
index 546931f6ff8..13e7bd8e1b9 100644
--- a/src/tools/miri/tests/pass/intrinsics-integer.rs
+++ b/src/tools/miri/tests/pass/intrinsics-integer.rs
@@ -1,12 +1,5 @@
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
+// SPDX-License-Identifier: MIT OR Apache-2.0
+// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org)
 
 #![feature(core_intrinsics)]
 use std::intrinsics::*;
diff --git a/src/tools/miri/tests/pass/intrinsics-math.rs b/src/tools/miri/tests/pass/intrinsics-math.rs
index 5973f4cd197..9f2dc333f33 100644
--- a/src/tools/miri/tests/pass/intrinsics-math.rs
+++ b/src/tools/miri/tests/pass/intrinsics-math.rs
@@ -1,12 +1,5 @@
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
+// SPDX-License-Identifier: MIT OR Apache-2.0
+// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org)
 
 macro_rules! assert_approx_eq {
     ($a:expr, $b:expr) => {{
diff --git a/src/tools/miri/tests/pass/issues/issue-30530.rs b/src/tools/miri/tests/pass/issues/issue-30530.rs
index 472b42adaac..b50a43ffd83 100644
--- a/src/tools/miri/tests/pass/issues/issue-30530.rs
+++ b/src/tools/miri/tests/pass/issues/issue-30530.rs
@@ -1,12 +1,5 @@
-// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
+// SPDX-License-Identifier: MIT OR Apache-2.0
+// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org)
 
 // Regression test for Issue #30530: alloca's created for storing
 // intermediate scratch values during brace-less match arms need to be
diff --git a/src/tools/miri/tests/pass/tag-align-dyn-u64.rs b/src/tools/miri/tests/pass/tag-align-dyn-u64.rs
index 72211a8d3f3..81a43cc8bcc 100644
--- a/src/tools/miri/tests/pass/tag-align-dyn-u64.rs
+++ b/src/tools/miri/tests/pass/tag-align-dyn-u64.rs
@@ -1,12 +1,5 @@
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
+// SPDX-License-Identifier: MIT OR Apache-2.0
+// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org)
 
 use std::mem;
 
diff --git a/src/tools/rust-installer b/src/tools/rust-installer
deleted file mode 160000
-Subproject 9981e4d1ea6ac0992ff21be5514d4230dc77548
diff --git a/src/tools/rust-installer/.gitignore b/src/tools/rust-installer/.gitignore
new file mode 100644
index 00000000000..fb017f484b1
--- /dev/null
+++ b/src/tools/rust-installer/.gitignore
@@ -0,0 +1,5 @@
+*~
+tmp
+target/
+**/*.rs.bk
+Cargo.lock
diff --git a/src/tools/rust-installer/Cargo.toml b/src/tools/rust-installer/Cargo.toml
new file mode 100644
index 00000000000..38b81a1baac
--- /dev/null
+++ b/src/tools/rust-installer/Cargo.toml
@@ -0,0 +1,28 @@
+[package]
+authors = ["The Rust Project Developers"]
+name = "installer"
+version = "0.0.0"
+edition = "2018"
+
+[[bin]]
+doc = false
+name = "rust-installer"
+path = "src/main.rs"
+
+[dependencies]
+anyhow = "1.0.19"
+flate2 = "1.0.1"
+rayon = "1.0"
+tar = "0.4.13"
+walkdir = "2"
+xz2 = "0.1.4"
+num_cpus = "1"
+remove_dir_all = "0.5"
+
+[dependencies.clap]
+features = ["derive"]
+version = "3.1"
+
+[target."cfg(windows)".dependencies]
+lazy_static = "1"
+winapi = { version = "0.3", features = ["errhandlingapi", "handleapi", "ioapiset", "winerror", "winioctl", "winnt"] }
diff --git a/src/tools/rust-installer/README.md b/src/tools/rust-installer/README.md
new file mode 100644
index 00000000000..99d8e5ca4cf
--- /dev/null
+++ b/src/tools/rust-installer/README.md
@@ -0,0 +1,71 @@
+[![Build Status](https://travis-ci.org/rust-lang/rust-installer.svg?branch=master)](https://travis-ci.org/rust-lang/rust-installer)
+
+A generator for the install.sh script commonly used to install Rust in
+Unix environments. It is used By Rust, Cargo, and is intended to be
+used by a future combined installer of Rust + Cargo.
+
+# Usage
+
+```
+./gen-installer.sh --product-name=Rust \
+                   --rel-manifest-dir=rustlib \
+                   --success-message=Rust-is-ready-to-roll. \
+                   --image-dir=./install-image \
+                   --work-dir=./temp \
+                   --output-dir=./dist \
+                   --non-installed-overlay=./overlay \
+                   --package-name=rustc-nightly-i686-apple-darwin \
+                   --component-name=rustc \
+                   --legacy-manifest-dirs=rustlib \
+                   --bulk-dirs=share/doc
+```
+
+Or, to just generate the script.
+
+```
+./gen-install-script.sh --product-name=Rust \
+                        --rel-manifest-dir=rustlib \
+                        --success-message=Rust-is-ready-to-roll. \
+                        --output-script=install.sh \
+                        --legacy-manifest-dirs=rustlib
+```
+
+*Note: the dashes in `success-message` are converted to spaces. The
+script's argument handling is broken with spaces.*
+
+To combine installers.
+
+```
+./combine-installers.sh --product-name=Rust \
+                        --rel-manifest-dir=rustlib \
+                        --success-message=Rust-is-ready-to-roll. \
+                        --work-dir=./temp \
+                        --output-dir=./dist \
+                        --non-installed-overlay=./overlay \
+                        --package-name=rustc-nightly-i686-apple-darwin \
+                        --legacy-manifest-dirs=rustlib \
+                        --input-tarballs=./rustc.tar.gz,cargo.tar.gz
+```
+
+# Future work
+
+* Make install.sh not have to be customized, pull it's data from a
+  config file.
+* Be more resiliant to installation failures, particularly if the disk
+  is full.
+* Pre-install and post-uninstall scripts.
+* Allow components to depend on or contradict other components.
+* Sanity check that expected destination dirs (bin, lib, share exist)?
+* Add --docdir flag. Is there a standard name for this?
+* Remove empty directories on uninstall.
+* Detect mismatches in --prefix, --mandir, etc. in follow-on
+  installs/uninstalls.
+* Fix argument handling for spaces.
+* Add --bindir.
+
+# License
+
+This software is distributed under the terms of both the MIT license
+and/or the Apache License (Version 2.0), at your option.
+
+See [LICENSE-APACHE](LICENSE-APACHE), [LICENSE-MIT](LICENSE-MIT) for details.
diff --git a/src/tools/rust-installer/combine-installers.sh b/src/tools/rust-installer/combine-installers.sh
new file mode 100755
index 00000000000..bee5319fd55
--- /dev/null
+++ b/src/tools/rust-installer/combine-installers.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+set -ue
+
+# Prints the absolute path of a directory to stdout
+abs_path() {
+    local path="$1"
+    # Unset CDPATH because it causes havok: it makes the destination unpredictable
+    # and triggers 'cd' to print the path to stdout. Route `cd`'s output to /dev/null
+    # for good measure.
+    (unset CDPATH && cd "$path" > /dev/null && pwd)
+}
+
+src_dir="$(abs_path $(dirname "$0"))"
+$CARGO run --manifest-path="$src_dir/Cargo.toml" -- combine "$@"
diff --git a/src/tools/rust-installer/gen-install-script.sh b/src/tools/rust-installer/gen-install-script.sh
new file mode 100755
index 00000000000..f112fd4b21f
--- /dev/null
+++ b/src/tools/rust-installer/gen-install-script.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+set -ue
+
+# Prints the absolute path of a directory to stdout
+abs_path() {
+    local path="$1"
+    # Unset CDPATH because it causes havok: it makes the destination unpredictable
+    # and triggers 'cd' to print the path to stdout. Route `cd`'s output to /dev/null
+    # for good measure.
+    (unset CDPATH && cd "$path" > /dev/null && pwd)
+}
+
+src_dir="$(abs_path $(dirname "$0"))"
+cargo run --manifest-path="$src_dir/Cargo.toml" -- script "$@"
diff --git a/src/tools/rust-installer/gen-installer.sh b/src/tools/rust-installer/gen-installer.sh
new file mode 100755
index 00000000000..eabd8c95cd8
--- /dev/null
+++ b/src/tools/rust-installer/gen-installer.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+set -ue
+
+# Prints the absolute path of a directory to stdout
+abs_path() {
+    local path="$1"
+    # Unset CDPATH because it causes havok: it makes the destination unpredictable
+    # and triggers 'cd' to print the path to stdout. Route `cd`'s output to /dev/null
+    # for good measure.
+    (unset CDPATH && cd "$path" > /dev/null && pwd)
+}
+
+src_dir="$(abs_path $(dirname "$0"))"
+$CARGO run --manifest-path="$src_dir/Cargo.toml" -- generate "$@"
diff --git a/src/tools/rust-installer/install-template.sh b/src/tools/rust-installer/install-template.sh
new file mode 100644
index 00000000000..92a3f1f2c98
--- /dev/null
+++ b/src/tools/rust-installer/install-template.sh
@@ -0,0 +1,996 @@
+#!/bin/bash
+
+# No undefined variables
+set -u
+
+init_logging() {
+    local _abs_libdir="$1"
+    local _logfile="$_abs_libdir/$TEMPLATE_REL_MANIFEST_DIR/install.log"
+    rm -f "$_logfile"
+    need_ok "failed to remove old installation log"
+    touch "$_logfile"
+    need_ok "failed to create installation log"
+    LOGFILE="$_logfile"
+}
+
+log_line() {
+    local _line="$1"
+
+    if [ -n "${LOGFILE-}" -a -e "${LOGFILE-}" ]; then
+	echo "$_line" >> "$LOGFILE"
+	# Ignore errors, which may happen e.g. after the manifest dir is deleted
+    fi
+}
+
+msg() {
+    local _line="install: ${1-}"
+    echo "$_line"
+    log_line "$_line"
+}
+
+verbose_msg() {
+    if [ -n "${CFG_VERBOSE-}" ]; then
+	msg "${1-}"
+    else
+	log_line "install: ${1-}"
+    fi
+}
+
+step_msg() {
+    msg
+    msg "$1"
+    msg
+}
+
+verbose_step_msg() {
+    if [ -n "${CFG_VERBOSE-}" ]; then
+	msg
+	msg "$1"
+	msg
+    else
+	log_line ""
+	log_line "install: $1"
+	log_line ""
+    fi
+}
+
+warn() {
+    local _line="install: WARNING: $1"
+    echo "$_line" >&2
+    log_line "$_line"
+}
+
+err() {
+    local _line="install: error: $1"
+    echo "$_line" >&2
+    log_line "$_line"
+    exit 1
+}
+
+# A non-user error that is likely to result in a corrupted install
+critical_err() {
+    local _line="install: error: $1. see logs at '${LOGFILE-}'"
+    echo "$_line" >&2
+    log_line "$_line"
+    exit 1
+}
+
+need_ok() {
+    if [ $? -ne 0 ]
+    then
+        err "$1"
+    fi
+}
+
+critical_need_ok() {
+    if [ $? -ne 0 ]
+    then
+        critical_err "$1"
+    fi
+}
+
+want_ok() {
+    if [ $? -ne 0 ]; then
+	warn "$1"
+    fi
+}
+
+assert_nz() {
+    if [ -z "$1" ]; then err "assert_nz $2"; fi
+}
+
+need_cmd() {
+    if command -v $1 >/dev/null 2>&1
+    then verbose_msg "found $1"
+    else err "need $1"
+    fi
+}
+
+run() {
+    local _line="\$ $*"
+    "$@"
+    local _retval=$?
+    log_line "$_line"
+    return $_retval
+}
+
+write_to_file() {
+    local _msg="$1"
+    local _file="$2"
+    local _line="$ echo \"$_msg\" > \"$_file\""
+    echo "$_msg" > "$_file"
+    local _retval=$?
+    log_line "$_line"
+    return $_retval
+}
+
+append_to_file() {
+    local _msg="$1"
+    local _file="$2"
+    local _line="$ echo \"$_msg\" >> \"$_file\""
+    echo "$_msg" >> "$_file"
+    local _retval=$?
+    log_line "$_line"
+    return $_retval
+}
+
+make_dir_recursive() {
+    local _dir="$1"
+    local _line="$ umask 022 && mkdir -p \"$_dir\""
+    umask 022 && mkdir -p "$_dir"
+    local _retval=$?
+    log_line "$_line"
+    return $_retval
+}
+
+putvar() {
+    local t
+    local tlen
+    eval t=\$$1
+    eval tlen=\${#$1}
+}
+
+valopt() {
+    VAL_OPTIONS="$VAL_OPTIONS $1"
+
+    local op=$1
+    local default=$2
+    shift
+    shift
+    local doc="$*"
+    if [ $HELP -eq 0 ]
+    then
+        local uop=$(echo $op | tr 'a-z-' 'A-Z_')
+        local v="CFG_${uop}"
+        eval $v="$default"
+        for arg in $CFG_ARGS
+        do
+            if echo "$arg" | grep -q -- "--$op="
+            then
+                local val=$(echo "$arg" | cut -f2 -d=)
+                eval $v=$val
+            fi
+        done
+        putvar $v
+    else
+        if [ -z "$default" ]
+        then
+            default="<none>"
+        fi
+        op="${op}=[${default}]"
+        printf "    --%-30s %s\n" "$op" "$doc"
+    fi
+}
+
+opt() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local op=$1
+    local default=$2
+    shift
+    shift
+    local doc="$*"
+    local flag=""
+
+    if [ $default -eq 0 ]
+    then
+        flag="enable"
+    else
+        flag="disable"
+        doc="don't $doc"
+    fi
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${flag}-${op}" ]
+            then
+                op=$(echo $op | tr 'a-z-' 'A-Z_')
+                flag=$(echo $flag | tr 'a-z' 'A-Z')
+                local v="CFG_${flag}_${op}"
+                eval $v=1
+                putvar $v
+            fi
+        done
+    else
+        if [ ! -z "${META-}" ]
+        then
+            op="$op=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$flag-$op" "$doc"
+     fi
+}
+
+flag() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local op=$1
+    shift
+    local doc="$*"
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${op}" ]
+            then
+                op=$(echo $op | tr 'a-z-' 'A-Z_')
+                local v="CFG_${op}"
+                eval $v=1
+                putvar $v
+            fi
+        done
+    else
+        if [ ! -z "${META-}" ]
+        then
+            op="$op=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$op" "$doc"
+     fi
+}
+
+validate_opt () {
+    for arg in $CFG_ARGS
+    do
+        local is_arg_valid=0
+        for option in $BOOL_OPTIONS
+        do
+            if test --disable-$option = $arg
+            then
+                is_arg_valid=1
+            fi
+            if test --enable-$option = $arg
+            then
+                is_arg_valid=1
+            fi
+            if test --$option = $arg
+            then
+                is_arg_valid=1
+            fi
+        done
+        for option in $VAL_OPTIONS
+        do
+            if echo "$arg" | grep -q -- "--$option="
+            then
+                is_arg_valid=1
+            fi
+        done
+        if [ "$arg" = "--help" ]
+        then
+            echo
+            echo "No more help available for Configure options,"
+            echo "check the Wiki or join our IRC channel"
+            break
+        else
+            if test $is_arg_valid -eq 0
+            then
+                err "Option '$arg' is not recognized"
+            fi
+        fi
+    done
+}
+
+absolutify() {
+    local file_path="$1"
+    local file_path_dirname="$(dirname "$file_path")"
+    local file_path_basename="$(basename "$file_path")"
+    local file_abs_path="$(abs_path "$file_path_dirname")"
+    local file_path="$file_abs_path/$file_path_basename"
+    # This is the return value
+    RETVAL="$file_path"
+}
+
+# Prints the absolute path of a directory to stdout
+abs_path() {
+    local path="$1"
+    # Unset CDPATH because it causes havok: it makes the destination unpredictable
+    # and triggers 'cd' to print the path to stdout. Route `cd`'s output to /dev/null
+    # for good measure.
+    (unset CDPATH && cd "$path" > /dev/null && pwd)
+}
+
+uninstall_legacy() {
+    local _abs_libdir="$1"
+
+    local _uninstalled_something=false
+
+    # Replace commas in legacy manifest list with spaces
+    _legacy_manifest_dirs=`echo "$TEMPLATE_LEGACY_MANIFEST_DIRS" | sed "s/,/ /g"`
+
+    # Uninstall from legacy manifests
+    local _md
+    for _md in $_legacy_manifest_dirs; do
+	# First, uninstall from the installation prefix.
+	# Errors are warnings - try to rm everything in the manifest even if some fail.
+	if [ -f "$_abs_libdir/$_md/manifest" ]
+	then
+
+	    # iterate through installed manifest and remove files
+	    local _p;
+	    while read _p; do
+		# the installed manifest contains absolute paths
+		msg "removing legacy file $_p"
+		if [ -f "$_p" ]
+		then
+		    run rm -f "$_p"
+		    want_ok "failed to remove $_p"
+		else
+		    warn "supposedly installed file $_p does not exist!"
+		fi
+	    done < "$_abs_libdir/$_md/manifest"
+
+	    # If we fail to remove $md below, then the
+	    # installed manifest will still be full; the installed manifest
+	    # needs to be empty before install.
+	    msg "removing legacy manifest $_abs_libdir/$_md/manifest"
+	    run rm -f "$_abs_libdir/$_md/manifest"
+	    # For the above reason, this is a hard error
+	    need_ok "failed to remove installed manifest"
+
+	    # Remove $template_rel_manifest_dir directory
+	    msg "removing legacy manifest dir $_abs_libdir/$_md"
+	    run rm -R "$_abs_libdir/$_md"
+	    want_ok "failed to remove $_md"
+
+	    _uninstalled_something=true
+	fi
+    done
+
+    RETVAL="$_uninstalled_something"
+}
+
+uninstall_components() {
+    local _abs_libdir="$1"
+    local _dest_prefix="$2"
+    local _components="$3"
+
+    # We're going to start by uninstalling existing components. This
+    local _uninstalled_something=false
+
+    # First, try removing any 'legacy' manifests from before
+    # rust-installer
+    uninstall_legacy "$_abs_libdir"
+    assert_nz "$RETVAL", "RETVAL"
+    if [ "$RETVAL" = true ]; then
+	_uninstalled_something=true;
+    fi
+
+    # Load the version of the installed installer
+    local _installed_version=
+    if [ -f "$abs_libdir/$TEMPLATE_REL_MANIFEST_DIR/rust-installer-version" ]; then
+	_installed_version=`cat "$_abs_libdir/$TEMPLATE_REL_MANIFEST_DIR/rust-installer-version"`
+
+	# Sanity check
+	if [ ! -n "$_installed_version" ]; then critical_err "rust installer version is empty"; fi
+    fi
+
+    # If there's something installed, then uninstall
+    if [ -n "$_installed_version" ]; then
+	# Check the version of the installed installer
+	case "$_installed_version" in
+
+	    # If this is a previous version, then upgrade in place to the
+	    # current version before uninstalling.
+	    2 )
+		# The only change between version 2 -> 3 is that components are placed
+		# in subdirectories of the installer tarball. There are no changes
+		# to the installed data format, so nothing to do.
+		;;
+
+	    # This is the current version. Nothing need to be done except uninstall.
+	    "$TEMPLATE_RUST_INSTALLER_VERSION")
+		;;
+
+	    # If this is an unknown (future) version then bail.
+	    * )
+		echo "The copy of $TEMPLATE_PRODUCT_NAME at $_dest_prefix was installed using an"
+		echo "unknown version ($_installed_version) of rust-installer."
+		echo "Uninstall it first with the installer used for the original installation"
+		echo "before continuing."
+		exit 1
+		;;
+	esac
+
+	local _md="$_abs_libdir/$TEMPLATE_REL_MANIFEST_DIR"
+	local _installed_components="$(cat "$_md/components")"
+
+	# Uninstall (our components only) before reinstalling
+	local _available_component
+	for _available_component in $_components; do
+	    local _installed_component
+	    for _installed_component in $_installed_components; do
+		if [ "$_available_component" = "$_installed_component" ]; then
+		    msg "uninstalling component '$_available_component'"
+		    local _component_manifest="$_md/manifest-$_installed_component"
+
+		    # Sanity check: there should be a component manifest
+		    if [ ! -f "$_component_manifest" ]; then
+			critical_err "installed component '$_installed_component' has no manifest"
+		    fi
+
+		    # Iterate through installed component manifest and remove files
+		    local _directive
+		    while read _directive; do
+
+			local _command=`echo $_directive | cut -f1 -d:`
+			local _file=`echo $_directive | cut -f2 -d:`
+
+			# Sanity checks
+			if [ ! -n "$_command" ]; then critical_err "malformed installation directive"; fi
+			if [ ! -n "$_file" ]; then critical_err "malformed installation directive"; fi
+
+			case "$_command" in
+			    file)
+				verbose_msg "removing file $_file"
+				if [ -f "$_file" ]; then
+				    run rm -f "$_file"
+				    want_ok "failed to remove $_file"
+				else
+				    warn "supposedly installed file $_file does not exist!"
+				fi
+				;;
+
+			    dir)
+				verbose_msg "removing directory $_file"
+				run rm -r "$_file"
+				want_ok "unable to remove directory $_file"
+				;;
+
+			    *)
+				critical_err "unknown installation directive"
+				;;
+			esac
+
+		    done < "$_component_manifest"
+
+		    # Remove the installed component manifest
+		    verbose_msg "removing component manifest $_component_manifest"
+		    run rm "$_component_manifest"
+		    # This is a hard error because the installation is unrecoverable
+		    critical_need_ok "failed to remove installed manifest for component '$_installed_component'"
+
+		    # Update the installed component list
+		    local _modified_components="$(sed "/^$_installed_component\$/d" "$_md/components")"
+		    write_to_file "$_modified_components" "$_md/components"
+		    critical_need_ok "failed to update installed component list"
+		fi
+	    done
+	done
+
+	# If there are no remaining components delete the manifest directory,
+	# but only if we're doing an uninstall - if we're doing an install,
+	# then leave the manifest directory around to hang onto the logs,
+	# and any files not managed by the installer.
+	if [ -n "${CFG_UNINSTALL-}" ]; then
+	    local _remaining_components="$(cat "$_md/components")"
+	    if [ ! -n "$_remaining_components" ]; then
+		verbose_msg "removing manifest directory $_md"
+		run rm -r "$_md"
+		want_ok "failed to remove $_md"
+
+		maybe_unconfigure_ld
+	    fi
+	fi
+
+	_uninstalled_something=true
+    fi
+
+    # There's no installed version. If we were asked to uninstall, then that's a problem.
+    if [ -n "${CFG_UNINSTALL-}" -a "$_uninstalled_something" = false ]
+    then
+	err "unable to find installation manifest at $CFG_LIBDIR/$TEMPLATE_REL_MANIFEST_DIR"
+    fi
+}
+
+install_components() {
+    local _src_dir="$1"
+    local _abs_libdir="$2"
+    local _dest_prefix="$3"
+    local _components="$4"
+
+    local _component
+    for _component in $_components; do
+
+	msg "installing component '$_component'"
+
+	# The file name of the manifest we're installing from
+	local _input_manifest="$_src_dir/$_component/manifest.in"
+
+	# Sanity check: do we have our input manifests?
+	if [ ! -f "$_input_manifest" ]; then
+	    critical_err "manifest for $_component does not exist at $_input_manifest"
+	fi
+
+	# The installed manifest directory
+	local _md="$_abs_libdir/$TEMPLATE_REL_MANIFEST_DIR"
+
+	# The file name of the manifest we're going to create during install
+	local _installed_manifest="$_md/manifest-$_component"
+
+	# Create the installed manifest, which we will fill in with absolute file paths
+	touch "$_installed_manifest"
+	critical_need_ok "failed to create installed manifest"
+
+	# Add this component to the installed component list
+	append_to_file "$_component" "$_md/components"
+	critical_need_ok "failed to update components list for $_component"
+
+	# Now install, iterate through the new manifest and copy files
+	local _directive
+	while read _directive; do
+
+	    local _command=`echo $_directive | cut -f1 -d:`
+	    local _file=`echo $_directive | cut -f2 -d:`
+
+	    # Sanity checks
+	    if [ ! -n "$_command" ]; then critical_err "malformed installation directive"; fi
+	    if [ ! -n "$_file" ]; then critical_err "malformed installation directive"; fi
+
+	    # Decide the destination of the file
+	    local _file_install_path="$_dest_prefix/$_file"
+
+	    if echo "$_file" | grep "^etc/" > /dev/null
+	    then
+		local _f="$(echo "$_file" | sed 's/^etc\///')"
+		_file_install_path="$CFG_SYSCONFDIR/$_f"
+	    fi
+
+	    if echo "$_file" | grep "^bin/" > /dev/null
+	    then
+		local _f="$(echo "$_file" | sed 's/^bin\///')"
+		_file_install_path="$CFG_BINDIR/$_f"
+	    fi
+
+	    if echo "$_file" | grep "^lib/" > /dev/null
+	    then
+		local _f="$(echo "$_file" | sed 's/^lib\///')"
+		_file_install_path="$CFG_LIBDIR/$_f"
+	    fi
+
+	    if echo "$_file" | grep "^share" > /dev/null
+	    then
+		local _f="$(echo "$_file" | sed 's/^share\///')"
+		_file_install_path="$CFG_DATADIR/$_f"
+	    fi
+
+	    if echo "$_file" | grep "^share/man/" > /dev/null
+	    then
+		local _f="$(echo "$_file" | sed 's/^share\/man\///')"
+		_file_install_path="$CFG_MANDIR/$_f"
+	    fi
+
+            # HACK: Try to support overriding --docdir.  Paths with the form
+            # "share/doc/$product/" can be redirected to a single --docdir
+            # path. If the following detects that --docdir has been specified
+            # then it will replace everything preceeding the "$product" path
+            # component. The problem here is that the combined rust installer
+            # contains two "products": rust and cargo; so the contents of those
+            # directories will both be dumped into the same directory; and the
+            # contents of those directories are _not_ disjoint. Since this feature
+            # is almost entirely to support 'make install' anyway I don't expect
+            # this problem to be a big deal in practice.
+            if [ "$CFG_DOCDIR" != "<default>" ]
+            then
+	        if echo "$_file" | grep "^share/doc/" > /dev/null
+	        then
+		    local _f="$(echo "$_file" | sed 's/^share\/doc\/[^/]*\///')"
+		    _file_install_path="$CFG_DOCDIR/$_f"
+	        fi
+            fi
+
+	    # Make sure there's a directory for it
+	    make_dir_recursive "$(dirname "$_file_install_path")"
+	    critical_need_ok "directory creation failed"
+
+	    # Make the path absolute so we can uninstall it later without
+	    # starting from the installation cwd
+	    absolutify "$_file_install_path"
+	    _file_install_path="$RETVAL"
+	    assert_nz "$_file_install_path" "file_install_path"
+
+	    case "$_command" in
+		file )
+
+		    verbose_msg "copying file $_file_install_path"
+
+		    maybe_backup_path "$_file_install_path"
+
+		    if echo "$_file" | grep "^bin/" > /dev/null || test -x "$_src_dir/$_component/$_file"
+		    then
+			run cp "$_src_dir/$_component/$_file" "$_file_install_path"
+			run chmod 755 "$_file_install_path"
+		    else
+			run cp "$_src_dir/$_component/$_file" "$_file_install_path"
+			run chmod 644 "$_file_install_path"
+		    fi
+		    critical_need_ok "file creation failed"
+
+		    # Update the manifest
+		    append_to_file "file:$_file_install_path" "$_installed_manifest"
+		    critical_need_ok "failed to update manifest"
+
+		    ;;
+
+		dir )
+
+		    verbose_msg "copying directory $_file_install_path"
+
+		    maybe_backup_path "$_file_install_path"
+
+		    run cp -R "$_src_dir/$_component/$_file" "$_file_install_path"
+		    critical_need_ok "failed to copy directory"
+
+                    # Set permissions. 0755 for dirs, 644 for files
+                    run chmod -R u+rwX,go+rX,go-w "$_file_install_path"
+                    critical_need_ok "failed to set permissions on directory"
+
+		    # Update the manifest
+		    append_to_file "dir:$_file_install_path" "$_installed_manifest"
+		    critical_need_ok "failed to update manifest"
+		    ;;
+
+		*)
+		    critical_err "unknown installation directive"
+		    ;;
+	    esac
+	done < "$_input_manifest"
+
+    done
+}
+
+maybe_configure_ld() {
+    local _abs_libdir="$1"
+
+    local _ostype="$(uname -s)"
+    assert_nz "$_ostype"  "ostype"
+
+    if [ "$_ostype" = "Linux" -a ! -n "${CFG_DISABLE_LDCONFIG-}" ]; then
+
+	# Fedora-based systems do not configure the dynamic linker to look
+	# /usr/local/lib, which is our default installation directory. To
+	# make things just work, try to put that directory in
+	# /etc/ld.so.conf.d/rust-installer-v1 so ldconfig picks it up.
+	# Issue #30.
+	#
+	# This will get rm'd when the last component is uninstalled in
+	# maybe_unconfigure_ld.
+	if [ "$_abs_libdir" = "/usr/local/lib" -a -d "/etc/ld.so.conf.d" ]; then
+	    echo "$_abs_libdir" > "/etc/ld.so.conf.d/rust-installer-v1-$TEMPLATE_REL_MANIFEST_DIR.conf"
+	    if [ $? -ne 0 ]; then
+		# This shouldn't happen if we've gotten this far
+		# installing to /usr/local
+		warn "failed to update /etc/ld.so.conf.d. this is unexpected"
+	    fi
+	fi
+
+	verbose_msg "running ldconfig"
+	if [ -n "${CFG_VERBOSE-}" ]; then
+	    ldconfig
+	else
+	    ldconfig 2> /dev/null
+	fi
+	if [ $? -ne 0 ]
+	then
+            warn "failed to run ldconfig. this may happen when not installing as root. run with --verbose to see the error"
+	fi
+    fi
+}
+
+maybe_unconfigure_ld() {
+    local _ostype="$(uname -s)"
+    assert_nz "$_ostype"  "ostype"
+
+    if [ "$_ostype" != "Linux" ]; then
+	return 0
+    fi
+
+    rm "/etc/ld.so.conf.d/rust-installer-v1-$TEMPLATE_REL_MANIFEST_DIR.conf" 2> /dev/null
+    # Above may fail since that file may not have been created on install
+}
+
+# Doing our own 'install'-like backup that is consistent across platforms
+maybe_backup_path() {
+    local _file_install_path="$1"
+
+    if [ -e "$_file_install_path" ]; then
+	msg "backing up existing file at $_file_install_path"
+	run mv -f "$_file_install_path" "$_file_install_path.old"
+	critical_need_ok "failed to back up $_file_install_path"
+    fi
+}
+
+install_uninstaller() {
+    local _src_dir="$1"
+    local _src_basename="$2"
+    local _abs_libdir="$3"
+
+    local _uninstaller="$_abs_libdir/$TEMPLATE_REL_MANIFEST_DIR/uninstall.sh"
+    msg "creating uninstall script at $_uninstaller"
+    run cp "$_src_dir/$_src_basename" "$_uninstaller"
+    critical_need_ok "unable to install uninstaller"
+}
+
+do_preflight_sanity_checks() {
+    local _src_dir="$1"
+    local _dest_prefix="$2"
+
+    # Sanity check: can we can write to the destination?
+    verbose_msg "verifying destination is writable"
+    make_dir_recursive "$CFG_LIBDIR"
+    need_ok "can't write to destination. consider \`sudo\`."
+    touch "$CFG_LIBDIR/rust-install-probe" > /dev/null
+    if [ $? -ne 0 ]
+    then
+	err "can't write to destination. consider \`sudo\`."
+    fi
+    rm "$CFG_LIBDIR/rust-install-probe"
+    need_ok "failed to remove install probe"
+
+    # Sanity check: don't install to the directory containing the installer.
+    # That would surely cause chaos.
+    verbose_msg "verifying destination is not the same as source"
+    local _prefix_dir="$(abs_path "$dest_prefix")"
+    if [ "$_src_dir" = "$_dest_prefix" -a "${CFG_UNINSTALL-}" != 1 ]; then
+	err "cannot install to same directory as installer"
+    fi
+}
+
+verbose_msg "looking for install programs"
+verbose_msg
+
+need_cmd mkdir
+need_cmd printf
+need_cmd cut
+need_cmd grep
+need_cmd uname
+need_cmd tr
+need_cmd sed
+need_cmd chmod
+need_cmd env
+need_cmd pwd
+
+CFG_ARGS="${@:-}"
+
+HELP=0
+if [ "${1-}" = "--help" ]
+then
+    HELP=1
+    shift
+    echo
+    echo "Usage: $0 [options]"
+    echo
+    echo "Options:"
+    echo
+else
+    verbose_step_msg "processing arguments"
+fi
+
+OPTIONS=""
+BOOL_OPTIONS=""
+VAL_OPTIONS=""
+
+flag uninstall "only uninstall from the installation prefix"
+valopt destdir "" "set installation root"
+valopt prefix "/usr/local" "set installation prefix"
+
+# Avoid prepending an extra / to the prefix path if there's no destdir
+# NB: CFG vars here are undefined when passing --help
+if [ -z "${CFG_DESTDIR-}" ]; then
+    CFG_DESTDIR_PREFIX="${CFG_PREFIX-}"
+else
+    CFG_DESTDIR_PREFIX="$CFG_DESTDIR/$CFG_PREFIX"
+fi
+
+# NB This isn't quite the same definition as in `configure`.
+# just using 'lib' instead of configure's CFG_LIBDIR_RELATIVE
+valopt without "" "comma-separated list of components to not install"
+valopt components "" "comma-separated list of components to install"
+flag list-components "list available components"
+valopt sysconfdir "$CFG_DESTDIR_PREFIX/etc" "install system configuration files"
+valopt bindir "$CFG_DESTDIR_PREFIX/bin" "install binaries"
+valopt libdir "$CFG_DESTDIR_PREFIX/lib" "install libraries"
+valopt datadir "$CFG_DESTDIR_PREFIX/share" "install data"
+# NB We repeat datadir default value because we don't set CFG_DATADIR in --help
+valopt mandir "${CFG_DATADIR-"$CFG_DESTDIR_PREFIX/share"}/man" "install man pages in PATH"
+# NB See the docdir handling in install_components for an explanation of this
+# weird <default> string
+valopt docdir "\<default\>" "install documentation in PATH"
+opt ldconfig 1 "run ldconfig after installation (Linux only)"
+opt verify 1 "obsolete"
+flag verbose "run with verbose output"
+
+if [ $HELP -eq 1 ]
+then
+    echo
+    exit 0
+fi
+
+verbose_step_msg "validating arguments"
+validate_opt
+
+# Template configuration.
+# These names surrounded by '%%` are replaced by sed when generating install.sh
+# FIXME: Might want to consider loading this from a file and not generating install.sh
+
+# Rust or Cargo
+TEMPLATE_PRODUCT_NAME=%%TEMPLATE_PRODUCT_NAME%%
+# rustlib or cargo
+TEMPLATE_REL_MANIFEST_DIR=%%TEMPLATE_REL_MANIFEST_DIR%%
+# 'Rust is ready to roll.' or 'Cargo is cool to cruise.'
+TEMPLATE_SUCCESS_MESSAGE=%%TEMPLATE_SUCCESS_MESSAGE%%
+# Locations to look for directories containing legacy, pre-versioned manifests
+TEMPLATE_LEGACY_MANIFEST_DIRS=%%TEMPLATE_LEGACY_MANIFEST_DIRS%%
+# The installer version
+TEMPLATE_RUST_INSTALLER_VERSION=%%TEMPLATE_RUST_INSTALLER_VERSION%%
+
+# OK, let's get installing ...
+
+# This is where we are installing from
+src_dir="$(abs_path $(dirname "$0"))"
+
+# The name of the script
+src_basename="$(basename "$0")"
+
+# If we've been run as 'uninstall.sh' (from the existing installation)
+# then we're doing a full uninstall, as opposed to the --uninstall flag
+# which just means 'uninstall my components'.
+if [ "$src_basename" = "uninstall.sh" ]; then
+    if [ "${*:-}" != "" ]; then
+	# Currently don't know what to do with arguments in this mode
+	err "uninstall.sh does not take any arguments"
+    fi
+    CFG_UNINSTALL=1
+    CFG_DESTDIR_PREFIX="$(abs_path "$src_dir/../../")"
+    CFG_LIBDIR="$(abs_path "$src_dir/../")"
+fi
+
+# This is where we are installing to
+dest_prefix="$CFG_DESTDIR_PREFIX"
+
+# Open the components file to get the list of components to install.
+# NB: During install this components file is read from the installer's
+# source dir, during a full uninstall it's read from the manifest dir,
+# and thus contains all installed components.
+components=`cat "$src_dir/components"`
+
+# Sanity check: do we have components?
+if [ ! -n "$components" ]; then
+    err "unable to find installation components"
+fi
+
+# If the user asked for a component list, do that and exit
+if [ -n "${CFG_LIST_COMPONENTS-}" ]; then
+    echo
+    echo "# Available components"
+    echo
+    for component in $components; do
+	echo "* $component"
+    done
+    echo
+    exit 0
+fi
+
+# If the user specified which components to install/uninstall,
+# then validate that they exist and select them for installation
+if [ -n "$CFG_COMPONENTS" ]; then
+    # Remove commas
+    user_components="$(echo "$CFG_COMPONENTS" | sed "s/,/ /g")"
+    for user_component in $user_components; do
+	found=false
+	for my_component in $components; do
+	    if [ "$user_component" = "$my_component" ]; then
+		found=true
+	    fi
+	done
+	if [ "$found" = false ]; then
+	    err "unknown component: $user_component"
+	fi
+    done
+    components="$user_components"
+fi
+
+if [ -n "$CFG_WITHOUT" ]; then
+    without_components="$(echo "$CFG_WITHOUT" | sed "s/,/ /g")"
+
+    # This does **not** check that all components in without_components are
+    # actually present in the list of available components.
+    #
+    # Currently that's considered good as it makes it easier to be compatible
+    # with multiple Rust versions (which may change the exact list of
+    # components) when writing install scripts.
+    new_comp=""
+    for component in $components; do
+        found=false
+        for my_component in $without_components; do
+            if [ "$component" = "$my_component" ]; then
+                found=true
+            fi
+        done
+        if [ "$found" = false ]; then
+            # If we didn't find the component in without, then add it to new list.
+            new_comp="$new_comp $component"
+        fi
+    done
+    components="$new_comp"
+fi
+
+if [ -z "$components" ]; then
+    if [ -z "${CFG_UNINSTALL-}" ]; then
+	err "no components selected for installation"
+    else
+	err "no components selected for uninstallation"
+    fi
+fi
+
+do_preflight_sanity_checks "$src_dir" "$dest_prefix"
+
+# Using an absolute path to libdir in a few places so that the status
+# messages are consistently using absolute paths.
+absolutify "$CFG_LIBDIR"
+abs_libdir="$RETVAL"
+assert_nz "$abs_libdir" "abs_libdir"
+
+# Create the manifest directory, where we will put our logs
+make_dir_recursive "$abs_libdir/$TEMPLATE_REL_MANIFEST_DIR"
+need_ok "failed to create $TEMPLATE_REL_MANIFEST_DIR"
+
+# Log messages and commands
+init_logging "$abs_libdir"
+
+# First do any uninstallation, including from legacy manifests. This
+# will also upgrade the metadata of existing installs.
+uninstall_components "$abs_libdir" "$dest_prefix" "$components"
+
+# If we're only uninstalling then exit
+if [ -n "${CFG_UNINSTALL-}" ]
+then
+    echo
+    echo "    $TEMPLATE_PRODUCT_NAME is uninstalled."
+    echo
+    exit 0
+fi
+
+# Create the manifest directory again! uninstall_legacy
+# may have deleted it.
+make_dir_recursive "$abs_libdir/$TEMPLATE_REL_MANIFEST_DIR"
+need_ok "failed to create $TEMPLATE_REL_MANIFEST_DIR"
+
+# Drop the version number into the manifest dir
+write_to_file "$TEMPLATE_RUST_INSTALLER_VERSION" "$abs_libdir/$TEMPLATE_REL_MANIFEST_DIR/rust-installer-version"
+critical_need_ok "failed to write installer version"
+
+# Install the uninstaller
+install_uninstaller "$src_dir" "$src_basename" "$abs_libdir"
+
+# Install each component
+install_components "$src_dir" "$abs_libdir" "$dest_prefix" "$components"
+
+# Make dynamic libraries available to the linker
+maybe_configure_ld "$abs_libdir"
+
+echo
+echo "    $TEMPLATE_SUCCESS_MESSAGE"
+echo
+
+
diff --git a/src/tools/rust-installer/make-tarballs.sh b/src/tools/rust-installer/make-tarballs.sh
new file mode 100755
index 00000000000..e342007da37
--- /dev/null
+++ b/src/tools/rust-installer/make-tarballs.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+set -ue
+
+# Prints the absolute path of a directory to stdout
+abs_path() {
+    local path="$1"
+    # Unset CDPATH because it causes havok: it makes the destination unpredictable
+    # and triggers 'cd' to print the path to stdout. Route `cd`'s output to /dev/null
+    # for good measure.
+    (unset CDPATH && cd "$path" > /dev/null && pwd)
+}
+
+src_dir="$(abs_path $(dirname "$0"))"
+$CARGO run --manifest-path="$src_dir/Cargo.toml" -- tarball "$@"
diff --git a/src/tools/rust-installer/rust-installer-version b/src/tools/rust-installer/rust-installer-version
new file mode 100644
index 00000000000..e440e5c8425
--- /dev/null
+++ b/src/tools/rust-installer/rust-installer-version
@@ -0,0 +1 @@
+3
\ No newline at end of file
diff --git a/src/tools/rust-installer/src/combiner.rs b/src/tools/rust-installer/src/combiner.rs
new file mode 100644
index 00000000000..2ec09d67e3e
--- /dev/null
+++ b/src/tools/rust-installer/src/combiner.rs
@@ -0,0 +1,161 @@
+use super::Scripter;
+use super::Tarballer;
+use crate::{
+    compression::{CompressionFormat, CompressionFormats},
+    util::*,
+};
+use anyhow::{bail, Context, Result};
+use std::io::{Read, Write};
+use std::path::Path;
+use tar::Archive;
+
+actor! {
+    #[derive(Debug)]
+    pub struct Combiner {
+        /// The name of the product, for display.
+        #[clap(value_name = "NAME")]
+        product_name: String = "Product",
+
+        /// The name of the package  tarball.
+        #[clap(value_name = "NAME")]
+        package_name: String = "package",
+
+        /// The directory under lib/ where the manifest lives.
+        #[clap(value_name = "DIR")]
+        rel_manifest_dir: String = "packagelib",
+
+        /// The string to print after successful installation.
+        #[clap(value_name = "MESSAGE")]
+        success_message: String = "Installed.",
+
+        /// Places to look for legacy manifests to uninstall.
+        #[clap(value_name = "DIRS")]
+        legacy_manifest_dirs: String = "",
+
+        /// Installers to combine.
+        #[clap(value_name = "FILE,FILE")]
+        input_tarballs: String = "",
+
+        /// Directory containing files that should not be installed.
+        #[clap(value_name = "DIR")]
+        non_installed_overlay: String = "",
+
+        /// The directory to do temporary work.
+        #[clap(value_name = "DIR")]
+        work_dir: String = "./workdir",
+
+        /// The location to put the final image and tarball.
+        #[clap(value_name = "DIR")]
+        output_dir: String = "./dist",
+
+        /// The formats used to compress the tarball
+        #[clap(value_name = "FORMAT", default_value_t)]
+        compression_formats: CompressionFormats,
+    }
+}
+
+impl Combiner {
+    /// Combines the installer tarballs.
+    pub fn run(self) -> Result<()> {
+        create_dir_all(&self.work_dir)?;
+
+        let package_dir = Path::new(&self.work_dir).join(&self.package_name);
+        if package_dir.exists() {
+            remove_dir_all(&package_dir)?;
+        }
+        create_dir_all(&package_dir)?;
+
+        // Merge each installer into the work directory of the new installer.
+        let components = create_new_file(package_dir.join("components"))?;
+        for input_tarball in self
+            .input_tarballs
+            .split(',')
+            .map(str::trim)
+            .filter(|s| !s.is_empty())
+        {
+            // Extract the input tarballs
+            let compression =
+                CompressionFormat::detect_from_path(input_tarball).ok_or_else(|| {
+                    anyhow::anyhow!("couldn't figure out the format of {}", input_tarball)
+                })?;
+            Archive::new(compression.decode(input_tarball)?)
+                .unpack(&self.work_dir)
+                .with_context(|| {
+                    format!(
+                        "unable to extract '{}' into '{}'",
+                        &input_tarball, self.work_dir
+                    )
+                })?;
+
+            let pkg_name =
+                input_tarball.trim_end_matches(&format!(".tar.{}", compression.extension()));
+            let pkg_name = Path::new(pkg_name).file_name().unwrap();
+            let pkg_dir = Path::new(&self.work_dir).join(&pkg_name);
+
+            // Verify the version number.
+            let mut version = String::new();
+            open_file(pkg_dir.join("rust-installer-version"))
+                .and_then(|mut file| Ok(file.read_to_string(&mut version)?))
+                .with_context(|| format!("failed to read version in '{}'", input_tarball))?;
+            if version.trim().parse() != Ok(crate::RUST_INSTALLER_VERSION) {
+                bail!("incorrect installer version in {}", input_tarball);
+            }
+
+            // Copy components to the new combined installer.
+            let mut pkg_components = String::new();
+            open_file(pkg_dir.join("components"))
+                .and_then(|mut file| Ok(file.read_to_string(&mut pkg_components)?))
+                .with_context(|| format!("failed to read components in '{}'", input_tarball))?;
+            for component in pkg_components.split_whitespace() {
+                // All we need to do is copy the component directory. We could
+                // move it, but rustbuild wants to reuse the unpacked package
+                // dir for OS-specific installers on macOS and Windows.
+                let component_dir = package_dir.join(&component);
+                create_dir(&component_dir)?;
+                copy_recursive(&pkg_dir.join(&component), &component_dir)?;
+
+                // Merge the component name.
+                writeln!(&components, "{}", component).context("failed to write new components")?;
+            }
+        }
+        drop(components);
+
+        // Write the installer version.
+        let version = package_dir.join("rust-installer-version");
+        writeln!(
+            create_new_file(version)?,
+            "{}",
+            crate::RUST_INSTALLER_VERSION
+        )
+        .context("failed to write new installer version")?;
+
+        // Copy the overlay.
+        if !self.non_installed_overlay.is_empty() {
+            copy_recursive(self.non_installed_overlay.as_ref(), &package_dir)?;
+        }
+
+        // Generate the install script.
+        let output_script = package_dir.join("install.sh");
+        let mut scripter = Scripter::default();
+        scripter
+            .product_name(self.product_name)
+            .rel_manifest_dir(self.rel_manifest_dir)
+            .success_message(self.success_message)
+            .legacy_manifest_dirs(self.legacy_manifest_dirs)
+            .output_script(path_to_str(&output_script)?.into());
+        scripter.run()?;
+
+        // Make the tarballs.
+        create_dir_all(&self.output_dir)?;
+        let output = Path::new(&self.output_dir).join(&self.package_name);
+        let mut tarballer = Tarballer::default();
+        tarballer
+            .work_dir(self.work_dir)
+            .input(self.package_name)
+            .output(path_to_str(&output)?.into())
+            .compression_formats(self.compression_formats.clone());
+        tarballer.run()?;
+
+        Ok(())
+    }
+}
diff --git a/src/tools/rust-installer/src/compression.rs b/src/tools/rust-installer/src/compression.rs
new file mode 100644
index 00000000000..013e05fda58
--- /dev/null
+++ b/src/tools/rust-installer/src/compression.rs
@@ -0,0 +1,214 @@
+use anyhow::{Context, Error};
+use flate2::{read::GzDecoder, write::GzEncoder};
+use rayon::prelude::*;
+use std::{convert::TryFrom, fmt, io::Read, io::Write, path::Path, str::FromStr};
+use xz2::{read::XzDecoder, write::XzEncoder};
+
+#[derive(Debug, Copy, Clone)]
+pub enum CompressionFormat {
+    Gz,
+    Xz,
+}
+
+impl CompressionFormat {
+    pub(crate) fn detect_from_path(path: impl AsRef<Path>) -> Option<Self> {
+        match path.as_ref().extension().and_then(|e| e.to_str()) {
+            Some("gz") => Some(CompressionFormat::Gz),
+            Some("xz") => Some(CompressionFormat::Xz),
+            _ => None,
+        }
+    }
+
+    pub(crate) fn extension(&self) -> &'static str {
+        match self {
+            CompressionFormat::Gz => "gz",
+            CompressionFormat::Xz => "xz",
+        }
+    }
+
+    pub(crate) fn encode(&self, path: impl AsRef<Path>) -> Result<Box<dyn Encoder>, Error> {
+        let mut os = path.as_ref().as_os_str().to_os_string();
+        os.push(format!(".{}", self.extension()));
+        let path = Path::new(&os);
+
+        if path.exists() {
+            crate::util::remove_file(path)?;
+        }
+        let file = crate::util::create_new_file(path)?;
+
+        Ok(match self {
+            CompressionFormat::Gz => Box::new(GzEncoder::new(file, flate2::Compression::best())),
+            CompressionFormat::Xz => {
+                let mut filters = xz2::stream::Filters::new();
+                // the preset is overridden by the other options so it doesn't matter
+                let mut lzma_ops = xz2::stream::LzmaOptions::new_preset(9).unwrap();
+                // This sets the overall dictionary size, which is also how much memory (baseline)
+                // is needed for decompression.
+                lzma_ops.dict_size(64 * 1024 * 1024);
+                // Use the best match finder for compression ratio.
+                lzma_ops.match_finder(xz2::stream::MatchFinder::BinaryTree4);
+                lzma_ops.mode(xz2::stream::Mode::Normal);
+                // Set nice len to the maximum for best compression ratio
+                lzma_ops.nice_len(273);
+                // Set depth to a reasonable value, 0 means auto, 1000 is somwhat high but gives
+                // good results.
+                lzma_ops.depth(1000);
+                // 2 is the default and does well for most files
+                lzma_ops.position_bits(2);
+                // 0 is the default and does well for most files
+                lzma_ops.literal_position_bits(0);
+                // 3 is the default and does well for most files
+                lzma_ops.literal_context_bits(3);
+
+                filters.lzma2(&lzma_ops);
+
+                let mut builder = xz2::stream::MtStreamBuilder::new();
+                builder.filters(filters);
+
+                // On 32-bit platforms limit ourselves to 3 threads, otherwise we exceed memory
+                // usage this process can take. In the future we'll likely only do super-fast
+                // compression in CI and move this heavyweight processing to promote-release (which
+                // is always 64-bit and can run on big-memory machines) but for now this lets us
+                // move forward.
+                if std::mem::size_of::<usize>() == 4 {
+                    builder.threads(3);
+                } else {
+                    builder.threads(6);
+                }
+
+                let compressor = XzEncoder::new_stream(
+                    std::io::BufWriter::new(file),
+                    builder.encoder().unwrap(),
+                );
+                Box::new(compressor)
+            }
+        })
+    }
+
+    pub(crate) fn decode(&self, path: impl AsRef<Path>) -> Result<Box<dyn Read>, Error> {
+        let file = crate::util::open_file(path.as_ref())?;
+        Ok(match self {
+            CompressionFormat::Gz => Box::new(GzDecoder::new(file)),
+            CompressionFormat::Xz => Box::new(XzDecoder::new(file)),
+        })
+    }
+}
+
+/// This struct wraps Vec<CompressionFormat> in order to parse the value from the command line.
+#[derive(Debug, Clone)]
+pub struct CompressionFormats(Vec<CompressionFormat>);
+
+impl TryFrom<&'_ str> for CompressionFormats {
+    type Error = Error;
+
+    fn try_from(value: &str) -> Result<Self, Self::Error> {
+        let mut parsed = Vec::new();
+        for format in value.split(',') {
+            match format.trim() {
+                "gz" => parsed.push(CompressionFormat::Gz),
+                "xz" => parsed.push(CompressionFormat::Xz),
+                other => anyhow::bail!("unknown compression format: {}", other),
+            }
+        }
+        Ok(CompressionFormats(parsed))
+    }
+}
+
+impl FromStr for CompressionFormats {
+    type Err = Error;
+
+    fn from_str(value: &str) -> Result<Self, Self::Err> {
+        Self::try_from(value)
+    }
+}
+
+impl fmt::Display for CompressionFormats {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        for (i, format) in self.iter().enumerate() {
+            if i != 0 {
+                write!(f, ",")?;
+            }
+            fmt::Display::fmt(
+                match format {
+                    CompressionFormat::Xz => "xz",
+                    CompressionFormat::Gz => "gz",
+                },
+                f,
+            )?;
+        }
+        Ok(())
+    }
+}
+
+impl Default for CompressionFormats {
+    fn default() -> Self {
+        Self(vec![CompressionFormat::Gz, CompressionFormat::Xz])
+    }
+}
+
+impl CompressionFormats {
+    pub(crate) fn iter(&self) -> impl Iterator<Item = CompressionFormat> + '_ {
+        self.0.iter().map(|i| *i)
+    }
+}
+
+pub(crate) trait Encoder: Send + Write {
+    fn finish(self: Box<Self>) -> Result<(), Error>;
+}
+
+impl<W: Send + Write> Encoder for GzEncoder<W> {
+    fn finish(self: Box<Self>) -> Result<(), Error> {
+        GzEncoder::finish(*self).context("failed to finish .gz file")?;
+        Ok(())
+    }
+}
+
+impl<W: Send + Write> Encoder for XzEncoder<W> {
+    fn finish(self: Box<Self>) -> Result<(), Error> {
+        XzEncoder::finish(*self).context("failed to finish .xz file")?;
+        Ok(())
+    }
+}
+
+pub(crate) struct CombinedEncoder {
+    encoders: Vec<Box<dyn Encoder>>,
+}
+
+impl CombinedEncoder {
+    pub(crate) fn new(encoders: Vec<Box<dyn Encoder>>) -> Box<dyn Encoder> {
+        Box::new(Self { encoders })
+    }
+}
+
+impl Write for CombinedEncoder {
+    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+        self.write_all(buf)?;
+        Ok(buf.len())
+    }
+
+    fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
+        self.encoders
+            .par_iter_mut()
+            .map(|w| w.write_all(buf))
+            .collect::<std::io::Result<Vec<()>>>()?;
+        Ok(())
+    }
+
+    fn flush(&mut self) -> std::io::Result<()> {
+        self.encoders
+            .par_iter_mut()
+            .map(|w| w.flush())
+            .collect::<std::io::Result<Vec<()>>>()?;
+        Ok(())
+    }
+}
+
+impl Encoder for CombinedEncoder {
+    fn finish(self: Box<Self>) -> Result<(), Error> {
+        self.encoders
+            .into_par_iter()
+            .map(|e| e.finish())
+            .collect::<Result<Vec<()>, Error>>()?;
+        Ok(())
+    }
+}
diff --git a/src/tools/rust-installer/src/generator.rs b/src/tools/rust-installer/src/generator.rs
new file mode 100644
index 00000000000..1e4d00b0553
--- /dev/null
+++ b/src/tools/rust-installer/src/generator.rs
@@ -0,0 +1,178 @@
+use super::Scripter;
+use super::Tarballer;
+use crate::compression::CompressionFormats;
+use crate::util::*;
+use anyhow::{bail, format_err, Context, Result};
+use std::collections::BTreeSet;
+use std::io::Write;
+use std::path::Path;
+
+actor! {
+    #[derive(Debug)]
+    pub struct Generator {
+        /// The name of the product, for display
+        #[clap(value_name = "NAME")]
+        product_name: String = "Product",
+
+        /// The name of the component, distinct from other installed components
+        #[clap(value_name = "NAME")]
+        component_name: String = "component",
+
+        /// The name of the package, tarball
+        #[clap(value_name = "NAME")]
+        package_name: String = "package",
+
+        /// The directory under lib/ where the manifest lives
+        #[clap(value_name = "DIR")]
+        rel_manifest_dir: String = "packagelib",
+
+        /// The string to print after successful installation
+        #[clap(value_name = "MESSAGE")]
+        success_message: String = "Installed.",
+
+        /// Places to look for legacy manifests to uninstall
+        #[clap(value_name = "DIRS")]
+        legacy_manifest_dirs: String = "",
+
+        /// Directory containing files that should not be installed
+        #[clap(value_name = "DIR")]
+        non_installed_overlay: String = "",
+
+        /// Path prefixes of directories that should be installed/uninstalled in bulk
+        #[clap(value_name = "DIRS")]
+        bulk_dirs: String = "",
+
+        /// The directory containing the installation medium
+        #[clap(value_name = "DIR")]
+        image_dir: String = "./install_image",
+
+        /// The directory to do temporary work
+        #[clap(value_name = "DIR")]
+        work_dir: String = "./workdir",
+
+        /// The location to put the final image and tarball
+        #[clap(value_name = "DIR")]
+        output_dir: String = "./dist",
+
+        /// The formats used to compress the tarball
+        #[clap(value_name = "FORMAT", default_value_t)]
+        compression_formats: CompressionFormats,
+    }
+}
+
+impl Generator {
+    /// Generates the actual installer tarball
+    pub fn run(self) -> Result<()> {
+        create_dir_all(&self.work_dir)?;
+
+        let package_dir = Path::new(&self.work_dir).join(&self.package_name);
+        if package_dir.exists() {
+            remove_dir_all(&package_dir)?;
+        }
+
+        // Copy the image and write the manifest
+        let component_dir = package_dir.join(&self.component_name);
+        create_dir_all(&component_dir)?;
+        copy_and_manifest(self.image_dir.as_ref(), &component_dir, &self.bulk_dirs)?;
+
+        // Write the component name
+        let components = package_dir.join("components");
+        writeln!(create_new_file(components)?, "{}", self.component_name)
+            .context("failed to write the component file")?;
+
+        // Write the installer version (only used by combine-installers.sh)
+        let version = package_dir.join("rust-installer-version");
+        writeln!(
+            create_new_file(version)?,
+            "{}",
+            crate::RUST_INSTALLER_VERSION
+        )
+        .context("failed to write new installer version")?;
+
+        // Copy the overlay
+        if !self.non_installed_overlay.is_empty() {
+            copy_recursive(self.non_installed_overlay.as_ref(), &package_dir)?;
+        }
+
+        // Generate the install script
+        let output_script = package_dir.join("install.sh");
+        let mut scripter = Scripter::default();
+        scripter
+            .product_name(self.product_name)
+            .rel_manifest_dir(self.rel_manifest_dir)
+            .success_message(self.success_message)
+            .legacy_manifest_dirs(self.legacy_manifest_dirs)
+            .output_script(path_to_str(&output_script)?.into());
+        scripter.run()?;
+
+        // Make the tarballs
+        create_dir_all(&self.output_dir)?;
+        let output = Path::new(&self.output_dir).join(&self.package_name);
+        let mut tarballer = Tarballer::default();
+        tarballer
+            .work_dir(self.work_dir)
+            .input(self.package_name)
+            .output(path_to_str(&output)?.into())
+            .compression_formats(self.compression_formats.clone());
+        tarballer.run()?;
+
+        Ok(())
+    }
+}
+
+/// Copies the `src` directory recursively to `dst`, writing `manifest.in` too.
+fn copy_and_manifest(src: &Path, dst: &Path, bulk_dirs: &str) -> Result<()> {
+    let mut manifest = create_new_file(dst.join("manifest.in"))?;
+    let bulk_dirs: Vec<_> = bulk_dirs
+        .split(',')
+        .filter(|s| !s.is_empty())
+        .map(Path::new)
+        .collect();
+
+    let mut paths = BTreeSet::new();
+    copy_with_callback(src, dst, |path, file_type| {
+        // We need paths to be compatible with both Unix and Windows.
+        if path
+            .components()
+            .filter_map(|c| c.as_os_str().to_str())
+            .any(|s| s.contains('\\'))
+        {
+            bail!(
+                "rust-installer doesn't support '\\' in path components: {:?}",
+                path
+            );
+        }
+
+        // Normalize to Unix-style path separators.
+        let normalized_string;
+        let mut string = path.to_str().ok_or_else(|| {
+            format_err!(
+                "rust-installer doesn't support non-Unicode paths: {:?}",
+                path
+            )
+        })?;
+        if string.contains('\\') {
+            normalized_string = string.replace('\\', "/");
+            string = &normalized_string;
+        }
+
+        if file_type.is_dir() {
+            // Only manifest directories that are explicitly bulk.
+            if bulk_dirs.contains(&path) {
+                paths.insert(format!("dir:{}\n", string));
+            }
+        } else {
+            // Only manifest files that aren't under bulk directories.
+            if !bulk_dirs.iter().any(|d| path.starts_with(d)) {
+                paths.insert(format!("file:{}\n", string));
+            }
+        }
+        Ok(())
+    })?;
+
+    for path in paths {
+        manifest.write_all(path.as_bytes())?;
+    }
+
+    Ok(())
+}
diff --git a/src/tools/rust-installer/src/lib.rs b/src/tools/rust-installer/src/lib.rs
new file mode 100644
index 00000000000..7990920192a
--- /dev/null
+++ b/src/tools/rust-installer/src/lib.rs
@@ -0,0 +1,17 @@
+#[macro_use]
+mod util;
+
+mod combiner;
+mod compression;
+mod generator;
+mod scripter;
+mod tarballer;
+
+pub use crate::combiner::Combiner;
+pub use crate::generator::Generator;
+pub use crate::scripter::Scripter;
+pub use crate::tarballer::Tarballer;
+
+/// The installer version, output only to be used by combine-installers.sh.
+/// (should match `SOURCE_DIRECTORY/rust_installer_version`)
+pub const RUST_INSTALLER_VERSION: u32 = 3;
diff --git a/src/tools/rust-installer/src/main.rs b/src/tools/rust-installer/src/main.rs
new file mode 100644
index 00000000000..be8a0d68343
--- /dev/null
+++ b/src/tools/rust-installer/src/main.rs
@@ -0,0 +1,27 @@
+use anyhow::{Context, Result};
+use clap::{self, Parser};
+
+#[derive(Parser)]
+struct CommandLine {
+    #[clap(subcommand)]
+    command: Subcommand,
+}
+
+#[derive(clap::Subcommand)]
+enum Subcommand {
+    Generate(installer::Generator),
+    Combine(installer::Combiner),
+    Script(installer::Scripter),
+    Tarball(installer::Tarballer),
+}
+
+fn main() -> Result<()> {
+    let command_line = CommandLine::parse();
+    match command_line.command {
+        Subcommand::Combine(combiner) => combiner.run().context("failed to combine installers")?,
+        Subcommand::Generate(generator) => generator.run().context("failed to generate installer")?,
+        Subcommand::Script(scripter) => scripter.run().context("failed to generate installation script")?,
+        Subcommand::Tarball(tarballer) => tarballer.run().context("failed to generate tarballs")?,
+    }
+    Ok(())
+}
diff --git a/src/tools/rust-installer/src/remove_dir_all.rs b/src/tools/rust-installer/src/remove_dir_all.rs
new file mode 100644
index 00000000000..11097652865
--- /dev/null
+++ b/src/tools/rust-installer/src/remove_dir_all.rs
@@ -0,0 +1,860 @@
+#![allow(non_snake_case)]
+
+use std::io;
+use std::path::Path;
+
+#[cfg(not(windows))]
+pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+    ::std::fs::remove_dir_all(path)
+}
+
+#[cfg(windows)]
+pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+    win::remove_dir_all(path)
+}
+
+#[cfg(windows)]
+mod win {
+    use winapi::ctypes::{c_uint, c_ushort};
+    use winapi::shared::minwindef::{BOOL, DWORD, FALSE, FILETIME, LPVOID};
+    use winapi::shared::winerror::{
+        ERROR_CALL_NOT_IMPLEMENTED, ERROR_INSUFFICIENT_BUFFER, ERROR_NO_MORE_FILES,
+    };
+    use winapi::um::errhandlingapi::{GetLastError, SetLastError};
+    use winapi::um::fileapi::{
+        CreateFileW, FindFirstFileW, FindNextFileW, GetFileInformationByHandle,
+    };
+    use winapi::um::fileapi::{BY_HANDLE_FILE_INFORMATION, CREATE_ALWAYS, CREATE_NEW};
+    use winapi::um::fileapi::{FILE_BASIC_INFO, FILE_RENAME_INFO, TRUNCATE_EXISTING};
+    use winapi::um::fileapi::{OPEN_ALWAYS, OPEN_EXISTING};
+    use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
+    use winapi::um::ioapiset::DeviceIoControl;
+    use winapi::um::libloaderapi::{GetModuleHandleW, GetProcAddress};
+    use winapi::um::minwinbase::{
+        FileBasicInfo, FileRenameInfo, FILE_INFO_BY_HANDLE_CLASS, WIN32_FIND_DATAW,
+    };
+    use winapi::um::winbase::SECURITY_SQOS_PRESENT;
+    use winapi::um::winbase::{
+        FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_DELETE_ON_CLOSE, FILE_FLAG_OPEN_REPARSE_POINT,
+    };
+    use winapi::um::winioctl::FSCTL_GET_REPARSE_POINT;
+    use winapi::um::winnt::{DELETE, FILE_ATTRIBUTE_DIRECTORY, HANDLE, LPCWSTR};
+    use winapi::um::winnt::{FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_REPARSE_POINT};
+    use winapi::um::winnt::{FILE_GENERIC_WRITE, FILE_WRITE_DATA, GENERIC_READ, GENERIC_WRITE};
+    use winapi::um::winnt::{FILE_READ_ATTRIBUTES, FILE_WRITE_ATTRIBUTES};
+    use winapi::um::winnt::{FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE};
+    use winapi::um::winnt::{IO_REPARSE_TAG_MOUNT_POINT, IO_REPARSE_TAG_SYMLINK, LARGE_INTEGER};
+
+    use std::ffi::{OsStr, OsString};
+    use std::io;
+    use std::mem;
+    use std::os::windows::ffi::{OsStrExt, OsStringExt};
+    use std::path::{Path, PathBuf};
+    use std::ptr;
+    use std::sync::Arc;
+
+    pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+        // On Windows it is not enough to just recursively remove the contents of a
+        // directory and then the directory itself. Deleting does not happen
+        // instantaneously, but is scheduled.
+        // To work around this, we move the file or directory to some `base_dir`
+        // right before deletion to avoid races.
+        //
+        // As `base_dir` we choose the parent dir of the directory we want to
+        // remove. We very probably have permission to create files here, as we
+        // already need write permission in this dir to delete the directory. And it
+        // should be on the same volume.
+        //
+        // To handle files with names like `CON` and `morse .. .`, and when a
+        // directory structure is so deep it needs long path names the path is first
+        // converted to a `//?/`-path with `get_path()`.
+        //
+        // To make sure we don't leave a moved file laying around if the process
+        // crashes before we can delete the file, we do all operations on an file
+        // handle. By opening a file with `FILE_FLAG_DELETE_ON_CLOSE` Windows will
+        // always delete the file when the handle closes.
+        //
+        // All files are renamed to be in the `base_dir`, and have their name
+        // changed to "rm-<counter>". After every rename the counter is increased.
+        // Rename should not overwrite possibly existing files in the base dir. So
+        // if it fails with `AlreadyExists`, we just increase the counter and try
+        // again.
+        //
+        // For read-only files and directories we first have to remove the read-only
+        // attribute before we can move or delete them. This also removes the
+        // attribute from possible hardlinks to the file, so just before closing we
+        // restore the read-only attribute.
+        //
+        // If 'path' points to a directory symlink or junction we should not
+        // recursively remove the target of the link, but only the link itself.
+        //
+        // Moving and deleting is guaranteed to succeed if we are able to open the
+        // file with `DELETE` permission. If others have the file open we only have
+        // `DELETE` permission if they have specified `FILE_SHARE_DELETE`. We can
+        // also delete the file now, but it will not disappear until all others have
+        // closed the file. But no-one can open the file after we have flagged it
+        // for deletion.
+
+        // Open the path once to get the canonical path, file type and attributes.
+        let (path, metadata) = {
+            let mut opts = OpenOptions::new();
+            opts.access_mode(FILE_READ_ATTRIBUTES);
+            opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT);
+            let file = File::open(path, &opts)?;
+            (get_path(&file)?, file.file_attr()?)
+        };
+
+        let mut ctx = RmdirContext {
+            base_dir: match path.parent() {
+                Some(dir) => dir,
+                None => {
+                    return Err(io::Error::new(
+                        io::ErrorKind::PermissionDenied,
+                        "can't delete root directory",
+                    ))
+                }
+            },
+            readonly: metadata.perm().readonly(),
+            counter: 0,
+        };
+
+        let filetype = metadata.file_type();
+        if filetype.is_dir() {
+            remove_dir_all_recursive(path.as_ref(), &mut ctx)
+        } else if filetype.is_symlink_dir() {
+            remove_item(path.as_ref(), &mut ctx)
+        } else {
+            Err(io::Error::new(
+                io::ErrorKind::PermissionDenied,
+                "Not a directory",
+            ))
+        }
+    }
+
+    fn readdir(p: &Path) -> io::Result<ReadDir> {
+        let root = p.to_path_buf();
+        let star = p.join("*");
+        let path = to_u16s(&star)?;
+
+        unsafe {
+            let mut wfd = mem::zeroed();
+            let find_handle = FindFirstFileW(path.as_ptr(), &mut wfd);
+            if find_handle != INVALID_HANDLE_VALUE {
+                Ok(ReadDir {
+                    handle: FindNextFileHandle(find_handle),
+                    root: Arc::new(root),
+                    first: Some(wfd),
+                })
+            } else {
+                Err(io::Error::last_os_error())
+            }
+        }
+    }
+
+    struct RmdirContext<'a> {
+        base_dir: &'a Path,
+        readonly: bool,
+        counter: u64,
+    }
+
+    fn remove_dir_all_recursive(path: &Path, ctx: &mut RmdirContext) -> io::Result<()> {
+        let dir_readonly = ctx.readonly;
+        for child in readdir(path)? {
+            let child = child?;
+            let child_type = child.file_type()?;
+            ctx.readonly = child.metadata()?.perm().readonly();
+            if child_type.is_dir() {
+                remove_dir_all_recursive(&child.path(), ctx)?;
+            } else {
+                remove_item(&child.path().as_ref(), ctx)?;
+            }
+        }
+        ctx.readonly = dir_readonly;
+        remove_item(path, ctx)
+    }
+
+    fn remove_item(path: &Path, ctx: &mut RmdirContext) -> io::Result<()> {
+        if !ctx.readonly {
+            let mut opts = OpenOptions::new();
+            opts.access_mode(DELETE);
+            opts.custom_flags(
+                FILE_FLAG_BACKUP_SEMANTICS | // delete directory
+                              FILE_FLAG_OPEN_REPARSE_POINT | // delete symlink
+                              FILE_FLAG_DELETE_ON_CLOSE,
+            );
+            let file = File::open(path, &opts)?;
+            move_item(&file, ctx)
+        } else {
+            // remove read-only permision
+            set_perm(&path, FilePermissions::new())?;
+            // move and delete file, similar to !readonly.
+            // only the access mode is different.
+            let mut opts = OpenOptions::new();
+            opts.access_mode(DELETE | FILE_WRITE_ATTRIBUTES);
+            opts.custom_flags(
+                FILE_FLAG_BACKUP_SEMANTICS
+                    | FILE_FLAG_OPEN_REPARSE_POINT
+                    | FILE_FLAG_DELETE_ON_CLOSE,
+            );
+            let file = File::open(path, &opts)?;
+            move_item(&file, ctx)?;
+            // restore read-only flag just in case there are other hard links
+            let mut perm = FilePermissions::new();
+            perm.set_readonly(true);
+            let _ = file.set_perm(perm); // ignore if this fails
+            Ok(())
+        }
+    }
+
+    macro_rules! compat_fn {
+        ($module:ident: $(
+            fn $symbol:ident($($argname:ident: $argtype:ty),*)
+                             -> $rettype:ty {
+                $($body:expr);*
+            }
+        )*) => ($(
+            #[allow(unused_variables)]
+            unsafe fn $symbol($($argname: $argtype),*) -> $rettype {
+                use std::sync::atomic::{AtomicUsize, Ordering};
+                use std::mem;
+                use std::ffi::CString;
+                type F = unsafe extern "system" fn($($argtype),*) -> $rettype;
+
+                lazy_static! { static ref PTR: AtomicUsize = AtomicUsize::new(0);}
+
+                fn lookup(module: &str, symbol: &str) -> Option<usize> {
+                    let mut module: Vec<u16> = module.encode_utf16().collect();
+                    module.push(0);
+                    let symbol = CString::new(symbol).unwrap();
+                    unsafe {
+                        let handle = GetModuleHandleW(module.as_ptr());
+                        match GetProcAddress(handle, symbol.as_ptr()) as usize {
+                            0 => None,
+                            n => Some(n),
+                        }
+                    }
+                }
+
+                fn store_func(ptr: &AtomicUsize, module: &str, symbol: &str,
+                              fallback: usize) -> usize {
+                    let value = lookup(module, symbol).unwrap_or(fallback);
+                    ptr.store(value, Ordering::SeqCst);
+                    value
+                }
+
+                fn load() -> usize {
+                    store_func(&PTR, stringify!($module), stringify!($symbol), fallback as usize)
+                }
+                unsafe extern "system" fn fallback($($argname: $argtype),*)
+                                                   -> $rettype {
+                    $($body);*
+                }
+
+                let addr = match PTR.load(Ordering::SeqCst) {
+                    0 => load(),
+                    n => n,
+                };
+                mem::transmute::<usize, F>(addr)($($argname),*)
+            }
+        )*)
+    }
+
+    compat_fn! {
+        kernel32:
+        fn GetFinalPathNameByHandleW(_hFile: HANDLE,
+                                     _lpszFilePath: LPCWSTR,
+                                     _cchFilePath: DWORD,
+                                     _dwFlags: DWORD) -> DWORD {
+            SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
+        }
+        fn SetFileInformationByHandle(_hFile: HANDLE,
+                                      _FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,
+                                      _lpFileInformation: LPVOID,
+                                      _dwBufferSize: DWORD) -> BOOL {
+            SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
+        }
+    }
+
+    fn cvt(i: i32) -> io::Result<i32> {
+        if i == 0 {
+            Err(io::Error::last_os_error())
+        } else {
+            Ok(i)
+        }
+    }
+
+    fn to_u16s<S: AsRef<OsStr>>(s: S) -> io::Result<Vec<u16>> {
+        fn inner(s: &OsStr) -> io::Result<Vec<u16>> {
+            let mut maybe_result: Vec<u16> = s.encode_wide().collect();
+            if maybe_result.iter().any(|&u| u == 0) {
+                return Err(io::Error::new(
+                    io::ErrorKind::InvalidInput,
+                    "strings passed to WinAPI cannot contain NULs",
+                ));
+            }
+            maybe_result.push(0);
+            Ok(maybe_result)
+        }
+        inner(s.as_ref())
+    }
+
+    fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] {
+        match v.iter().position(|c| *c == 0) {
+            // don't include the 0
+            Some(i) => &v[..i],
+            None => v,
+        }
+    }
+
+    fn fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> io::Result<T>
+    where
+        F1: FnMut(*mut u16, DWORD) -> DWORD,
+        F2: FnOnce(&[u16]) -> T,
+    {
+        // Start off with a stack buf but then spill over to the heap if we end up
+        // needing more space.
+        let mut stack_buf = [0u16; 512];
+        let mut heap_buf = Vec::new();
+        unsafe {
+            let mut n = stack_buf.len();
+            loop {
+                let buf = if n <= stack_buf.len() {
+                    &mut stack_buf[..]
+                } else {
+                    let extra = n - heap_buf.len();
+                    heap_buf.reserve(extra);
+                    heap_buf.set_len(n);
+                    &mut heap_buf[..]
+                };
+
+                // This function is typically called on windows API functions which
+                // will return the correct length of the string, but these functions
+                // also return the `0` on error. In some cases, however, the
+                // returned "correct length" may actually be 0!
+                //
+                // To handle this case we call `SetLastError` to reset it to 0 and
+                // then check it again if we get the "0 error value". If the "last
+                // error" is still 0 then we interpret it as a 0 length buffer and
+                // not an actual error.
+                SetLastError(0);
+                let k = match f1(buf.as_mut_ptr(), n as DWORD) {
+                    0 if GetLastError() == 0 => 0,
+                    0 => return Err(io::Error::last_os_error()),
+                    n => n,
+                } as usize;
+                if k == n && GetLastError() == ERROR_INSUFFICIENT_BUFFER {
+                    n *= 2;
+                } else if k >= n {
+                    n = k;
+                } else {
+                    return Ok(f2(&buf[..k]));
+                }
+            }
+        }
+    }
+
+    #[derive(Clone, PartialEq, Eq, Debug, Default)]
+    struct FilePermissions {
+        readonly: bool,
+    }
+
+    impl FilePermissions {
+        fn new() -> FilePermissions {
+            Default::default()
+        }
+        fn readonly(&self) -> bool {
+            self.readonly
+        }
+        fn set_readonly(&mut self, readonly: bool) {
+            self.readonly = readonly
+        }
+    }
+
+    #[derive(Clone)]
+    struct OpenOptions {
+        // generic
+        read: bool,
+        write: bool,
+        append: bool,
+        truncate: bool,
+        create: bool,
+        create_new: bool,
+        // system-specific
+        custom_flags: u32,
+        access_mode: Option<DWORD>,
+        attributes: DWORD,
+        share_mode: DWORD,
+        security_qos_flags: DWORD,
+        security_attributes: usize, // FIXME: should be a reference
+    }
+
+    impl OpenOptions {
+        fn new() -> OpenOptions {
+            OpenOptions {
+                // generic
+                read: false,
+                write: false,
+                append: false,
+                truncate: false,
+                create: false,
+                create_new: false,
+                // system-specific
+                custom_flags: 0,
+                access_mode: None,
+                share_mode: FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                attributes: 0,
+                security_qos_flags: 0,
+                security_attributes: 0,
+            }
+        }
+        fn custom_flags(&mut self, flags: u32) {
+            self.custom_flags = flags;
+        }
+        fn access_mode(&mut self, access_mode: u32) {
+            self.access_mode = Some(access_mode);
+        }
+
+        fn get_access_mode(&self) -> io::Result<DWORD> {
+            const ERROR_INVALID_PARAMETER: i32 = 87;
+
+            match (self.read, self.write, self.append, self.access_mode) {
+                (_, _, _, Some(mode)) => Ok(mode),
+                (true, false, false, None) => Ok(GENERIC_READ),
+                (false, true, false, None) => Ok(GENERIC_WRITE),
+                (true, true, false, None) => Ok(GENERIC_READ | GENERIC_WRITE),
+                (false, _, true, None) => Ok(FILE_GENERIC_WRITE & !FILE_WRITE_DATA),
+                (true, _, true, None) => Ok(GENERIC_READ | (FILE_GENERIC_WRITE & !FILE_WRITE_DATA)),
+                (false, false, false, None) => {
+                    Err(io::Error::from_raw_os_error(ERROR_INVALID_PARAMETER))
+                }
+            }
+        }
+
+        fn get_creation_mode(&self) -> io::Result<DWORD> {
+            const ERROR_INVALID_PARAMETER: i32 = 87;
+
+            match (self.write, self.append) {
+                (true, false) => {}
+                (false, false) => {
+                    if self.truncate || self.create || self.create_new {
+                        return Err(io::Error::from_raw_os_error(ERROR_INVALID_PARAMETER));
+                    }
+                }
+                (_, true) => {
+                    if self.truncate && !self.create_new {
+                        return Err(io::Error::from_raw_os_error(ERROR_INVALID_PARAMETER));
+                    }
+                }
+            }
+
+            Ok(match (self.create, self.truncate, self.create_new) {
+                (false, false, false) => OPEN_EXISTING,
+                (true, false, false) => OPEN_ALWAYS,
+                (false, true, false) => TRUNCATE_EXISTING,
+                (true, true, false) => CREATE_ALWAYS,
+                (_, _, true) => CREATE_NEW,
+            })
+        }
+
+        fn get_flags_and_attributes(&self) -> DWORD {
+            self.custom_flags
+                | self.attributes
+                | self.security_qos_flags
+                | if self.security_qos_flags != 0 {
+                    SECURITY_SQOS_PRESENT
+                } else {
+                    0
+                }
+                | if self.create_new {
+                    FILE_FLAG_OPEN_REPARSE_POINT
+                } else {
+                    0
+                }
+        }
+    }
+
+    struct File {
+        handle: Handle,
+    }
+
+    impl File {
+        fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
+            let path = to_u16s(path)?;
+            let handle = unsafe {
+                CreateFileW(
+                    path.as_ptr(),
+                    opts.get_access_mode()?,
+                    opts.share_mode,
+                    opts.security_attributes as *mut _,
+                    opts.get_creation_mode()?,
+                    opts.get_flags_and_attributes(),
+                    ptr::null_mut(),
+                )
+            };
+            if handle == INVALID_HANDLE_VALUE {
+                Err(io::Error::last_os_error())
+            } else {
+                Ok(File {
+                    handle: Handle::new(handle),
+                })
+            }
+        }
+
+        fn file_attr(&self) -> io::Result<FileAttr> {
+            unsafe {
+                let mut info: BY_HANDLE_FILE_INFORMATION = mem::zeroed();
+                cvt(GetFileInformationByHandle(self.handle.raw(), &mut info))?;
+                let mut attr = FileAttr {
+                    attributes: info.dwFileAttributes,
+                    creation_time: info.ftCreationTime,
+                    last_access_time: info.ftLastAccessTime,
+                    last_write_time: info.ftLastWriteTime,
+                    file_size: ((info.nFileSizeHigh as u64) << 32) | (info.nFileSizeLow as u64),
+                    reparse_tag: 0,
+                };
+                if attr.is_reparse_point() {
+                    let mut b = [0; MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+                    if let Ok((_, buf)) = self.reparse_point(&mut b) {
+                        attr.reparse_tag = buf.ReparseTag;
+                    }
+                }
+                Ok(attr)
+            }
+        }
+
+        fn set_attributes(&self, attr: DWORD) -> io::Result<()> {
+            let zero: LARGE_INTEGER = unsafe { mem::zeroed() };
+
+            let mut info = FILE_BASIC_INFO {
+                CreationTime: zero,   // do not change
+                LastAccessTime: zero, // do not change
+                LastWriteTime: zero,  // do not change
+                ChangeTime: zero,     // do not change
+                FileAttributes: attr,
+            };
+            let size = mem::size_of_val(&info);
+            cvt(unsafe {
+                SetFileInformationByHandle(
+                    self.handle.raw(),
+                    FileBasicInfo,
+                    &mut info as *mut _ as *mut _,
+                    size as DWORD,
+                )
+            })?;
+            Ok(())
+        }
+
+        fn rename(&self, new: &Path, replace: bool) -> io::Result<()> {
+            // &self must be opened with DELETE permission
+            use std::iter;
+            #[cfg(target_arch = "x86")]
+            const STRUCT_SIZE: usize = 12;
+            #[cfg(target_arch = "x86_64")]
+            const STRUCT_SIZE: usize = 20;
+
+            // FIXME: check for internal NULs in 'new'
+            let mut data: Vec<u16> = iter::repeat(0u16)
+                .take(STRUCT_SIZE / 2)
+                .chain(new.as_os_str().encode_wide())
+                .collect();
+            data.push(0);
+            let size = data.len() * 2;
+
+            unsafe {
+                // Thanks to alignment guarantees on Windows this works
+                // (8 for 32-bit and 16 for 64-bit)
+                let info = data.as_mut_ptr() as *mut FILE_RENAME_INFO;
+                // The type of ReplaceIfExists is BOOL, but it actually expects a
+                // BOOLEAN. This means true is -1, not c::TRUE.
+                (*info).ReplaceIfExists = if replace { -1 } else { FALSE };
+                (*info).RootDirectory = ptr::null_mut();
+                (*info).FileNameLength = (size - STRUCT_SIZE) as DWORD;
+                cvt(SetFileInformationByHandle(
+                    self.handle().raw(),
+                    FileRenameInfo,
+                    data.as_mut_ptr() as *mut _ as *mut _,
+                    size as DWORD,
+                ))?;
+                Ok(())
+            }
+        }
+        fn set_perm(&self, perm: FilePermissions) -> io::Result<()> {
+            let attr = self.file_attr()?.attributes;
+            if perm.readonly == (attr & FILE_ATTRIBUTE_READONLY != 0) {
+                Ok(())
+            } else if perm.readonly {
+                self.set_attributes(attr | FILE_ATTRIBUTE_READONLY)
+            } else {
+                self.set_attributes(attr & !FILE_ATTRIBUTE_READONLY)
+            }
+        }
+
+        fn handle(&self) -> &Handle {
+            &self.handle
+        }
+
+        fn reparse_point<'a>(
+            &self,
+            space: &'a mut [u8; MAXIMUM_REPARSE_DATA_BUFFER_SIZE],
+        ) -> io::Result<(DWORD, &'a REPARSE_DATA_BUFFER)> {
+            unsafe {
+                let mut bytes = 0;
+                cvt({
+                    DeviceIoControl(
+                        self.handle.raw(),
+                        FSCTL_GET_REPARSE_POINT,
+                        ptr::null_mut(),
+                        0,
+                        space.as_mut_ptr() as *mut _,
+                        space.len() as DWORD,
+                        &mut bytes,
+                        ptr::null_mut(),
+                    )
+                })?;
+                Ok((bytes, &*(space.as_ptr() as *const REPARSE_DATA_BUFFER)))
+            }
+        }
+    }
+
+    #[derive(Copy, Clone, PartialEq, Eq, Hash)]
+    enum FileType {
+        Dir,
+        File,
+        SymlinkFile,
+        SymlinkDir,
+        ReparsePoint,
+        MountPoint,
+    }
+
+    impl FileType {
+        fn new(attrs: DWORD, reparse_tag: DWORD) -> FileType {
+            match (
+                attrs & FILE_ATTRIBUTE_DIRECTORY != 0,
+                attrs & FILE_ATTRIBUTE_REPARSE_POINT != 0,
+                reparse_tag,
+            ) {
+                (false, false, _) => FileType::File,
+                (true, false, _) => FileType::Dir,
+                (false, true, IO_REPARSE_TAG_SYMLINK) => FileType::SymlinkFile,
+                (true, true, IO_REPARSE_TAG_SYMLINK) => FileType::SymlinkDir,
+                (true, true, IO_REPARSE_TAG_MOUNT_POINT) => FileType::MountPoint,
+                (_, true, _) => FileType::ReparsePoint,
+                // Note: if a _file_ has a reparse tag of the type IO_REPARSE_TAG_MOUNT_POINT it is
+                // invalid, as junctions always have to be dirs. We set the filetype to ReparsePoint
+                // to indicate it is something symlink-like, but not something you can follow.
+            }
+        }
+
+        fn is_dir(&self) -> bool {
+            *self == FileType::Dir
+        }
+        fn is_symlink_dir(&self) -> bool {
+            *self == FileType::SymlinkDir || *self == FileType::MountPoint
+        }
+    }
+
+    impl DirEntry {
+        fn new(root: &Arc<PathBuf>, wfd: &WIN32_FIND_DATAW) -> Option<DirEntry> {
+            let first_bytes = &wfd.cFileName[0..3];
+            if first_bytes.starts_with(&[46, 0]) || first_bytes.starts_with(&[46, 46, 0]) {
+                None
+            } else {
+                Some(DirEntry {
+                    root: root.clone(),
+                    data: *wfd,
+                })
+            }
+        }
+
+        fn path(&self) -> PathBuf {
+            self.root.join(&self.file_name())
+        }
+
+        fn file_name(&self) -> OsString {
+            let filename = truncate_utf16_at_nul(&self.data.cFileName);
+            OsString::from_wide(filename)
+        }
+
+        fn file_type(&self) -> io::Result<FileType> {
+            Ok(FileType::new(
+                self.data.dwFileAttributes,
+                /* reparse_tag = */ self.data.dwReserved0,
+            ))
+        }
+
+        fn metadata(&self) -> io::Result<FileAttr> {
+            Ok(FileAttr {
+                attributes: self.data.dwFileAttributes,
+                creation_time: self.data.ftCreationTime,
+                last_access_time: self.data.ftLastAccessTime,
+                last_write_time: self.data.ftLastWriteTime,
+                file_size: ((self.data.nFileSizeHigh as u64) << 32)
+                    | (self.data.nFileSizeLow as u64),
+                reparse_tag: if self.data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT != 0 {
+                    // reserved unless this is a reparse point
+                    self.data.dwReserved0
+                } else {
+                    0
+                },
+            })
+        }
+    }
+
+    struct DirEntry {
+        root: Arc<PathBuf>,
+        data: WIN32_FIND_DATAW,
+    }
+
+    struct ReadDir {
+        handle: FindNextFileHandle,
+        root: Arc<PathBuf>,
+        first: Option<WIN32_FIND_DATAW>,
+    }
+
+    impl Iterator for ReadDir {
+        type Item = io::Result<DirEntry>;
+        fn next(&mut self) -> Option<io::Result<DirEntry>> {
+            if let Some(first) = self.first.take() {
+                if let Some(e) = DirEntry::new(&self.root, &first) {
+                    return Some(Ok(e));
+                }
+            }
+            unsafe {
+                let mut wfd = mem::zeroed();
+                loop {
+                    if FindNextFileW(self.handle.0, &mut wfd) == 0 {
+                        if GetLastError() == ERROR_NO_MORE_FILES {
+                            return None;
+                        } else {
+                            return Some(Err(io::Error::last_os_error()));
+                        }
+                    }
+                    if let Some(e) = DirEntry::new(&self.root, &wfd) {
+                        return Some(Ok(e));
+                    }
+                }
+            }
+        }
+    }
+
+    #[derive(Clone)]
+    struct FileAttr {
+        attributes: DWORD,
+        creation_time: FILETIME,
+        last_access_time: FILETIME,
+        last_write_time: FILETIME,
+        file_size: u64,
+        reparse_tag: DWORD,
+    }
+
+    impl FileAttr {
+        fn perm(&self) -> FilePermissions {
+            FilePermissions {
+                readonly: self.attributes & FILE_ATTRIBUTE_READONLY != 0,
+            }
+        }
+
+        fn file_type(&self) -> FileType {
+            FileType::new(self.attributes, self.reparse_tag)
+        }
+
+        fn is_reparse_point(&self) -> bool {
+            self.attributes & FILE_ATTRIBUTE_REPARSE_POINT != 0
+        }
+    }
+
+    #[repr(C)]
+    struct REPARSE_DATA_BUFFER {
+        ReparseTag: c_uint,
+        ReparseDataLength: c_ushort,
+        Reserved: c_ushort,
+        rest: (),
+    }
+
+    const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: usize = 16 * 1024;
+
+    /// An owned container for `HANDLE` object, closing them on Drop.
+    ///
+    /// All methods are inherited through a `Deref` impl to `RawHandle`
+    struct Handle(RawHandle);
+
+    use std::ops::Deref;
+
+    /// A wrapper type for `HANDLE` objects to give them proper Send/Sync inference
+    /// as well as Rust-y methods.
+    ///
+    /// This does **not** drop the handle when it goes out of scope, use `Handle`
+    /// instead for that.
+    #[derive(Copy, Clone)]
+    struct RawHandle(HANDLE);
+
+    unsafe impl Send for RawHandle {}
+    unsafe impl Sync for RawHandle {}
+
+    impl Handle {
+        fn new(handle: HANDLE) -> Handle {
+            Handle(RawHandle::new(handle))
+        }
+    }
+
+    impl Deref for Handle {
+        type Target = RawHandle;
+        fn deref(&self) -> &RawHandle {
+            &self.0
+        }
+    }
+
+    impl Drop for Handle {
+        fn drop(&mut self) {
+            unsafe {
+                let _ = CloseHandle(self.raw());
+            }
+        }
+    }
+
+    impl RawHandle {
+        fn new(handle: HANDLE) -> RawHandle {
+            RawHandle(handle)
+        }
+
+        fn raw(&self) -> HANDLE {
+            self.0
+        }
+    }
+
+    struct FindNextFileHandle(HANDLE);
+
+    fn get_path(f: &File) -> io::Result<PathBuf> {
+        fill_utf16_buf(
+            |buf, sz| unsafe {
+                GetFinalPathNameByHandleW(f.handle.raw(), buf, sz, VOLUME_NAME_DOS)
+            },
+            |buf| PathBuf::from(OsString::from_wide(buf)),
+        )
+    }
+
+    fn move_item(file: &File, ctx: &mut RmdirContext) -> io::Result<()> {
+        let mut tmpname = ctx.base_dir.join(format! {"rm-{}", ctx.counter});
+        ctx.counter += 1;
+        // Try to rename the file. If it already exists, just retry with an other
+        // filename.
+        while let Err(err) = file.rename(tmpname.as_ref(), false) {
+            if err.kind() != io::ErrorKind::AlreadyExists {
+                return Err(err);
+            };
+            tmpname = ctx.base_dir.join(format!("rm-{}", ctx.counter));
+            ctx.counter += 1;
+        }
+        Ok(())
+    }
+
+    fn set_perm(path: &Path, perm: FilePermissions) -> io::Result<()> {
+        let mut opts = OpenOptions::new();
+        opts.access_mode(FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES);
+        opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS);
+        let file = File::open(path, &opts)?;
+        file.set_perm(perm)
+    }
+
+    const VOLUME_NAME_DOS: DWORD = 0x0;
+}
diff --git a/src/tools/rust-installer/src/scripter.rs b/src/tools/rust-installer/src/scripter.rs
new file mode 100644
index 00000000000..06affc029fd
--- /dev/null
+++ b/src/tools/rust-installer/src/scripter.rs
@@ -0,0 +1,68 @@
+use crate::util::*;
+use anyhow::{Context, Result};
+use std::io::Write;
+
+const TEMPLATE: &'static str = include_str!("../install-template.sh");
+
+actor! {
+    #[derive(Debug)]
+    pub struct Scripter {
+        /// The name of the product, for display
+        #[clap(value_name = "NAME")]
+        product_name: String = "Product",
+
+        /// The directory under lib/ where the manifest lives
+        #[clap(value_name = "DIR")]
+        rel_manifest_dir: String = "manifestlib",
+
+        /// The string to print after successful installation
+        #[clap(value_name = "MESSAGE")]
+        success_message: String = "Installed.",
+
+        /// Places to look for legacy manifests to uninstall
+        #[clap(value_name = "DIRS")]
+        legacy_manifest_dirs: String = "",
+
+        /// The name of the output script
+        #[clap(value_name = "FILE")]
+        output_script: String = "install.sh",
+    }
+}
+
+impl Scripter {
+    /// Generates the actual installer script
+    pub fn run(self) -> Result<()> {
+        // Replace dashes in the success message with spaces (our arg handling botches spaces)
+        // TODO: still needed? Kept for compatibility for now.
+        let product_name = self.product_name.replace('-', " ");
+
+        // Replace dashes in the success message with spaces (our arg handling botches spaces)
+        // TODO: still needed? Kept for compatibility for now.
+        let success_message = self.success_message.replace('-', " ");
+
+        let script = TEMPLATE
+            .replace("%%TEMPLATE_PRODUCT_NAME%%", &sh_quote(&product_name))
+            .replace("%%TEMPLATE_REL_MANIFEST_DIR%%", &self.rel_manifest_dir)
+            .replace("%%TEMPLATE_SUCCESS_MESSAGE%%", &sh_quote(&success_message))
+            .replace(
+                "%%TEMPLATE_LEGACY_MANIFEST_DIRS%%",
+                &sh_quote(&self.legacy_manifest_dirs),
+            )
+            .replace(
+                "%%TEMPLATE_RUST_INSTALLER_VERSION%%",
+                &sh_quote(&crate::RUST_INSTALLER_VERSION),
+            );
+
+        create_new_executable(&self.output_script)?
+            .write_all(script.as_ref())
+            .with_context(|| format!("failed to write output script '{}'", self.output_script))?;
+
+        Ok(())
+    }
+}
+
+fn sh_quote<T: ToString>(s: &T) -> String {
+    // We'll single-quote the whole thing, so first replace single-quotes with
+    // '"'"' (leave quoting, double-quote one `'`, re-enter single-quoting)
+    format!("'{}'", s.to_string().replace('\'', r#"'"'"'"#))
+}
diff --git a/src/tools/rust-installer/src/tarballer.rs b/src/tools/rust-installer/src/tarballer.rs
new file mode 100644
index 00000000000..76f5af3fa53
--- /dev/null
+++ b/src/tools/rust-installer/src/tarballer.rs
@@ -0,0 +1,143 @@
+use anyhow::{bail, Context, Result};
+use std::fs::{read_link, symlink_metadata};
+use std::io::{empty, BufWriter, Write};
+use std::path::Path;
+use tar::{Builder, Header};
+use walkdir::WalkDir;
+
+use crate::{
+    compression::{CombinedEncoder, CompressionFormats},
+    util::*,
+};
+
+actor! {
+    #[derive(Debug)]
+    pub struct Tarballer {
+        /// The input folder to be compressed.
+        #[clap(value_name = "NAME")]
+        input: String = "package",
+
+        /// The prefix of the tarballs.
+        #[clap(value_name = "PATH")]
+        output: String = "./dist",
+
+        /// The folder in which the input is to be found.
+        #[clap(value_name = "DIR")]
+        work_dir: String = "./workdir",
+
+        /// The formats used to compress the tarball.
+        #[clap(value_name = "FORMAT", default_value_t)]
+        compression_formats: CompressionFormats,
+    }
+}
+
+impl Tarballer {
+    /// Generates the actual tarballs
+    pub fn run(self) -> Result<()> {
+        let tarball_name = self.output.clone() + ".tar";
+        let encoder = CombinedEncoder::new(
+            self.compression_formats
+                .iter()
+                .map(|f| f.encode(&tarball_name))
+                .collect::<Result<Vec<_>>>()?,
+        );
+
+        // Sort files by their suffix, to group files with the same name from
+        // different locations (likely identical) and files with the same
+        // extension (likely containing similar data).
+        let (dirs, mut files) = get_recursive_paths(&self.work_dir, &self.input)
+            .context("failed to collect file paths")?;
+        files.sort_by(|a, b| a.bytes().rev().cmp(b.bytes().rev()));
+
+        // Write the tar into both encoded files. We write all directories
+        // first, so files may be directly created. (See rust-lang/rustup.rs#1092.)
+        let buf = BufWriter::with_capacity(1024 * 1024, encoder);
+        let mut builder = Builder::new(buf);
+
+        let pool = rayon::ThreadPoolBuilder::new()
+            .num_threads(2)
+            .build()
+            .unwrap();
+        pool.install(move || {
+            for path in dirs {
+                let src = Path::new(&self.work_dir).join(&path);
+                builder
+                    .append_dir(&path, &src)
+                    .with_context(|| format!("failed to tar dir '{}'", src.display()))?;
+            }
+            for path in files {
+                let src = Path::new(&self.work_dir).join(&path);
+                append_path(&mut builder, &src, &path)
+                    .with_context(|| format!("failed to tar file '{}'", src.display()))?;
+            }
+            builder
+                .into_inner()
+                .context("failed to finish writing .tar stream")?
+                .into_inner()
+                .ok()
+                .unwrap()
+                .finish()?;
+
+            Ok(())
+        })
+    }
+}
+
+fn append_path<W: Write>(builder: &mut Builder<W>, src: &Path, path: &String) -> Result<()> {
+    let stat = symlink_metadata(src)?;
+    let mut header = Header::new_gnu();
+    header.set_metadata(&stat);
+    if stat.file_type().is_symlink() {
+        let link = read_link(src)?;
+        header.set_link_name(&link)?;
+        builder.append_data(&mut header, path, &mut empty())?;
+    } else {
+        if cfg!(windows) {
+            // Windows doesn't really have a mode, so `tar` never marks files executable.
+            // Use an extension whitelist to update files that usually should be so.
+            const EXECUTABLES: [&'static str; 4] = ["exe", "dll", "py", "sh"];
+            if let Some(ext) = src.extension().and_then(|s| s.to_str()) {
+                if EXECUTABLES.contains(&ext) {
+                    let mode = header.mode()?;
+                    header.set_mode(mode | 0o111);
+                }
+            }
+        }
+        let file = open_file(src)?;
+        builder.append_data(&mut header, path, &file)?;
+    }
+    Ok(())
+}
+
+/// Returns all `(directories, files)` under the source path.
+fn get_recursive_paths<P, Q>(root: P, name: Q) -> Result<(Vec<String>, Vec<String>)>
+where
+    P: AsRef<Path>,
+    Q: AsRef<Path>,
+{
+    let root = root.as_ref();
+    let name = name.as_ref();
+
+    if !name.is_relative() && !name.starts_with(root) {
+        bail!(
+            "input '{}' is not in work dir '{}'",
+            name.display(),
+            root.display()
+        );
+    }
+
+    let mut dirs = vec![];
+    let mut files = vec![];
+    for entry in WalkDir::new(root.join(name)) {
+        let entry = entry?;
+        let path = entry.path().strip_prefix(root)?;
+        let path = path_to_str(&path)?;
+
+        if entry.file_type().is_dir() {
+            dirs.push(path.to_owned());
+        } else {
+            files.push(path.to_owned());
+        }
+    }
+    Ok((dirs, files))
+}
diff --git a/src/tools/rust-installer/src/util.rs b/src/tools/rust-installer/src/util.rs
new file mode 100644
index 00000000000..674617c657c
--- /dev/null
+++ b/src/tools/rust-installer/src/util.rs
@@ -0,0 +1,156 @@
+use anyhow::{format_err, Context, Result};
+use std::fs;
+use std::path::Path;
+use walkdir::WalkDir;
+
+// Needed to set the script mode to executable.
+#[cfg(unix)]
+use std::os::unix::fs::OpenOptionsExt;
+// FIXME: what about Windows? Are default ACLs executable?
+
+#[cfg(unix)]
+use std::os::unix::fs::symlink as symlink_file;
+#[cfg(windows)]
+use std::os::windows::fs::symlink_file;
+
+/// Converts a `&Path` to a UTF-8 `&str`.
+pub fn path_to_str(path: &Path) -> Result<&str> {
+    path.to_str()
+        .ok_or_else(|| format_err!("path is not valid UTF-8 '{}'", path.display()))
+}
+
+/// Wraps `fs::copy` with a nicer error message.
+pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<u64> {
+    if fs::symlink_metadata(&from)?.file_type().is_symlink() {
+        let link = fs::read_link(&from)?;
+        symlink_file(link, &to)?;
+        Ok(0)
+    } else {
+        let amt = fs::copy(&from, &to).with_context(|| {
+            format!(
+                "failed to copy '{}' to '{}'",
+                from.as_ref().display(),
+                to.as_ref().display()
+            )
+        })?;
+        Ok(amt)
+    }
+}
+
+/// Wraps `fs::create_dir` with a nicer error message.
+pub fn create_dir<P: AsRef<Path>>(path: P) -> Result<()> {
+    fs::create_dir(&path)
+        .with_context(|| format!("failed to create dir '{}'", path.as_ref().display()))?;
+    Ok(())
+}
+
+/// Wraps `fs::create_dir_all` with a nicer error message.
+pub fn create_dir_all<P: AsRef<Path>>(path: P) -> Result<()> {
+    fs::create_dir_all(&path)
+        .with_context(|| format!("failed to create dir '{}'", path.as_ref().display()))?;
+    Ok(())
+}
+
+/// Wraps `fs::OpenOptions::create_new().open()` as executable, with a nicer error message.
+pub fn create_new_executable<P: AsRef<Path>>(path: P) -> Result<fs::File> {
+    let mut options = fs::OpenOptions::new();
+    options.write(true).create_new(true);
+    #[cfg(unix)]
+    options.mode(0o755);
+    let file = options
+        .open(&path)
+        .with_context(|| format!("failed to create file '{}'", path.as_ref().display()))?;
+    Ok(file)
+}
+
+/// Wraps `fs::OpenOptions::create_new().open()`, with a nicer error message.
+pub fn create_new_file<P: AsRef<Path>>(path: P) -> Result<fs::File> {
+    let file = fs::OpenOptions::new()
+        .write(true)
+        .create_new(true)
+        .open(&path)
+        .with_context(|| format!("failed to create file '{}'", path.as_ref().display()))?;
+    Ok(file)
+}
+
+/// Wraps `fs::File::open()` with a nicer error message.
+pub fn open_file<P: AsRef<Path>>(path: P) -> Result<fs::File> {
+    let file = fs::File::open(&path)
+        .with_context(|| format!("failed to open file '{}'", path.as_ref().display()))?;
+    Ok(file)
+}
+
+/// Wraps `remove_dir_all` with a nicer error message.
+pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> Result<()> {
+    remove_dir_all::remove_dir_all(path.as_ref())
+        .with_context(|| format!("failed to remove dir '{}'", path.as_ref().display()))?;
+    Ok(())
+}
+
+/// Wrap `fs::remove_file` with a nicer error message
+pub fn remove_file<P: AsRef<Path>>(path: P) -> Result<()> {
+    fs::remove_file(path.as_ref())
+        .with_context(|| format!("failed to remove file '{}'", path.as_ref().display()))?;
+    Ok(())
+}
+
+/// Copies the `src` directory recursively to `dst`. Both are assumed to exist
+/// when this function is called.
+pub fn copy_recursive(src: &Path, dst: &Path) -> Result<()> {
+    copy_with_callback(src, dst, |_, _| Ok(()))
+}
+
+/// Copies the `src` directory recursively to `dst`. Both are assumed to exist
+/// when this function is called. Invokes a callback for each path visited.
+pub fn copy_with_callback<F>(src: &Path, dst: &Path, mut callback: F) -> Result<()>
+where
+    F: FnMut(&Path, fs::FileType) -> Result<()>,
+{
+    for entry in WalkDir::new(src).min_depth(1) {
+        let entry = entry?;
+        let file_type = entry.file_type();
+        let path = entry.path().strip_prefix(src)?;
+        let dst = dst.join(path);
+
+        if file_type.is_dir() {
+            create_dir(&dst)?;
+        } else {
+            copy(entry.path(), dst)?;
+        }
+        callback(&path, file_type)?;
+    }
+    Ok(())
+}
+
+macro_rules! actor_field_default {
+    () => { Default::default() };
+    (= $expr:expr) => { $expr.into() }
+}
+
+/// Creates an "actor" with default values, setters for all fields, and Clap parser support.
+macro_rules! actor {
+    ($( #[ $attr:meta ] )+ pub struct $name:ident {
+        $( $( #[ $field_attr:meta ] )+ $field:ident : $type:ty $(= $default:tt)*, )*
+    }) => {
+        $( #[ $attr ] )+
+        #[derive(clap::Args)]
+        pub struct $name {
+            $( $( #[ $field_attr ] )+ #[clap(long, $(default_value = $default)*)] $field : $type, )*
+        }
+
+        impl Default for $name {
+            fn default() -> $name {
+                $name {
+                    $($field : actor_field_default!($(= $default)*), )*
+                }
+            }
+        }
+
+        impl $name {
+            $(pub fn $field(&mut self, value: $type) -> &mut Self {
+                self.$field = value;
+                self
+            })*
+        }
+    }
+}
diff --git a/src/tools/rust-installer/test.sh b/src/tools/rust-installer/test.sh
new file mode 100755
index 00000000000..dac6f77ef14
--- /dev/null
+++ b/src/tools/rust-installer/test.sh
@@ -0,0 +1,1341 @@
+#!/bin/bash
+
+set -e -u
+
+if [ -x /bin/echo ]; then
+    ECHO='/bin/echo'
+else
+    ECHO='echo'
+fi
+
+# Prints the absolute path of a directory to stdout
+abs_path() {
+    local path="$1"
+    # Unset CDPATH because it causes havok: it makes the destination unpredictable
+    # and triggers 'cd' to print the path to stdout. Route `cd`'s output to /dev/null
+    # for good measure.
+    (unset CDPATH && cd "$path" > /dev/null && pwd)
+}
+
+S="$(abs_path $(dirname $0))"
+
+TEST_DIR="$S/test"
+WORK_DIR="$TMP_DIR/workdir"
+OUT_DIR="$TMP_DIR/outdir"
+PREFIX_DIR="$TMP_DIR/prefix"
+
+case $(uname -s) in
+
+    MINGW* | MSYS*)
+	WINDOWS=1
+        ;;
+esac
+
+say() {
+    echo "test: $1"
+}
+
+pre() {
+    echo "test: $1"
+    rm -Rf "$WORK_DIR"
+    rm -Rf "$OUT_DIR"
+    rm -Rf "$PREFIX_DIR"
+    mkdir -p "$WORK_DIR"
+    mkdir -p "$OUT_DIR"
+    mkdir -p "$PREFIX_DIR"
+}
+
+need_ok() {
+    if [ $? -ne 0 ]
+    then
+	echo
+	echo "TEST FAILED!"
+	echo
+	exit 1
+    fi
+}
+
+fail() {
+    echo
+    echo "$1"
+    echo
+    echo "TEST FAILED!"
+    echo
+    exit 1
+}
+
+try() {
+    set +e
+    _cmd="$@"
+    _output=`$@ 2>&1`
+    if [ $? -ne 0 ]; then
+	echo \$ "$_cmd"
+	# Using /bin/echo to avoid escaping
+	$ECHO "$_output"
+	echo
+	echo "TEST FAILED!"
+	echo
+	exit 1
+    else
+	if [ -n "${VERBOSE-}" -o -n "${VERBOSE_CMD-}" ]; then
+	    echo \$ "$_cmd"
+	fi
+	if [ -n "${VERBOSE-}" -o -n "${VERBOSE_OUTPUT-}" ]; then
+	    $ECHO "$_output"
+	fi
+    fi
+    set -e
+}
+
+expect_fail() {
+    set +e
+    _cmd="$@"
+    _output=`$@ 2>&1`
+    if [ $? -eq 0 ]; then
+	echo \$ "$_cmd"
+	# Using /bin/echo to avoid escaping
+	$ECHO "$_output"
+	echo
+	echo "TEST FAILED!"
+	echo
+	exit 1
+    else
+	if [ -n "${VERBOSE-}" -o -n "${VERBOSE_CMD-}" ]; then
+	    echo \$ "$_cmd"
+	fi
+	if [ -n "${VERBOSE-}" -o -n "${VERBOSE_OUTPUT-}" ]; then
+	    $ECHO "$_output"
+	fi
+    fi
+    set -e
+}
+
+expect_output_ok() {
+    set +e
+    local _expected="$1"
+    shift 1
+    _cmd="$@"
+    _output=`$@ 2>&1`
+    if [ $? -ne 0 ]; then
+	echo \$ "$_cmd"
+	# Using /bin/echo to avoid escaping
+	$ECHO "$_output"
+	echo
+	echo "TEST FAILED!"
+	echo
+	exit 1
+    elif ! echo "$_output" | grep -q "$_expected"; then
+	echo \$ "$_cmd"
+	$ECHO "$_output"
+	echo
+	echo "missing expected output '$_expected'"
+	echo
+	echo
+	echo "TEST FAILED!"
+	echo
+	exit 1
+    else
+	if [ -n "${VERBOSE-}" -o -n "${VERBOSE_CMD-}" ]; then
+	    echo \$ "$_cmd"
+	fi
+	if [ -n "${VERBOSE-}" -o -n "${VERBOSE_OUTPUT-}" ]; then
+	    $ECHO "$_output"
+	fi
+    fi
+    set -e
+}
+
+expect_output_fail() {
+    set +e
+    local _expected="$1"
+    shift 1
+    _cmd="$@"
+    _output=`$@ 2>&1`
+    if [ $? -eq 0 ]; then
+	echo \$ "$_cmd"
+	# Using /bin/echo to avoid escaping
+	$ECHO "$_output"
+	echo
+	echo "TEST FAILED!"
+	echo
+	exit 1
+    elif ! echo "$_output" | grep -q "$_expected"; then
+	echo \$ "$_cmd"
+	$ECHO "$_output"
+	echo
+	echo "missing expected output '$_expected'"
+	echo
+	echo
+	echo "TEST FAILED!"
+	echo
+	exit 1
+    else
+	if [ -n "${VERBOSE-}" -o -n "${VERBOSE_CMD-}" ]; then
+	    echo \$ "$_cmd"
+	fi
+	if [ -n "${VERBOSE-}" -o -n "${VERBOSE_OUTPUT-}" ]; then
+	    $ECHO "$_output"
+	fi
+    fi
+    set -e
+}
+
+expect_not_output_ok() {
+    set +e
+    local _expected="$1"
+    shift 1
+    _cmd="$@"
+    _output=`$@ 2>&1`
+    if [ $? -ne 0 ]; then
+	echo \$ "$_cmd"
+	# Using /bin/echo to avoid escaping
+	$ECHO "$_output"
+	echo
+	echo "TEST FAILED!"
+	echo
+	exit 1
+    elif echo "$_output" | grep -q "$_expected"; then
+	echo \$ "$_cmd"
+	$ECHO "$_output"
+	echo
+	echo "unexpected output '$_expected'"
+	echo
+	echo
+	echo "TEST FAILED!"
+	echo
+	exit 1
+    else
+	if [ -n "${VERBOSE-}" -o -n "${VERBOSE_CMD-}" ]; then
+	    echo \$ "$_cmd"
+	fi
+	if [ -n "${VERBOSE-}" -o -n "${VERBOSE_OUTPUT-}" ]; then
+	    $ECHO "$_output"
+	fi
+    fi
+    set -e
+}
+
+runtest() {
+    local _testname="$1"
+    if [ -n "${TESTNAME-}" ]; then
+	if ! echo "$_testname" | grep -q "$TESTNAME"; then
+	    return 0
+	fi
+    fi
+
+    pre "$_testname"
+    "$_testname"
+}
+
+# Installation tests
+
+basic_install() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR"
+    try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+    try test -e "$PREFIX_DIR/something-to-install"
+    try test -e "$PREFIX_DIR/dir-to-install/foo"
+    try test -e "$PREFIX_DIR/bin/program"
+    try test -e "$PREFIX_DIR/bin/program2"
+    try test -e "$PREFIX_DIR/bin/bad-bin"
+}
+runtest basic_install
+
+basic_uninstall() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR"
+    try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+    try "$WORK_DIR/package/install.sh --uninstall" --prefix="$PREFIX_DIR"
+    try test ! -e "$PREFIX_DIR/something-to-install"
+    try test ! -e "$PREFIX_DIR/dir-to-install/foo"
+    try test ! -e "$PREFIX_DIR/bin/program"
+    try test ! -e "$PREFIX_DIR/bin/program2"
+    try test ! -e "$PREFIX_DIR/bin/bad-bin"
+    try test ! -e "$PREFIX_DIR/lib/packagelib"
+}
+runtest basic_uninstall
+
+not_installed_files() {
+    mkdir -p "$WORK_DIR/overlay"
+    touch "$WORK_DIR/overlay/not-installed"
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--non-installed-overlay="$WORK_DIR/overlay"
+    try test -e "$WORK_DIR/package/not-installed"
+    try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+    try test ! -e "$PREFIX_DIR/not-installed"
+}
+runtest not_installed_files
+
+tarball_with_package_name() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rustc-nightly
+    try "$WORK_DIR/rustc-nightly/install.sh" --prefix="$PREFIX_DIR"
+    try test -e "$OUT_DIR/rustc-nightly.tar.gz"
+    try test -e "$OUT_DIR/rustc-nightly.tar.xz"
+}
+runtest tarball_with_package_name
+
+install_overwrite_backup() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR"
+    try mkdir -p "$PREFIX_DIR/bin"
+    touch "$PREFIX_DIR/bin/program"
+    try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+    # The existing program was backed up by 'install'
+    try test -e "$PREFIX_DIR/bin/program.old"
+}
+runtest install_overwrite_backup
+
+bulk_directory() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--bulk-dirs=dir-to-install
+    try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+    try test -e "$PREFIX_DIR/something-to-install"
+    try test -e "$PREFIX_DIR/dir-to-install/foo"
+    try test -e "$PREFIX_DIR/bin/program"
+    try test -e "$PREFIX_DIR/bin/program2"
+    try test -e "$PREFIX_DIR/bin/bad-bin"
+    try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR" --uninstall
+    try test ! -e "$PREFIX_DIR/dir-to-install"
+}
+runtest bulk_directory
+
+bulk_directory_overwrite() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--bulk-dirs=dir-to-install
+    try mkdir -p "$PREFIX_DIR/dir-to-install"
+    try touch "$PREFIX_DIR/dir-to-install/overwrite"
+    try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+    # The file that used to exist in the directory no longer does
+    try test ! -e "$PREFIX_DIR/dir-to-install/overwrite"
+    # It was backed up
+    try test -e "$PREFIX_DIR/dir-to-install.old/overwrite"
+}
+runtest bulk_directory_overwrite
+
+bulk_directory_overwrite_existing_backup() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--bulk-dirs=dir-to-install
+    try mkdir -p "$PREFIX_DIR/dir-to-install"
+    try touch "$PREFIX_DIR/dir-to-install/overwrite"
+    # This time we've already got an existing backup of the overwritten directory.
+    # The install should still succeed.
+    try mkdir -p "$PREFIX_DIR/dir-to-install~"
+    try touch "$PREFIX_DIR/dir-to-install~/overwrite"
+    try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+    try test ! -e "$PREFIX_DIR/dir-to-install/overwrite"
+    try test -e "$PREFIX_DIR/dir-to-install~/overwrite"
+}
+runtest bulk_directory_overwrite_existing_backup
+
+nested_bulk_directory() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image4" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--bulk-dirs=dir-to-install/qux
+    try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+    try test -e "$PREFIX_DIR/dir-to-install/qux/bar"
+    try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR" --uninstall
+    try test ! -e "$PREFIX_DIR/dir-to-install/qux"
+}
+runtest nested_bulk_directory
+
+only_bulk_directory_no_files() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image5" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--bulk-dirs=dir-to-install
+    try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+    try test -e "$PREFIX_DIR/dir-to-install/foo"
+    try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR" --uninstall
+    try test ! -e "$PREFIX_DIR/dir-to-install/foo"
+}
+runtest only_bulk_directory_no_files
+
+nested_not_installed_files() {
+    mkdir -p "$WORK_DIR/overlay"
+    touch "$WORK_DIR/overlay/not-installed"
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image4" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--non-installed-overlay="$WORK_DIR/overlay"
+    try test -e "$WORK_DIR/package/not-installed"
+    try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+    try test ! -e "$PREFIX_DIR/not-installed"
+}
+runtest nested_not_installed_files
+
+multiple_components() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR/c1" \
+	--output-dir="$OUT_DIR/c1" \
+	--component-name=rustc
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image3" \
+	--work-dir="$WORK_DIR/c2" \
+	--output-dir="$OUT_DIR/c2" \
+	--component-name=cargo
+    try "$WORK_DIR/c1/package/install.sh" --prefix="$PREFIX_DIR"
+    try "$WORK_DIR/c2/package/install.sh" --prefix="$PREFIX_DIR"
+    try test -e "$PREFIX_DIR/something-to-install"
+    try test -e "$PREFIX_DIR/dir-to-install/foo"
+    try test -e "$PREFIX_DIR/bin/program"
+    try test -e "$PREFIX_DIR/bin/program2"
+    try test -e "$PREFIX_DIR/bin/bad-bin"
+    try test -e "$PREFIX_DIR/bin/cargo"
+    try "$WORK_DIR/c1/package/install.sh" --prefix="$PREFIX_DIR" --uninstall
+    try test ! -e "$PREFIX_DIR/something-to-install"
+    try test ! -e "$PREFIX_DIR/dir-to-install/foo"
+    try test ! -e "$PREFIX_DIR/bin/program"
+    try test ! -e "$PREFIX_DIR/bin/program2"
+    try test ! -e "$PREFIX_DIR/bin/bad-bin"
+    try "$WORK_DIR/c2/package/install.sh" --prefix="$PREFIX_DIR" --uninstall
+    try test ! -e "$PREFIX_DIR/bin/cargo"
+    try test ! -e "$PREFIX_DIR/lib/packagelib"
+}
+runtest multiple_components
+
+uninstall_from_installed_script() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR/c1" \
+	--output-dir="$OUT_DIR/c1" \
+	--component-name=rustc
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image3" \
+	--work-dir="$WORK_DIR/c2" \
+	--output-dir="$OUT_DIR/c2" \
+	--component-name=cargo
+    try "$WORK_DIR/c1/package/install.sh" --prefix="$PREFIX_DIR"
+    try "$WORK_DIR/c2/package/install.sh" --prefix="$PREFIX_DIR"
+    try test -e "$PREFIX_DIR/something-to-install"
+    try test -e "$PREFIX_DIR/dir-to-install/foo"
+    try test -e "$PREFIX_DIR/bin/program"
+    try test -e "$PREFIX_DIR/bin/program2"
+    try test -e "$PREFIX_DIR/bin/bad-bin"
+    try test -e "$PREFIX_DIR/bin/cargo"
+    # All components should be uninstalled by this script
+    try sh "$PREFIX_DIR/lib/packagelib/uninstall.sh"
+    try test ! -e "$PREFIX_DIR/something-to-install"
+    try test ! -e "$PREFIX_DIR/dir-to-install/foo"
+    try test ! -e "$PREFIX_DIR/bin/program"
+    try test ! -e "$PREFIX_DIR/bin/program2"
+    try test ! -e "$PREFIX_DIR/bin/bad-bin"
+    try test ! -e "$PREFIX_DIR/bin/cargo"
+    try test ! -e "$PREFIX_DIR/lib/packagelib"
+}
+runtest uninstall_from_installed_script
+
+uninstall_from_installed_script_with_args_fails() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR/c1" \
+	--output-dir="$OUT_DIR/c1" \
+	--component-name=rustc
+    try "$WORK_DIR/c1/package/install.sh" --prefix="$PREFIX_DIR"
+    expect_output_fail "uninstall.sh does not take any arguments" sh "$PREFIX_DIR/lib/packagelib/uninstall.sh" --prefix=foo
+}
+runtest uninstall_from_installed_script_with_args_fails
+
+# Combined installer tests
+
+combine_installers() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rustc \
+	--component-name=rustc
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image3" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=cargo \
+	--component-name=cargo
+    try sh "$S/combine-installers.sh" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust \
+	--input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
+    try test -e "$PREFIX_DIR/something-to-install"
+    try test -e "$PREFIX_DIR/dir-to-install/foo"
+    try test -e "$PREFIX_DIR/bin/program"
+    try test -e "$PREFIX_DIR/bin/program2"
+    try test -e "$PREFIX_DIR/bin/bad-bin"
+    try test -e "$PREFIX_DIR/bin/cargo"
+    try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR"
+    try test ! -e "$PREFIX_DIR/something-to-install"
+    try test ! -e "$PREFIX_DIR/dir-to-install/foo"
+    try test ! -e "$PREFIX_DIR/bin/program"
+    try test ! -e "$PREFIX_DIR/bin/program2"
+    try test ! -e "$PREFIX_DIR/bin/bad-bin"
+    try test ! -e "$PREFIX_DIR/bin/cargo"
+    try test ! -e "$PREFIX_DIR/lib/packagelib"
+}
+runtest combine_installers
+
+combine_three_installers() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rustc \
+	--component-name=rustc
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image3" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=cargo \
+	--component-name=cargo
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image4" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust-docs \
+	--component-name=rust-docs
+    try sh "$S/combine-installers.sh" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust \
+	--input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
+    try test -e "$PREFIX_DIR/something-to-install"
+    try test -e "$PREFIX_DIR/dir-to-install/foo"
+    try test -e "$PREFIX_DIR/bin/program"
+    try test -e "$PREFIX_DIR/bin/program2"
+    try test -e "$PREFIX_DIR/bin/bad-bin"
+    try test -e "$PREFIX_DIR/bin/cargo"
+    try test -e "$PREFIX_DIR/dir-to-install/qux/bar"
+    try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR"
+    try test ! -e "$PREFIX_DIR/something-to-install"
+    try test ! -e "$PREFIX_DIR/dir-to-install/foo"
+    try test ! -e "$PREFIX_DIR/bin/program"
+    try test ! -e "$PREFIX_DIR/bin/program2"
+    try test ! -e "$PREFIX_DIR/bin/bad-bin"
+    try test ! -e "$PREFIX_DIR/bin/cargo"
+    try test ! -e "$PREFIX_DIR/lib/packagelib"
+    try test ! -e "$PREFIX_DIR/dir-to-install/qux/bar"
+}
+runtest combine_three_installers
+
+combine_installers_with_overlay() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rustc \
+	--component-name=rustc
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image3" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=cargo \
+	--component-name=cargo
+    mkdir -p "$WORK_DIR/overlay"
+    touch "$WORK_DIR/overlay/README"
+    try sh "$S/combine-installers.sh" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust \
+	--input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz" \
+	--non-installed-overlay="$WORK_DIR/overlay"
+    try test -e "$WORK_DIR/rust/README"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
+    try test ! -e "$PREFIX_DIR/README"
+}
+runtest combine_installers_with_overlay
+
+combined_with_bulk_dirs() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rustc \
+	--component-name=rustc \
+	--bulk-dirs=dir-to-install
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image3" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=cargo \
+	--component-name=cargo
+    try sh "$S/combine-installers.sh" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust \
+	--input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
+    try test -e "$PREFIX_DIR/dir-to-install/foo"
+    try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR"
+    try test ! -e "$PREFIX_DIR/dir-to-install"
+}
+runtest combined_with_bulk_dirs
+
+combine_install_with_separate_uninstall() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rustc \
+	--component-name=rustc \
+	--rel-manifest-dir=rustlib
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image3" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=cargo \
+	--component-name=cargo \
+	--rel-manifest-dir=rustlib
+    try sh "$S/combine-installers.sh" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust \
+	--input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz" \
+	--rel-manifest-dir=rustlib
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
+    try test -e "$PREFIX_DIR/something-to-install"
+    try test -e "$PREFIX_DIR/dir-to-install/foo"
+    try test -e "$PREFIX_DIR/bin/program"
+    try test -e "$PREFIX_DIR/bin/program2"
+    try test -e "$PREFIX_DIR/bin/bad-bin"
+    try test -e "$PREFIX_DIR/bin/cargo"
+    try "$WORK_DIR/rustc/install.sh --uninstall" --prefix="$PREFIX_DIR"
+    try test ! -e "$PREFIX_DIR/something-to-install"
+    try test ! -e "$PREFIX_DIR/dir-to-install/foo"
+    try test ! -e "$PREFIX_DIR/bin/program"
+    try test ! -e "$PREFIX_DIR/bin/program2"
+    try test ! -e "$PREFIX_DIR/bin/bad-bin"
+    try "$WORK_DIR/cargo/install.sh --uninstall" --prefix="$PREFIX_DIR"
+    try test ! -e "$PREFIX_DIR/bin/cargo"
+    try test ! -e "$PREFIX_DIR/lib/packagelib"
+}
+runtest combine_install_with_separate_uninstall
+
+select_components_to_install() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rustc \
+	--component-name=rustc
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image3" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=cargo \
+	--component-name=cargo
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image4" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust-docs \
+	--component-name=rust-docs
+    try sh "$S/combine-installers.sh" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust \
+	--input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR" --components=rustc
+    try test -e "$PREFIX_DIR/bin/program"
+    try test ! -e "$PREFIX_DIR/bin/cargo"
+    try test ! -e "$PREFIX_DIR/baz"
+    try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR" --components=cargo
+    try test ! -e "$PREFIX_DIR/bin/program"
+    try test -e "$PREFIX_DIR/bin/cargo"
+    try test ! -e "$PREFIX_DIR/baz"
+    try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR" --components=rust-docs
+    try test ! -e "$PREFIX_DIR/bin/program"
+    try test ! -e "$PREFIX_DIR/bin/cargo"
+    try test -e "$PREFIX_DIR/baz"
+    try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR" --components=rustc,cargo
+    try test -e "$PREFIX_DIR/bin/program"
+    try test -e "$PREFIX_DIR/bin/cargo"
+    try test ! -e "$PREFIX_DIR/baz"
+    try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR" --components=rustc,cargo,rust-docs
+    try test ! -e "$PREFIX_DIR/bin/program"
+    try test ! -e "$PREFIX_DIR/bin/cargo"
+    try test ! -e "$PREFIX_DIR/baz"
+    try test ! -e "$PREFIX_DIR/lib/packagelib"
+}
+runtest select_components_to_install
+
+select_components_to_uninstall() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rustc \
+	--component-name=rustc
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image3" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=cargo \
+	--component-name=cargo
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image4" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust-docs \
+	--component-name=rust-docs
+    try sh "$S/combine-installers.sh" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust \
+	--input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
+    try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR" --components=rustc
+    try test ! -e "$PREFIX_DIR/bin/program"
+    try test -e "$PREFIX_DIR/bin/cargo"
+    try test -e "$PREFIX_DIR/baz"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
+    try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR" --components=cargo
+    try test -e "$PREFIX_DIR/bin/program"
+    try test ! -e "$PREFIX_DIR/bin/cargo"
+    try test -e "$PREFIX_DIR/baz"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
+    try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR" --components=rust-docs
+    try test -e "$PREFIX_DIR/bin/program"
+    try test -e "$PREFIX_DIR/bin/cargo"
+    try test ! -e "$PREFIX_DIR/baz"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
+    try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR" --components=rustc,cargo
+    try test ! -e "$PREFIX_DIR/bin/program"
+    try test ! -e "$PREFIX_DIR/bin/cargo"
+    try test -e "$PREFIX_DIR/baz"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
+    try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR" --components=rustc,cargo,rust-docs
+    try test ! -e "$PREFIX_DIR/bin/program"
+    try test ! -e "$PREFIX_DIR/bin/cargo"
+    try test ! -e "$PREFIX_DIR/baz"
+    try test ! -e "$PREFIX_DIR/lib/packagelib"
+}
+runtest select_components_to_uninstall
+
+invalid_component() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rustc \
+	--component-name=rustc
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image3" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=cargo \
+	--component-name=cargo
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image4" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust-docs \
+	--component-name=rust-docs
+    try sh "$S/combine-installers.sh" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust \
+	--input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+    expect_output_fail "unknown component" "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR" --components=foo
+}
+runtest invalid_component
+
+without_components() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rustc \
+	--component-name=rustc
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image3" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=cargo \
+	--component-name=cargo
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image4" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust-docs \
+	--component-name=rust-docs
+    try sh "$S/combine-installers.sh" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust \
+	--input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR" --without=rust-docs
+    try test -e "$PREFIX_DIR/bin/program"
+    try test -e "$PREFIX_DIR/bin/cargo"
+    try test ! -e "$PREFIX_DIR/baz"
+    try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR" --without=rust-docs,cargo
+    try test -e "$PREFIX_DIR/bin/program"
+    try test ! -e "$PREFIX_DIR/bin/cargo"
+    try test ! -e "$PREFIX_DIR/baz"
+    try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR" --without=rust-docs,rustc
+    try test ! -e "$PREFIX_DIR/bin/program"
+    try test -e "$PREFIX_DIR/bin/cargo"
+    try test ! -e "$PREFIX_DIR/baz"
+    try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR"
+}
+runtest without_components
+
+# --uninstall --without is kind of weird,
+# --without causes components to remain installed
+uninstall_without_components() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rustc \
+	--component-name=rustc
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image3" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=cargo \
+	--component-name=cargo
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image4" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust-docs \
+	--component-name=rust-docs
+    try sh "$S/combine-installers.sh" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust \
+	--input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
+    try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR" --without=rust-docs
+    try test ! -e "$PREFIX_DIR/bin/program"
+    try test ! -e "$PREFIX_DIR/bin/cargo"
+    try test -e "$PREFIX_DIR/baz"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
+    try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR" --without=rust-docs,cargo
+    try test ! -e "$PREFIX_DIR/bin/program"
+    try test -e "$PREFIX_DIR/bin/cargo"
+    try test -e "$PREFIX_DIR/baz"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
+    try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR" --without=rust-docs,rustc
+    try test -e "$PREFIX_DIR/bin/program"
+    try test ! -e "$PREFIX_DIR/bin/cargo"
+    try test -e "$PREFIX_DIR/baz"
+}
+runtest uninstall_without_components
+
+without_any_components() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rustc \
+	--component-name=rustc
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image3" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=cargo \
+	--component-name=cargo
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image4" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust-docs \
+	--component-name=rust-docs
+    try sh "$S/combine-installers.sh" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust \
+	--input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+    expect_output_fail "no components selected for installation" \
+	"$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR" --without=rust-docs,rustc,cargo
+}
+runtest without_any_components
+
+uninstall_without_any_components() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rustc \
+	--component-name=rustc
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image3" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=cargo \
+	--component-name=cargo
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image4" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust-docs \
+	--component-name=rust-docs
+    try sh "$S/combine-installers.sh" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust \
+	--input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
+    expect_output_fail "no components selected for uninstallation" \
+	"$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR" \
+	--uninstall --without=rust-docs,rustc,cargo
+}
+runtest uninstall_without_any_components
+
+list_components() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rustc \
+	--component-name=rustc
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image3" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=cargo \
+	--component-name=cargo
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image4" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust-docs \
+	--component-name=rust-docs
+    try sh "$S/combine-installers.sh" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust \
+	--input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+    expect_output_ok "rustc" "$WORK_DIR/rust/install.sh" --list-components
+    expect_output_ok "cargo" "$WORK_DIR/rust/install.sh" --list-components
+    expect_output_ok "rust-docs" "$WORK_DIR/rust/install.sh" --list-components
+}
+runtest list_components
+
+combined_remains() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rustc \
+	--component-name=rustc
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image3" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=cargo \
+	--component-name=cargo
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image4" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust-docs \
+	--component-name=rust-docs
+    try sh "$S/combine-installers.sh" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust \
+	--input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+    for component in rustc cargo rust-docs; do
+	# rustbuild wants the original extracted package intact too
+	try test -d "$WORK_DIR/$component/$component"
+	try test -d "$WORK_DIR/rust/$component"
+    done
+}
+runtest combined_remains
+
+# Smoke tests
+
+cannot_write_error() {
+    # chmod doesn't work on windows
+    if [ ! -n "${WINDOWS-}" ]; then
+	try sh "$S/gen-installer.sh" \
+	    --image-dir="$TEST_DIR/image1" \
+	    --work-dir="$WORK_DIR" \
+	    --output-dir="$OUT_DIR"
+	chmod u-w "$PREFIX_DIR"
+	expect_fail "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+	chmod u+w "$PREFIX_DIR"
+    fi
+}
+runtest cannot_write_error
+
+cannot_install_to_installer() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=my-package
+    expect_output_fail "cannot install to same directory as installer" \
+	"$WORK_DIR/my-package/install.sh" --prefix="$WORK_DIR/my-package"
+}
+runtest cannot_install_to_installer
+
+upgrade_from_future_installer_error() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--rel-manifest-dir=rustlib
+    try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+    echo 100 > "$PREFIX_DIR/lib/rustlib/rust-installer-version"
+    expect_fail "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+}
+runtest upgrade_from_future_installer_error
+
+destdir() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR"
+    try "$WORK_DIR/package/install.sh" --destdir="$PREFIX_DIR/" --prefix=prefix
+    try test -e "$PREFIX_DIR/prefix/bin/program"
+}
+runtest destdir
+
+destdir_no_trailing_slash() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR"
+    try "$WORK_DIR/package/install.sh" --destdir="$PREFIX_DIR" --prefix=prefix
+    try test -e "$PREFIX_DIR/prefix/bin/program"
+}
+runtest destdir_no_trailing_slash
+
+disable_verify_noop() {
+    # Obsolete --disable-verify flag doesn't generate error
+    try sh "$S/gen-installer.sh" \
+       --image-dir="$TEST_DIR/image1" \
+       --work-dir="$WORK_DIR" \
+       --output-dir="$OUT_DIR"
+    try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR" --disable-verify
+}
+runtest disable_verify_noop
+
+create_log() {
+    try sh "$S/gen-installer.sh" \
+       --image-dir="$TEST_DIR/image1" \
+       --work-dir="$WORK_DIR" \
+       --output-dir="$OUT_DIR"
+    try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+    try test -e "$PREFIX_DIR/lib/packagelib/install.log"
+    local _log="$(cat "$PREFIX_DIR/lib/packagelib/install.log")"
+    if [ -z "$_log" ]; then
+	fail "log is empty"
+    fi
+}
+runtest create_log
+
+leave_log_after_failure() {
+    # chmod doesn't work on windows
+    if [ ! -n "${WINDOWS-}" ]; then
+	try sh "$S/gen-installer.sh" \
+	    --image-dir="$TEST_DIR/image1" \
+	    --work-dir="$WORK_DIR" \
+	    --output-dir="$OUT_DIR"
+	mkdir -p "$PREFIX_DIR/lib/packagelib"
+	touch "$PREFIX_DIR/lib/packagelib/components"
+	chmod u-w "$PREFIX_DIR/lib/packagelib/components"
+	expect_fail "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+	chmod u+w "$PREFIX_DIR/lib/packagelib/components"
+	try test -e "$PREFIX_DIR/lib/packagelib/install.log"
+	local _log="$(cat "$PREFIX_DIR/lib/packagelib/install.log")"
+	if [ -z "$_log" ]; then
+	    fail "log is empty"
+	fi
+	# script should tell user where the logs are
+	if ! grep -q "see logs at" "$PREFIX_DIR/lib/packagelib/install.log"; then
+	    fail "missing log message"
+	fi
+    fi
+}
+runtest leave_log_after_failure
+
+# https://github.com/rust-lang/rust-installer/issues/22
+help() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR"
+    try "$WORK_DIR/package/install.sh" --help
+}
+runtest help
+
+# https://github.com/rust-lang/rust-installer/issues/31
+CDPATH_does_not_destroy_things() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR"
+    cd "$WORK_DIR" || exit 1
+    export CDPATH="../$(basename $WORK_DIR)/foo"
+    try sh "package/install.sh" --prefix="$PREFIX_DIR"
+    cd "$S" || exit 1
+    cd "$PREFIX_DIR" || exit 1
+    export CDPATH="../$(basename $PREFIX_DIR)"
+    try sh "lib/packagelib/uninstall.sh"
+    cd "$S" || exit 1
+    unset CDPATH
+}
+runtest CDPATH_does_not_destroy_things
+
+docdir_default() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image-docdir1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR"
+    try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+    try test -e "$PREFIX_DIR/share/doc/rust/README"
+    try test -e "$PREFIX_DIR/share/doc/rust/rustdocs.txt"
+}
+runtest docdir_default
+
+docdir() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image-docdir1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR"
+    try mkdir "$WORK_DIR/docdir"
+    try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR" --docdir="$WORK_DIR/docdir"
+    try test -e "$WORK_DIR/docdir/README"
+    try test -e "$WORK_DIR/docdir/rustdocs.txt"
+}
+runtest docdir
+
+docdir_combined() {
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image-docdir1" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+        --package-name="rustc" \
+        --component-name="rustc"
+    try sh "$S/gen-installer.sh" \
+	--image-dir="$TEST_DIR/image-docdir2" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+        --package-name="cargo" \
+        --component-name="cargo"
+    try sh "$S/combine-installers.sh" \
+	--work-dir="$WORK_DIR" \
+	--output-dir="$OUT_DIR" \
+	--package-name=rust \
+	--input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz"
+    try mkdir "$WORK_DIR/docdir"
+    try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR" --docdir="$WORK_DIR/docdir"
+    try test -e "$WORK_DIR/docdir/README"
+    try test -e "$WORK_DIR/docdir/rustdocs.txt"
+    try test -e "$WORK_DIR/docdir/README"
+    try test -e "$WORK_DIR/docdir/cargodocs.txt"
+}
+runtest docdir_combined
+
+combine_installers_different_input_compression_formats() {
+    try sh "$S/gen-installer.sh" \
+        --image-dir="$TEST_DIR/image1" \
+        --work-dir="$WORK_DIR" \
+        --output-dir="$OUT_DIR" \
+        --package-name=rustc \
+        --component-name=rustc \
+        --compression-formats=xz
+    try sh "$S/gen-installer.sh" \
+        --image-dir="$TEST_DIR/image3" \
+        --work-dir="$WORK_DIR" \
+        --output-dir="$OUT_DIR" \
+        --package-name=cargo \
+        --component-name=cargo \
+        --compression-formats=gz
+    try sh "$S/combine-installers.sh" \
+        --work-dir="$WORK_DIR" \
+        --output-dir="$OUT_DIR" \
+        --package-name=rust \
+        --input-tarballs="$OUT_DIR/rustc.tar.xz,$OUT_DIR/cargo.tar.gz"
+
+    try test -e "${OUT_DIR}/rust.tar.gz"
+    try test -e "${OUT_DIR}/rust.tar.xz"
+}
+runtest combine_installers_different_input_compression_formats
+
+generate_compression_formats_one() {
+    try sh "$S/gen-installer.sh" \
+        --image-dir="$TEST_DIR/image1" \
+        --work-dir="$WORK_DIR" \
+        --output-dir="$OUT_DIR" \
+        --package-name="rustc" \
+        --component-name="rustc" \
+        --compression-formats="xz"
+
+    try test ! -e "${OUT_DIR}/rustc.tar.gz"
+    try test -e "${OUT_DIR}/rustc.tar.xz"
+}
+runtest generate_compression_formats_one
+
+generate_compression_formats_multiple() {
+    try sh "$S/gen-installer.sh" \
+        --image-dir="$TEST_DIR/image1" \
+        --work-dir="$WORK_DIR" \
+        --output-dir="$OUT_DIR" \
+        --package-name="rustc" \
+        --component-name="rustc" \
+        --compression-formats="gz,xz"
+
+    try test -e "${OUT_DIR}/rustc.tar.gz"
+    try test -e "${OUT_DIR}/rustc.tar.xz"
+}
+runtest generate_compression_formats_multiple
+
+generate_compression_formats_error() {
+    expect_fail sh "$S/gen-installer.sh" \
+        --image-dir="$TEST_DIR/image1" \
+        --work-dir="$WORK_DIR" \
+        --output-dir="$OUT_DIR" \
+        --package-name="rustc" \
+        --component-name="rustc" \
+        --compression-formats="xz,foobar"
+}
+runtest generate_compression_formats_error
+
+combine_compression_formats_one() {
+    try sh "$S/gen-installer.sh" \
+        --image-dir="$TEST_DIR/image1" \
+        --work-dir="$WORK_DIR" \
+        --output-dir="$OUT_DIR" \
+        --package-name=rustc \
+        --component-name=rustc
+    try sh "$S/gen-installer.sh" \
+        --image-dir="$TEST_DIR/image3" \
+        --work-dir="$WORK_DIR" \
+        --output-dir="$OUT_DIR" \
+        --package-name=cargo \
+        --component-name=cargo
+    try sh "$S/combine-installers.sh" \
+        --work-dir="$WORK_DIR" \
+        --output-dir="$OUT_DIR" \
+        --package-name=rust \
+        --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz" \
+        --compression-formats=xz
+
+    try test ! -e "${OUT_DIR}/rust.tar.gz"
+    try test -e "${OUT_DIR}/rust.tar.xz"
+}
+runtest combine_compression_formats_one
+
+combine_compression_formats_multiple() {
+    try sh "$S/gen-installer.sh" \
+        --image-dir="$TEST_DIR/image1" \
+        --work-dir="$WORK_DIR" \
+        --output-dir="$OUT_DIR" \
+        --package-name=rustc \
+        --component-name=rustc
+    try sh "$S/gen-installer.sh" \
+        --image-dir="$TEST_DIR/image3" \
+        --work-dir="$WORK_DIR" \
+        --output-dir="$OUT_DIR" \
+        --package-name=cargo \
+        --component-name=cargo
+    try sh "$S/combine-installers.sh" \
+        --work-dir="$WORK_DIR" \
+        --output-dir="$OUT_DIR" \
+        --package-name=rust \
+        --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz" \
+        --compression-formats=xz,gz
+
+    try test -e "${OUT_DIR}/rust.tar.gz"
+    try test -e "${OUT_DIR}/rust.tar.xz"
+}
+runtest combine_compression_formats_multiple
+
+combine_compression_formats_error() {
+    try sh "$S/gen-installer.sh" \
+        --image-dir="$TEST_DIR/image1" \
+        --work-dir="$WORK_DIR" \
+        --output-dir="$OUT_DIR" \
+        --package-name=rustc \
+        --component-name=rustc
+    try sh "$S/gen-installer.sh" \
+        --image-dir="$TEST_DIR/image3" \
+        --work-dir="$WORK_DIR" \
+        --output-dir="$OUT_DIR" \
+        --package-name=cargo \
+        --component-name=cargo
+    expect_fail sh "$S/combine-installers.sh" \
+        --work-dir="$WORK_DIR" \
+        --output-dir="$OUT_DIR" \
+        --package-name=rust \
+        --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz" \
+        --compression-formats=xz,foobar
+}
+runtest combine_compression_formats_error
+
+tarball_compression_formats_one() {
+    try cp -r "${TEST_DIR}/image1" "${WORK_DIR}/image"
+    try sh "$S/make-tarballs.sh" \
+        --input="${WORK_DIR}/image" \
+        --work-dir="${WORK_DIR}" \
+        --output="${OUT_DIR}/rustc" \
+        --compression-formats="xz"
+
+    try test ! -e "${OUT_DIR}/rustc.tar.gz"
+    try test -e "${OUT_DIR}/rustc.tar.xz"
+}
+runtest tarball_compression_formats_one
+
+tarball_compression_formats_multiple() {
+    try cp -r "${TEST_DIR}/image1" "${WORK_DIR}/image"
+    try sh "$S/make-tarballs.sh" \
+        --input="${WORK_DIR}/image" \
+        --work-dir="${WORK_DIR}" \
+        --output="${OUT_DIR}/rustc" \
+        --compression-formats="xz,gz"
+
+    try test -e "${OUT_DIR}/rustc.tar.gz"
+    try test -e "${OUT_DIR}/rustc.tar.xz"
+}
+runtest tarball_compression_formats_multiple
+
+tarball_compression_formats_error() {
+    try cp -r "${TEST_DIR}/image1" "${WORK_DIR}/image"
+    expect_fail sh "$S/make-tarballs.sh" \
+        --input="${WORK_DIR}/image" \
+        --work-dir="${WORK_DIR}" \
+        --output="${OUT_DIR}/rustc" \
+        --compression-formats="xz,foobar"
+}
+runtest tarball_compression_formats_error
+
+echo
+echo "TOTAL SUCCESS!"
+echo
diff --git a/src/tools/rust-installer/test/image-docdir1/share/doc/rust/README b/src/tools/rust-installer/test/image-docdir1/share/doc/rust/README
new file mode 100644
index 00000000000..871732e64f9
--- /dev/null
+++ b/src/tools/rust-installer/test/image-docdir1/share/doc/rust/README
@@ -0,0 +1 @@
+rust
diff --git a/src/tools/rust-installer/test/image-docdir1/share/doc/rust/rustdocs.txt b/src/tools/rust-installer/test/image-docdir1/share/doc/rust/rustdocs.txt
new file mode 100644
index 00000000000..871732e64f9
--- /dev/null
+++ b/src/tools/rust-installer/test/image-docdir1/share/doc/rust/rustdocs.txt
@@ -0,0 +1 @@
+rust
diff --git a/src/tools/rust-installer/test/image-docdir2/share/doc/cargo/README b/src/tools/rust-installer/test/image-docdir2/share/doc/cargo/README
new file mode 100644
index 00000000000..033a48cafdf
--- /dev/null
+++ b/src/tools/rust-installer/test/image-docdir2/share/doc/cargo/README
@@ -0,0 +1 @@
+cargo
diff --git a/src/tools/rust-installer/test/image-docdir2/share/doc/cargo/cargodocs.txt b/src/tools/rust-installer/test/image-docdir2/share/doc/cargo/cargodocs.txt
new file mode 100644
index 00000000000..033a48cafdf
--- /dev/null
+++ b/src/tools/rust-installer/test/image-docdir2/share/doc/cargo/cargodocs.txt
@@ -0,0 +1 @@
+cargo
diff --git a/src/tools/rust-installer/test/image1/bin/bad-bin b/src/tools/rust-installer/test/image1/bin/bad-bin
new file mode 100644
index 00000000000..b5b0e3234b4
--- /dev/null
+++ b/src/tools/rust-installer/test/image1/bin/bad-bin
@@ -0,0 +1 @@
+#!/bin/bogus
\ No newline at end of file
diff --git a/src/tools/rust-installer/test/image1/bin/program b/src/tools/rust-installer/test/image1/bin/program
new file mode 100755
index 00000000000..96b4b06ad41
--- /dev/null
+++ b/src/tools/rust-installer/test/image1/bin/program
@@ -0,0 +1 @@
+#!/bin/sh
\ No newline at end of file
diff --git a/src/tools/rust-installer/test/image1/bin/program2 b/src/tools/rust-installer/test/image1/bin/program2
new file mode 100755
index 00000000000..96b4b06ad41
--- /dev/null
+++ b/src/tools/rust-installer/test/image1/bin/program2
@@ -0,0 +1 @@
+#!/bin/sh
\ No newline at end of file
diff --git a/src/tools/rust-installer/test/image1/dir-to-install/foo b/src/tools/rust-installer/test/image1/dir-to-install/foo
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/tools/rust-installer/test/image1/dir-to-install/foo
diff --git a/src/tools/rust-installer/test/image1/dir-to-not-install/foo b/src/tools/rust-installer/test/image1/dir-to-not-install/foo
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/tools/rust-installer/test/image1/dir-to-not-install/foo
diff --git a/src/tools/rust-installer/test/image1/something-to-install b/src/tools/rust-installer/test/image1/something-to-install
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/tools/rust-installer/test/image1/something-to-install
diff --git a/src/tools/rust-installer/test/image1/something-to-not-install b/src/tools/rust-installer/test/image1/something-to-not-install
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/tools/rust-installer/test/image1/something-to-not-install
diff --git a/src/tools/rust-installer/test/image2/bin/oldprogram b/src/tools/rust-installer/test/image2/bin/oldprogram
new file mode 100755
index 00000000000..96b4b06ad41
--- /dev/null
+++ b/src/tools/rust-installer/test/image2/bin/oldprogram
@@ -0,0 +1 @@
+#!/bin/sh
\ No newline at end of file
diff --git a/src/tools/rust-installer/test/image2/dir-to-install/bar b/src/tools/rust-installer/test/image2/dir-to-install/bar
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/tools/rust-installer/test/image2/dir-to-install/bar
diff --git a/src/tools/rust-installer/test/image2/something-to-install b/src/tools/rust-installer/test/image2/something-to-install
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/tools/rust-installer/test/image2/something-to-install
diff --git a/src/tools/rust-installer/test/image3/bin/cargo b/src/tools/rust-installer/test/image3/bin/cargo
new file mode 100755
index 00000000000..96b4b06ad41
--- /dev/null
+++ b/src/tools/rust-installer/test/image3/bin/cargo
@@ -0,0 +1 @@
+#!/bin/sh
\ No newline at end of file
diff --git a/src/tools/rust-installer/test/image4/baz b/src/tools/rust-installer/test/image4/baz
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/tools/rust-installer/test/image4/baz
diff --git a/src/tools/rust-installer/test/image4/dir-to-install/qux/bar b/src/tools/rust-installer/test/image4/dir-to-install/qux/bar
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/tools/rust-installer/test/image4/dir-to-install/qux/bar
diff --git a/src/tools/rust-installer/test/image5/dir-to-install/foo b/src/tools/rust-installer/test/image5/dir-to-install/foo
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/tools/rust-installer/test/image5/dir-to-install/foo
diff --git a/src/tools/rustc-workspace-hack/Cargo.toml b/src/tools/rustc-workspace-hack/Cargo.toml
index 84b16a37a33..e088ffbbe77 100644
--- a/src/tools/rustc-workspace-hack/Cargo.toml
+++ b/src/tools/rustc-workspace-hack/Cargo.toml
@@ -84,11 +84,17 @@ libz-sys = { version = "1.1.2" }
 # Ensure default features of regex, which are disabled in some scenarios.
 regex = { version = "1.5.6" }
 serde_json = { version = "1.0.31", features = ["raw_value", "unbounded_depth"] }
-syn = { version = "1", features = ['full', 'visit'] }
+syn = { version = "1", features = ['full', 'visit', 'visit-mut'] } # `visit-mut` required by Cargo via `gix`
 url = { version = "2.0", features = ['serde'] }
 # Ensure default features of rand, which are disabled in some scenarios.
 rand = { version = "0.8.5" }
 
+# Ensure features of `hashbrown`, `smallvec`, and `once_cell`,
+# which are used transitively by Cargo (via `gix`).
+hashbrown = { version = "0.12.3", default-features = false, features = ["inline-more"] }
+once_cell = { version = "1.16.0", default-features = false, features = ["unstable"] }
+smallvec = { version = "1.10.0", features = ["write"] }
+
 [target.'cfg(not(windows))'.dependencies]
 openssl = { version = "0.10.35", optional = true }
 
diff --git a/src/tools/rustdoc-gui/.eslintrc.js b/src/tools/rustdoc-gui/.eslintrc.js
new file mode 100644
index 00000000000..f4aadc07199
--- /dev/null
+++ b/src/tools/rustdoc-gui/.eslintrc.js
@@ -0,0 +1,96 @@
+module.exports = {
+    "env": {
+        "browser": true,
+        "node": true,
+        "es6": true
+    },
+    "extends": "eslint:recommended",
+    "parserOptions": {
+        "ecmaVersion": 2018,
+        "sourceType": "module"
+    },
+    "rules": {
+        "linebreak-style": [
+            "error",
+            "unix"
+        ],
+        "semi": [
+            "error",
+            "always"
+        ],
+        "quotes": [
+            "error",
+            "double"
+        ],
+        "linebreak-style": [
+            "error",
+            "unix"
+        ],
+        "no-trailing-spaces": "error",
+        "no-var": ["error"],
+        "prefer-const": ["error"],
+        "prefer-arrow-callback": ["error"],
+        "brace-style": [
+            "error",
+            "1tbs",
+            { "allowSingleLine": false }
+        ],
+        "keyword-spacing": [
+            "error",
+            { "before": true, "after": true }
+        ],
+        "arrow-spacing": [
+            "error",
+            { "before": true, "after": true }
+        ],
+        "key-spacing": [
+            "error",
+            { "beforeColon": false, "afterColon": true, "mode": "strict" }
+        ],
+        "func-call-spacing": ["error", "never"],
+        "space-infix-ops": "error",
+        "space-before-function-paren": ["error", "never"],
+        "space-before-blocks": "error",
+        "comma-dangle": ["error", "always-multiline"],
+        "comma-style": ["error", "last"],
+        "max-len": ["error", { "code": 100, "tabWidth": 4 }],
+        "eol-last": ["error", "always"],
+        "arrow-parens": ["error", "as-needed"],
+        "no-unused-vars": [
+            "error",
+            {
+                "argsIgnorePattern": "^_",
+                "varsIgnorePattern": "^_"
+            }
+        ],
+        "eqeqeq": "error",
+        "no-const-assign": "error",
+        "no-debugger": "error",
+        "no-dupe-args": "error",
+        "no-dupe-else-if": "error",
+        "no-dupe-keys": "error",
+        "no-duplicate-case": "error",
+        "no-ex-assign": "error",
+        "no-fallthrough": "error",
+        "no-invalid-regexp": "error",
+        "no-import-assign": "error",
+        "no-self-compare": "error",
+        "no-template-curly-in-string": "error",
+        "block-scoped-var": "error",
+        "guard-for-in": "error",
+        "no-alert": "error",
+        "no-confusing-arrow": "error",
+        "no-div-regex": "error",
+        "no-floating-decimal": "error",
+        "no-implicit-globals": "error",
+        "no-implied-eval": "error",
+        "no-label-var": "error",
+        "no-lonely-if": "error",
+        "no-mixed-operators": "error",
+        "no-multi-assign": "error",
+        "no-return-assign": "error",
+        "no-script-url": "error",
+        "no-sequences": "error",
+        "no-div-regex": "error",
+    }
+};
diff --git a/src/tools/rustdoc-gui/tester.js b/src/tools/rustdoc-gui/tester.js
index 2f0ca1ec3d0..72baad606f0 100644
--- a/src/tools/rustdoc-gui/tester.js
+++ b/src/tools/rustdoc-gui/tester.js
@@ -6,8 +6,8 @@
 
 const fs = require("fs");
 const path = require("path");
-const os = require('os');
-const {Options, runTest} = require('browser-ui-test');
+const os = require("os");
+const {Options, runTest} = require("browser-ui-test");
 
 // If a test fails or errors, we will retry it two more times in case it was a flaky failure.
 const NB_RETRY = 3;
@@ -31,7 +31,7 @@ function isNumeric(s) {
 }
 
 function parseOptions(args) {
-    var opts = {
+    const opts = {
         "doc_folder": "",
         "tests_folder": "",
         "files": [],
@@ -42,7 +42,7 @@ function parseOptions(args) {
         "executable_path": null,
         "no_sandbox": false,
     };
-    var correspondances = {
+    const correspondances = {
         "--doc-folder": "doc_folder",
         "--tests-folder": "tests_folder",
         "--debug": "debug",
@@ -52,39 +52,41 @@ function parseOptions(args) {
         "--no-sandbox": "no_sandbox",
     };
 
-    for (var i = 0; i < args.length; ++i) {
-        if (args[i] === "--doc-folder"
-            || args[i] === "--tests-folder"
-            || args[i] === "--file"
-            || args[i] === "--jobs"
-            || args[i] === "--executable-path") {
+    for (let i = 0; i < args.length; ++i) {
+        const arg = args[i];
+        if (arg === "--doc-folder"
+            || arg === "--tests-folder"
+            || arg === "--file"
+            || arg === "--jobs"
+            || arg === "--executable-path") {
             i += 1;
             if (i >= args.length) {
-                console.log("Missing argument after `" + args[i - 1] + "` option.");
+                console.log("Missing argument after `" + arg + "` option.");
                 return null;
             }
-            if (args[i - 1] === "--jobs") {
-                if (!isNumeric(args[i])) {
+            const arg_value = args[i];
+            if (arg === "--jobs") {
+                if (!isNumeric(arg_value)) {
                     console.log(
-                        "`--jobs` option expects a positive number, found `" + args[i] + "`");
+                        "`--jobs` option expects a positive number, found `" + arg_value + "`");
                     return null;
                 }
-                opts["jobs"] = parseInt(args[i]);
-            } else if (args[i - 1] !== "--file") {
-                opts[correspondances[args[i - 1]]] = args[i];
+                opts["jobs"] = parseInt(arg_value);
+            } else if (arg !== "--file") {
+                opts[correspondances[arg]] = arg_value;
             } else {
-                opts["files"].push(args[i]);
+                opts["files"].push(arg_value);
             }
-        } else if (args[i] === "--help") {
+        } else if (arg === "--help") {
             showHelp();
             process.exit(0);
-        } else if (args[i] === "--no-sandbox") {
+        } else if (arg === "--no-sandbox") {
             console.log("`--no-sandbox` is being used. Be very careful!");
-            opts[correspondances[args[i]]] = true;
-        } else if (correspondances[args[i]]) {
-            opts[correspondances[args[i]]] = true;
+            opts[correspondances[arg]] = true;
+        } else if (correspondances[arg]) {
+            opts[correspondances[arg]] = true;
         } else {
-            console.log("Unknown option `" + args[i] + "`.");
+            console.log("Unknown option `" + arg + "`.");
             console.log("Use `--help` to see the list of options");
             return null;
         }
@@ -186,7 +188,7 @@ function createEmptyResults() {
 }
 
 async function main(argv) {
-    let opts = parseOptions(argv.slice(2));
+    const opts = parseOptions(argv.slice(2));
     if (opts === null) {
         process.exit(1);
     }
@@ -198,7 +200,7 @@ async function main(argv) {
     const framework_options = new Options();
     try {
         // This is more convenient that setting fields one by one.
-        let args = [
+        const args = [
             "--variable", "DOC_PATH", opts["doc_folder"], "--enable-fail-on-js-error",
             "--allow-file-access-from-files",
         ];
@@ -232,7 +234,7 @@ async function main(argv) {
     } else {
         files = opts["files"];
     }
-    files = files.filter(file => path.extname(file) == ".goml");
+    files = files.filter(file => path.extname(file) === ".goml");
     if (files.length === 0) {
         console.error("rustdoc-gui: No test selected");
         process.exit(2);
@@ -257,7 +259,7 @@ async function main(argv) {
 
     // We catch this "event" to display a nicer message in case of unexpected exit (because of a
     // missing `--no-sandbox`).
-    const exitHandling = (code) => {
+    const exitHandling = () => {
         if (!opts["no_sandbox"]) {
             console.log("");
             console.log(
@@ -266,10 +268,10 @@ async function main(argv) {
             console.log("");
         }
     };
-    process.on('exit', exitHandling);
+    process.on("exit", exitHandling);
 
     const originalFilesLen = files.length;
-    let results = createEmptyResults();
+    const results = createEmptyResults();
     const status_bar = char_printer(files.length);
 
     let new_results;
@@ -279,7 +281,7 @@ async function main(argv) {
         Array.prototype.push.apply(results.successful, new_results.successful);
         // We generate the new list of files with the previously failing tests.
         files = Array.prototype.concat(new_results.failed, new_results.errored).map(
-            f => f['file_name']);
+            f => f["file_name"]);
         if (files.length > originalFilesLen / 2) {
             // If we have too many failing tests, it's very likely not flaky failures anymore so
             // no need to retry.
diff --git a/src/tools/rustdoc-js/.eslintrc.js b/src/tools/rustdoc-js/.eslintrc.js
new file mode 100644
index 00000000000..4ab3a315733
--- /dev/null
+++ b/src/tools/rustdoc-js/.eslintrc.js
@@ -0,0 +1,96 @@
+module.exports = {
+    "env": {
+        "browser": true,
+        "node": true,
+        "es6": true
+    },
+    "extends": "eslint:recommended",
+    "parserOptions": {
+        "ecmaVersion": 2015,
+        "sourceType": "module"
+    },
+    "rules": {
+        "linebreak-style": [
+            "error",
+            "unix"
+        ],
+        "semi": [
+            "error",
+            "always"
+        ],
+        "quotes": [
+            "error",
+            "double"
+        ],
+        "linebreak-style": [
+            "error",
+            "unix"
+        ],
+        "no-trailing-spaces": "error",
+        "no-var": ["error"],
+        "prefer-const": ["error"],
+        "prefer-arrow-callback": ["error"],
+        "brace-style": [
+            "error",
+            "1tbs",
+            { "allowSingleLine": false }
+        ],
+        "keyword-spacing": [
+            "error",
+            { "before": true, "after": true }
+        ],
+        "arrow-spacing": [
+            "error",
+            { "before": true, "after": true }
+        ],
+        "key-spacing": [
+            "error",
+            { "beforeColon": false, "afterColon": true, "mode": "strict" }
+        ],
+        "func-call-spacing": ["error", "never"],
+        "space-infix-ops": "error",
+        "space-before-function-paren": ["error", "never"],
+        "space-before-blocks": "error",
+        "comma-dangle": ["error", "always-multiline"],
+        "comma-style": ["error", "last"],
+        "max-len": ["error", { "code": 100, "tabWidth": 4 }],
+        "eol-last": ["error", "always"],
+        "arrow-parens": ["error", "as-needed"],
+        "no-unused-vars": [
+            "error",
+            {
+                "argsIgnorePattern": "^_",
+                "varsIgnorePattern": "^_"
+            }
+        ],
+        "eqeqeq": "error",
+        "no-const-assign": "error",
+        "no-debugger": "error",
+        "no-dupe-args": "error",
+        "no-dupe-else-if": "error",
+        "no-dupe-keys": "error",
+        "no-duplicate-case": "error",
+        "no-ex-assign": "error",
+        "no-fallthrough": "error",
+        "no-invalid-regexp": "error",
+        "no-import-assign": "error",
+        "no-self-compare": "error",
+        "no-template-curly-in-string": "error",
+        "block-scoped-var": "error",
+        "guard-for-in": "error",
+        "no-alert": "error",
+        "no-confusing-arrow": "error",
+        "no-div-regex": "error",
+        "no-floating-decimal": "error",
+        "no-implicit-globals": "error",
+        "no-implied-eval": "error",
+        "no-label-var": "error",
+        "no-lonely-if": "error",
+        "no-mixed-operators": "error",
+        "no-multi-assign": "error",
+        "no-return-assign": "error",
+        "no-script-url": "error",
+        "no-sequences": "error",
+        "no-div-regex": "error",
+    }
+};
diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js
index e617ceba3b9..8d46a8ce7f1 100644
--- a/src/tools/rustdoc-js/tester.js
+++ b/src/tools/rustdoc-js/tester.js
@@ -1,5 +1,5 @@
-const fs = require('fs');
-const path = require('path');
+const fs = require("fs");
+const path = require("path");
 
 function loadContent(content) {
     const Module = module.constructor;
@@ -15,7 +15,7 @@ function loadContent(content) {
 }
 
 function readFile(filePath) {
-    return fs.readFileSync(filePath, 'utf8');
+    return fs.readFileSync(filePath, "utf8");
 }
 
 function contentToDiffLine(key, value) {
@@ -25,41 +25,41 @@ function contentToDiffLine(key, value) {
 // This function is only called when no matching result was found and therefore will only display
 // the diff between the two items.
 function betterLookingDiff(entry, data) {
-    let output = ' {\n';
-    const spaces = '     ';
+    let output = " {\n";
+    const spaces = "     ";
     for (const key in entry) {
-        if (!entry.hasOwnProperty(key)) {
+        if (!Object.prototype.hasOwnProperty.call(entry, key)) {
             continue;
         }
-        if (!data || !data.hasOwnProperty(key)) {
-            output += '-' + spaces + contentToDiffLine(key, entry[key]) + '\n';
+        if (!data || !Object.prototype.hasOwnProperty.call(data, key)) {
+            output += "-" + spaces + contentToDiffLine(key, entry[key]) + "\n";
             continue;
         }
         const value = data[key];
         if (value !== entry[key]) {
-            output += '-' + spaces + contentToDiffLine(key, entry[key]) + '\n';
-            output += '+' + spaces + contentToDiffLine(key, value) + '\n';
+            output += "-" + spaces + contentToDiffLine(key, entry[key]) + "\n";
+            output += "+" + spaces + contentToDiffLine(key, value) + "\n";
         } else {
-            output += spaces + contentToDiffLine(key, value) + '\n';
+            output += spaces + contentToDiffLine(key, value) + "\n";
         }
     }
-    return output + ' }';
+    return output + " }";
 }
 
 function lookForEntry(entry, data) {
     return data.findIndex(data_entry => {
         let allGood = true;
         for (const key in entry) {
-            if (!entry.hasOwnProperty(key)) {
+            if (!Object.prototype.hasOwnProperty.call(entry, key)) {
                 continue;
             }
             let value = data_entry[key];
             // To make our life easier, if there is a "parent" type, we add it to the path.
-            if (key === 'path' && data_entry['parent'] !== undefined) {
+            if (key === "path" && data_entry["parent"] !== undefined) {
                 if (value.length > 0) {
-                    value += '::' + data_entry['parent']['name'];
+                    value += "::" + data_entry["parent"]["name"];
                 } else {
-                    value = data_entry['parent']['name'];
+                    value = data_entry["parent"]["name"];
                 }
             }
             if (value !== entry[key]) {
@@ -95,7 +95,7 @@ function checkNeededFields(fullPath, expected, error_text, queryName, position)
         fieldsToCheck = [];
     }
     for (const field of fieldsToCheck) {
-        if (!expected.hasOwnProperty(field)) {
+        if (!Object.prototype.hasOwnProperty.call(expected, field)) {
             let text = `${queryName}==> Mandatory key \`${field}\` is not present`;
             if (fullPath.length > 0) {
                 text += ` in field \`${fullPath}\``;
@@ -117,22 +117,22 @@ function valueCheck(fullPath, expected, result, error_text, queryName) {
                 error_text.push(`${queryName}==> EXPECTED has extra value in array from field ` +
                     `\`${fullPath}\` (position ${i}): \`${JSON.stringify(expected[i])}\``);
             } else {
-                valueCheck(fullPath + '[' + i + ']', expected[i], result[i], error_text, queryName);
+                valueCheck(fullPath + "[" + i + "]", expected[i], result[i], error_text, queryName);
             }
         }
         for (; i < result.length; ++i) {
             error_text.push(`${queryName}==> RESULT has extra value in array from field ` +
                 `\`${fullPath}\` (position ${i}): \`${JSON.stringify(result[i])}\` ` +
-                'compared to EXPECTED');
+                "compared to EXPECTED");
         }
     } else if (expected !== null && typeof expected !== "undefined" &&
-               expected.constructor == Object) {
+               expected.constructor == Object) { // eslint-disable-line eqeqeq
         for (const key in expected) {
-            if (!expected.hasOwnProperty(key)) {
+            if (!Object.prototype.hasOwnProperty.call(expected, key)) {
                 continue;
             }
-            if (!result.hasOwnProperty(key)) {
-                error_text.push('==> Unknown key "' + key + '"');
+            if (!Object.prototype.hasOwnProperty.call(result, key)) {
+                error_text.push("==> Unknown key \"" + key + "\"");
                 break;
             }
             let result_v = result[key];
@@ -147,13 +147,13 @@ function valueCheck(fullPath, expected, result, error_text, queryName) {
                 });
                 result_v = result_v.join("");
             }
-            const obj_path = fullPath + (fullPath.length > 0 ? '.' : '') + key;
+            const obj_path = fullPath + (fullPath.length > 0 ? "." : "") + key;
             valueCheck(obj_path, expected[key], result_v, error_text, queryName);
         }
     } else {
         const expectedValue = JSON.stringify(expected);
         const resultValue = JSON.stringify(result);
-        if (expectedValue != resultValue) {
+        if (expectedValue !== resultValue) {
             error_text.push(`${queryName}==> Different values for field \`${fullPath}\`:\n` +
                 `EXPECTED: \`${expectedValue}\`\nRESULT:   \`${resultValue}\``);
         }
@@ -164,7 +164,7 @@ function runParser(query, expected, parseQuery, queryName) {
     const error_text = [];
     checkNeededFields("", expected, error_text, queryName, null);
     if (error_text.length === 0) {
-        valueCheck('', expected, parseQuery(query), error_text, queryName);
+        valueCheck("", expected, parseQuery(query), error_text, queryName);
     }
     return error_text;
 }
@@ -177,16 +177,16 @@ function runSearch(query, expected, doSearch, loadedFile, queryName) {
     const error_text = [];
 
     for (const key in expected) {
-        if (!expected.hasOwnProperty(key)) {
+        if (!Object.prototype.hasOwnProperty.call(expected, key)) {
             continue;
         }
-        if (!results.hasOwnProperty(key)) {
-            error_text.push('==> Unknown key "' + key + '"');
+        if (!Object.prototype.hasOwnProperty.call(results, key)) {
+            error_text.push("==> Unknown key \"" + key + "\"");
             break;
         }
         const entry = expected[key];
 
-        if (exact_check == true && entry.length !== results[key].length) {
+        if (exact_check && entry.length !== results[key].length) {
             error_text.push(queryName + "==> Expected exactly " + entry.length +
                             " results but found " + results[key].length + " in '" + key + "'");
         }
@@ -268,7 +268,7 @@ function runCheck(loadedFile, key, callback) {
 function runChecks(testFile, doSearch, parseQuery) {
     let checkExpected = false;
     let checkParsed = false;
-    let testFileContent = readFile(testFile) + 'exports.QUERY = QUERY;';
+    let testFileContent = readFile(testFile) + "exports.QUERY = QUERY;";
 
     if (testFileContent.indexOf("FILTER_CRATE") !== -1) {
         testFileContent += "exports.FILTER_CRATE = FILTER_CRATE;";
@@ -277,11 +277,11 @@ function runChecks(testFile, doSearch, parseQuery) {
     }
 
     if (testFileContent.indexOf("\nconst EXPECTED") !== -1) {
-        testFileContent += 'exports.EXPECTED = EXPECTED;';
+        testFileContent += "exports.EXPECTED = EXPECTED;";
         checkExpected = true;
     }
     if (testFileContent.indexOf("\nconst PARSED") !== -1) {
-        testFileContent += 'exports.PARSED = PARSED;';
+        testFileContent += "exports.PARSED = PARSED;";
         checkParsed = true;
     }
     if (!checkParsed && !checkExpected) {
@@ -325,7 +325,7 @@ function loadSearchJS(doc_folder, resource_suffix) {
     const searchWords = searchModule.initSearch(searchIndex.searchIndex);
 
     return {
-        doSearch: function (queryStr, filterCrate, currentCrate) {
+        doSearch: function(queryStr, filterCrate, currentCrate) {
             return searchModule.execQuery(searchModule.parseQuery(queryStr), searchWords,
                 filterCrate, currentCrate);
         },
@@ -361,22 +361,24 @@ function parseOptions(args) {
     };
 
     for (let i = 0; i < args.length; ++i) {
-        if (correspondences.hasOwnProperty(args[i])) {
+        const arg = args[i];
+        if (Object.prototype.hasOwnProperty.call(correspondences, arg)) {
             i += 1;
             if (i >= args.length) {
-                console.log("Missing argument after `" + args[i - 1] + "` option.");
+                console.log("Missing argument after `" + arg + "` option.");
                 return null;
             }
-            if (args[i - 1] !== "--test-file") {
-                opts[correspondences[args[i - 1]]] = args[i];
+            const arg_value = args[i];
+            if (arg !== "--test-file") {
+                opts[correspondences[arg]] = arg_value;
             } else {
-                opts[correspondences[args[i - 1]]].push(args[i]);
+                opts[correspondences[arg]].push(arg_value);
             }
-        } else if (args[i] === "--help") {
+        } else if (arg === "--help") {
             showHelp();
             process.exit(0);
         } else {
-            console.log("Unknown option `" + args[i] + "`.");
+            console.log("Unknown option `" + arg + "`.");
             console.log("Use `--help` to see the list of options");
             return null;
         }
@@ -405,17 +407,17 @@ function main(argv) {
     );
     let errors = 0;
 
-    const doSearch = function (queryStr, filterCrate) {
+    const doSearch = function(queryStr, filterCrate) {
         return parseAndSearch.doSearch(queryStr, filterCrate, opts["crate_name"]);
     };
 
     if (opts["test_file"].length !== 0) {
-        opts["test_file"].forEach(function (file) {
+        opts["test_file"].forEach(file => {
             process.stdout.write(`Testing ${file} ... `);
             errors += runChecks(file, doSearch, parseAndSearch.parseQuery);
         });
     } else if (opts["test_folder"].length !== 0) {
-        fs.readdirSync(opts["test_folder"]).forEach(function (file) {
+        fs.readdirSync(opts["test_folder"]).forEach(file => {
             if (!file.endsWith(".js")) {
                 return;
             }
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index bddfdcfaf19..d71fa5c8be5 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -42,13 +42,16 @@ const EXCEPTIONS: &[(&str, &str)] = &[
     ("bitmaps", "MPL-2.0+"),                                 // cargo via im-rc
     ("fiat-crypto", "MIT OR Apache-2.0 OR BSD-1-Clause"),    // cargo via pasetors
     ("subtle", "BSD-3-Clause"),                              // cargo via pasetors
+    ("dunce", "CC0-1.0 OR MIT-0"),                           // cargo via gix (and dev dependency)
+    ("imara-diff", "Apache-2.0"),                            // cargo via gix
+    ("sha1_smol", "BSD-3-Clause"),                           // cargo via gix
+    ("unicode-bom", "Apache-2.0"),                           // cargo via gix
     ("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot
     ("snap", "BSD-3-Clause"),    // rustc
     ("fluent-langneg", "Apache-2.0"), // rustc (fluent translations)
     ("self_cell", "Apache-2.0"), // rustc (fluent translations)
     // FIXME: this dependency violates the documentation comment above:
     ("fortanix-sgx-abi", "MPL-2.0"), // libstd but only for `sgx` target
-    ("dunce", "CC0-1.0"),            // cargo (dev dependency)
     ("similar", "Apache-2.0"),       // cargo (dev dependency)
     ("normalize-line-endings", "Apache-2.0"), // cargo (dev dependency)
     ("dissimilar", "Apache-2.0"),    // rustdoc, rustc_lexer (few tests) via expect-test, (dev deps)
diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs
index 33938ac9a0a..868579b4b1a 100644
--- a/src/tools/tidy/src/pal.rs
+++ b/src/tools/tidy/src/pal.rs
@@ -128,6 +128,7 @@ fn check_cfgs(
             || cfg.contains("target_env")
             || cfg.contains("target_abi")
             || cfg.contains("target_vendor")
+            || cfg.contains("target_family")
             || cfg.contains("unix")
             || cfg.contains("windows");
 
diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs
index 9ecb30529cc..75a4586cb7f 100644
--- a/src/tools/tidy/src/style.rs
+++ b/src/tools/tidy/src/style.rs
@@ -171,9 +171,9 @@ fn contains_ignore_directive(can_contain: bool, contents: &str, check: &str) ->
 }
 
 macro_rules! suppressible_tidy_err {
-    ($err:ident, $skip:ident, $msg:expr) => {
+    ($err:ident, $skip:ident, $msg:literal) => {
         if let Directive::Deny = $skip {
-            $err($msg);
+            $err(&format!($msg));
         } else {
             $skip = Directive::Ignore(true);
         }
@@ -300,10 +300,13 @@ pub fn check(path: &Path, bad: &mut bool) {
             contains_ignore_directive(can_contain, &contents, "leading-newlines");
         let mut skip_copyright = contains_ignore_directive(can_contain, &contents, "copyright");
         let mut skip_dbg = contains_ignore_directive(can_contain, &contents, "dbg");
+        let mut skip_odd_backticks =
+            contains_ignore_directive(can_contain, &contents, "odd-backticks");
         let mut leading_new_lines = false;
         let mut trailing_new_lines = 0;
         let mut lines = 0;
         let mut last_safety_comment = false;
+        let mut comment_block: Option<(usize, usize)> = None;
         let is_test = file.components().any(|c| c.as_os_str() == "tests");
         // scanning the whole file for multiple needles at once is more efficient than
         // executing lines times needles separate searches.
@@ -351,7 +354,7 @@ pub fn check(path: &Path, bad: &mut bool) {
                 suppressible_tidy_err!(
                     err,
                     skip_line_length,
-                    &format!("line longer than {max_columns} chars")
+                    "line longer than {max_columns} chars"
                 );
             }
             if !is_style_file && line.contains('\t') {
@@ -415,15 +418,55 @@ pub fn check(path: &Path, bad: &mut bool) {
 
             // For now only enforce in compiler
             let is_compiler = || file.components().any(|c| c.as_os_str() == "compiler");
-            if is_compiler()
-                && line.contains("//")
-                && line
-                    .chars()
-                    .collect::<Vec<_>>()
-                    .windows(4)
-                    .any(|cs| matches!(cs, ['.', ' ', ' ', last] if last.is_alphabetic()))
-            {
-                err(DOUBLE_SPACE_AFTER_DOT)
+
+            if is_compiler() {
+                if line.contains("//")
+                    && line
+                        .chars()
+                        .collect::<Vec<_>>()
+                        .windows(4)
+                        .any(|cs| matches!(cs, ['.', ' ', ' ', last] if last.is_alphabetic()))
+                {
+                    err(DOUBLE_SPACE_AFTER_DOT)
+                }
+
+                if filename.ends_with(".ftl") {
+                    let line_backticks = trimmed.chars().filter(|ch| *ch == '`').count();
+                    if line_backticks % 2 == 1 {
+                        suppressible_tidy_err!(err, skip_odd_backticks, "odd number of backticks");
+                    }
+                } else if trimmed.contains("//") {
+                    let (start_line, mut backtick_count) = comment_block.unwrap_or((i + 1, 0));
+                    let line_backticks = trimmed.chars().filter(|ch| *ch == '`').count();
+                    let comment_text = trimmed.split("//").nth(1).unwrap();
+                    // This check ensures that we don't lint for code that has `//` in a string literal
+                    if line_backticks % 2 == 1 {
+                        backtick_count += comment_text.chars().filter(|ch| *ch == '`').count();
+                    }
+                    comment_block = Some((start_line, backtick_count));
+                } else {
+                    if let Some((start_line, backtick_count)) = comment_block.take() {
+                        if backtick_count % 2 == 1 {
+                            let mut err = |msg: &str| {
+                                tidy_error!(bad, "{}:{start_line}: {msg}", file.display());
+                            };
+                            let block_len = (i + 1) - start_line;
+                            if block_len == 1 {
+                                suppressible_tidy_err!(
+                                    err,
+                                    skip_odd_backticks,
+                                    "comment with odd number of backticks"
+                                );
+                            } else {
+                                suppressible_tidy_err!(
+                                    err,
+                                    skip_odd_backticks,
+                                    "{block_len}-line comment block with odd number of backticks"
+                                );
+                            }
+                        }
+                    }
+                }
             }
         }
         if leading_new_lines {
@@ -441,7 +484,7 @@ pub fn check(path: &Path, bad: &mut bool) {
             n => suppressible_tidy_err!(
                 err,
                 skip_trailing_newlines,
-                &format!("too many trailing newlines ({n})")
+                "too many trailing newlines ({n})"
             ),
         };
         if lines > LINES {
diff --git a/tests/mir-opt/building/custom/terminators.drop_first.built.after.mir b/tests/mir-opt/building/custom/terminators.drop_first.built.after.mir
index c903e594696..ada78c0fc78 100644
--- a/tests/mir-opt/building/custom/terminators.drop_first.built.after.mir
+++ b/tests/mir-opt/building/custom/terminators.drop_first.built.after.mir
@@ -4,10 +4,11 @@ fn drop_first(_1: WriteOnDrop<'_>, _2: WriteOnDrop<'_>) -> () {
     let mut _0: ();                      // return place in scope 0 at $DIR/terminators.rs:+0:59: +0:59
 
     bb0: {
-        replace(_1 <- move _2) -> bb1;   // scope 0 at $DIR/terminators.rs:+3:13: +3:49
+        drop(_1) -> bb1;                 // scope 0 at $DIR/terminators.rs:+3:13: +3:30
     }
 
     bb1: {
-        return;                          // scope 0 at $DIR/terminators.rs:+7:13: +7:21
+        _1 = move _2;                    // scope 0 at $DIR/terminators.rs:+7:13: +7:24
+        return;                          // scope 0 at $DIR/terminators.rs:+8:13: +8:21
     }
 }
diff --git a/tests/mir-opt/building/custom/terminators.rs b/tests/mir-opt/building/custom/terminators.rs
index c23233fcf9a..f1240566168 100644
--- a/tests/mir-opt/building/custom/terminators.rs
+++ b/tests/mir-opt/building/custom/terminators.rs
@@ -48,10 +48,11 @@ impl<'a> Drop for WriteOnDrop<'a> {
 fn drop_first<'a>(a: WriteOnDrop<'a>, b: WriteOnDrop<'a>) {
     mir!(
         {
-            DropAndReplace(a, Move(b), retblock)
+            Drop(a, retblock)
         }
 
         retblock = {
+            a = Move(b);
             Return()
         }
     )
diff --git a/tests/mir-opt/building/match_false_edges.full_tested_match.built.after.mir b/tests/mir-opt/building/match_false_edges.full_tested_match.built.after.mir
index cb36bc64da6..0e6de839df3 100644
--- a/tests/mir-opt/building/match_false_edges.full_tested_match.built.after.mir
+++ b/tests/mir-opt/building/match_false_edges.full_tested_match.built.after.mir
@@ -100,6 +100,7 @@ fn full_tested_match() -> () {
     }
 
     bb11: {
+        PlaceMention(_1);                // scope 0 at $DIR/match_false_edges.rs:+1:13: +5:6
         StorageDead(_2);                 // scope 0 at $DIR/match_false_edges.rs:+5:6: +5:7
         StorageDead(_1);                 // scope 0 at $DIR/match_false_edges.rs:+5:6: +5:7
         _0 = const ();                   // scope 0 at $DIR/match_false_edges.rs:+0:28: +6:2
diff --git a/tests/mir-opt/building/match_false_edges.full_tested_match2.built.after.mir b/tests/mir-opt/building/match_false_edges.full_tested_match2.built.after.mir
index 7f8755faac6..37e6b1cd4b4 100644
--- a/tests/mir-opt/building/match_false_edges.full_tested_match2.built.after.mir
+++ b/tests/mir-opt/building/match_false_edges.full_tested_match2.built.after.mir
@@ -100,6 +100,7 @@ fn full_tested_match2() -> () {
     }
 
     bb11: {
+        PlaceMention(_1);                // scope 0 at $DIR/match_false_edges.rs:+1:13: +5:6
         StorageDead(_2);                 // scope 0 at $DIR/match_false_edges.rs:+5:6: +5:7
         StorageDead(_1);                 // scope 0 at $DIR/match_false_edges.rs:+5:6: +5:7
         _0 = const ();                   // scope 0 at $DIR/match_false_edges.rs:+0:29: +6:2
diff --git a/tests/mir-opt/building/match_false_edges.main.built.after.mir b/tests/mir-opt/building/match_false_edges.main.built.after.mir
index e8b93f4371e..7b8983138d2 100644
--- a/tests/mir-opt/building/match_false_edges.main.built.after.mir
+++ b/tests/mir-opt/building/match_false_edges.main.built.after.mir
@@ -162,6 +162,7 @@ fn main() -> () {
     }
 
     bb19: {
+        PlaceMention(_1);                // scope 0 at $DIR/match_false_edges.rs:+1:13: +6:6
         StorageDead(_2);                 // scope 0 at $DIR/match_false_edges.rs:+6:6: +6:7
         StorageDead(_1);                 // scope 0 at $DIR/match_false_edges.rs:+6:6: +6:7
         _0 = const ();                   // scope 0 at $DIR/match_false_edges.rs:+0:11: +7:2
diff --git a/tests/mir-opt/building/uniform_array_move_out.move_out_by_subslice.built.after.mir b/tests/mir-opt/building/uniform_array_move_out.move_out_by_subslice.built.after.mir
index 234cd083977..d80a77fefe5 100644
--- a/tests/mir-opt/building/uniform_array_move_out.move_out_by_subslice.built.after.mir
+++ b/tests/mir-opt/building/uniform_array_move_out.move_out_by_subslice.built.after.mir
@@ -77,6 +77,7 @@ fn move_out_by_subslice() -> () {
     bb6: {
         StorageDead(_2);                 // scope 0 at $DIR/uniform_array_move_out.rs:+1:26: +1:27
         FakeRead(ForLet(None), _1);      // scope 0 at $DIR/uniform_array_move_out.rs:+1:9: +1:10
+        PlaceMention(_1);                // scope 1 at $DIR/uniform_array_move_out.rs:+2:21: +2:22
         StorageLive(_12);                // scope 1 at $DIR/uniform_array_move_out.rs:+2:10: +2:12
         _12 = move _1[0..2];             // scope 1 at $DIR/uniform_array_move_out.rs:+2:10: +2:12
         _0 = const ();                   // scope 0 at $DIR/uniform_array_move_out.rs:+0:27: +3:2
diff --git a/tests/mir-opt/building/uniform_array_move_out.move_out_from_end.built.after.mir b/tests/mir-opt/building/uniform_array_move_out.move_out_from_end.built.after.mir
index 24a189498d3..5f5c18c9f0c 100644
--- a/tests/mir-opt/building/uniform_array_move_out.move_out_from_end.built.after.mir
+++ b/tests/mir-opt/building/uniform_array_move_out.move_out_from_end.built.after.mir
@@ -77,6 +77,7 @@ fn move_out_from_end() -> () {
     bb6: {
         StorageDead(_2);                 // scope 0 at $DIR/uniform_array_move_out.rs:+1:26: +1:27
         FakeRead(ForLet(None), _1);      // scope 0 at $DIR/uniform_array_move_out.rs:+1:9: +1:10
+        PlaceMention(_1);                // scope 1 at $DIR/uniform_array_move_out.rs:+2:20: +2:21
         StorageLive(_12);                // scope 1 at $DIR/uniform_array_move_out.rs:+2:14: +2:16
         _12 = move _1[1 of 2];           // scope 1 at $DIR/uniform_array_move_out.rs:+2:14: +2:16
         _0 = const ();                   // scope 0 at $DIR/uniform_array_move_out.rs:+0:24: +3:2
diff --git a/tests/mir-opt/copy-prop/reborrow.demiraw.CopyProp.diff b/tests/mir-opt/copy-prop/reborrow.demiraw.CopyProp.diff
new file mode 100644
index 00000000000..6c32b675a5b
--- /dev/null
+++ b/tests/mir-opt/copy-prop/reborrow.demiraw.CopyProp.diff
@@ -0,0 +1,56 @@
+- // MIR for `demiraw` before CopyProp
++ // MIR for `demiraw` after CopyProp
+  
+  fn demiraw(_1: u8) -> () {
+      debug x => _1;                       // in scope 0 at $DIR/reborrow.rs:+0:12: +0:17
+      let mut _0: ();                      // return place in scope 0 at $DIR/reborrow.rs:+0:23: +0:23
+      let _2: *mut u8;                     // in scope 0 at $DIR/reborrow.rs:+1:9: +1:10
+      let mut _4: &mut u8;                 // in scope 0 at $DIR/reborrow.rs:+2:22: +2:29
+      let _6: ();                          // in scope 0 at $DIR/reborrow.rs:+4:5: +4:14
+      let mut _7: *mut u8;                 // in scope 0 at $DIR/reborrow.rs:+4:12: +4:13
+      scope 1 {
+          debug a => _2;                   // in scope 1 at $DIR/reborrow.rs:+1:9: +1:10
+          let _3: &mut u8;                 // in scope 1 at $DIR/reborrow.rs:+2:9: +2:10
+          scope 2 {
+              debug b => _3;               // in scope 2 at $DIR/reborrow.rs:+2:9: +2:10
+              let _5: *mut u8;             // in scope 2 at $DIR/reborrow.rs:+3:9: +3:10
+              scope 4 {
+-                 debug c => _5;           // in scope 4 at $DIR/reborrow.rs:+3:9: +3:10
++                 debug c => _2;           // in scope 4 at $DIR/reborrow.rs:+3:9: +3:10
+              }
+          }
+          scope 3 {
+          }
+      }
+  
+      bb0: {
+-         StorageLive(_2);                 // scope 0 at $DIR/reborrow.rs:+1:9: +1:10
+          _2 = &raw mut _1;                // scope 0 at $DIR/reborrow.rs:+1:13: +1:23
+          StorageLive(_3);                 // scope 1 at $DIR/reborrow.rs:+2:9: +2:10
+          StorageLive(_4);                 // scope 1 at $DIR/reborrow.rs:+2:22: +2:29
+          _4 = &mut (*_2);                 // scope 3 at $DIR/reborrow.rs:+2:22: +2:29
+          _3 = &mut (*_4);                 // scope 1 at $DIR/reborrow.rs:+2:22: +2:29
+          StorageDead(_4);                 // scope 1 at $DIR/reborrow.rs:+2:31: +2:32
+-         StorageLive(_5);                 // scope 2 at $DIR/reborrow.rs:+3:9: +3:10
+-         _5 = _2;                         // scope 2 at $DIR/reborrow.rs:+3:13: +3:14
+          StorageLive(_6);                 // scope 4 at $DIR/reborrow.rs:+4:5: +4:14
+-         StorageLive(_7);                 // scope 4 at $DIR/reborrow.rs:+4:12: +4:13
+-         _7 = _5;                         // scope 4 at $DIR/reborrow.rs:+4:12: +4:13
+-         _6 = opaque::<*mut u8>(move _7) -> bb1; // scope 4 at $DIR/reborrow.rs:+4:5: +4:14
++         _6 = opaque::<*mut u8>(_2) -> bb1; // scope 4 at $DIR/reborrow.rs:+4:5: +4:14
+                                           // mir::Constant
+                                           // + span: $DIR/reborrow.rs:38:5: 38:11
+                                           // + literal: Const { ty: fn(*mut u8) {opaque::<*mut u8>}, val: Value(<ZST>) }
+      }
+  
+      bb1: {
+-         StorageDead(_7);                 // scope 4 at $DIR/reborrow.rs:+4:13: +4:14
+          StorageDead(_6);                 // scope 4 at $DIR/reborrow.rs:+4:14: +4:15
+          _0 = const ();                   // scope 0 at $DIR/reborrow.rs:+0:23: +5:2
+-         StorageDead(_5);                 // scope 2 at $DIR/reborrow.rs:+5:1: +5:2
+          StorageDead(_3);                 // scope 1 at $DIR/reborrow.rs:+5:1: +5:2
+-         StorageDead(_2);                 // scope 0 at $DIR/reborrow.rs:+5:1: +5:2
+          return;                          // scope 0 at $DIR/reborrow.rs:+5:2: +5:2
+      }
+  }
+  
diff --git a/tests/mir-opt/copy-prop/reborrow.miraw.CopyProp.diff b/tests/mir-opt/copy-prop/reborrow.miraw.CopyProp.diff
new file mode 100644
index 00000000000..2f1b522c2ec
--- /dev/null
+++ b/tests/mir-opt/copy-prop/reborrow.miraw.CopyProp.diff
@@ -0,0 +1,52 @@
+- // MIR for `miraw` before CopyProp
++ // MIR for `miraw` after CopyProp
+  
+  fn miraw(_1: u8) -> () {
+      debug x => _1;                       // in scope 0 at $DIR/reborrow.rs:+0:10: +0:15
+      let mut _0: ();                      // return place in scope 0 at $DIR/reborrow.rs:+0:21: +0:21
+      let _2: *mut u8;                     // in scope 0 at $DIR/reborrow.rs:+1:9: +1:10
+      let _5: ();                          // in scope 0 at $DIR/reborrow.rs:+4:5: +4:14
+      let mut _6: *mut u8;                 // in scope 0 at $DIR/reborrow.rs:+4:12: +4:13
+      scope 1 {
+          debug a => _2;                   // in scope 1 at $DIR/reborrow.rs:+1:9: +1:10
+          let _3: *mut u8;                 // in scope 1 at $DIR/reborrow.rs:+2:9: +2:10
+          scope 2 {
+              debug b => _3;               // in scope 2 at $DIR/reborrow.rs:+2:9: +2:10
+              let _4: *mut u8;             // in scope 2 at $DIR/reborrow.rs:+3:9: +3:10
+              scope 4 {
+-                 debug c => _4;           // in scope 4 at $DIR/reborrow.rs:+3:9: +3:10
++                 debug c => _2;           // in scope 4 at $DIR/reborrow.rs:+3:9: +3:10
+              }
+          }
+          scope 3 {
+          }
+      }
+  
+      bb0: {
+-         StorageLive(_2);                 // scope 0 at $DIR/reborrow.rs:+1:9: +1:10
+          _2 = &raw mut _1;                // scope 0 at $DIR/reborrow.rs:+1:13: +1:23
+          StorageLive(_3);                 // scope 1 at $DIR/reborrow.rs:+2:9: +2:10
+          _3 = &raw mut (*_2);             // scope 3 at $DIR/reborrow.rs:+2:22: +2:33
+-         StorageLive(_4);                 // scope 2 at $DIR/reborrow.rs:+3:9: +3:10
+-         _4 = _2;                         // scope 2 at $DIR/reborrow.rs:+3:13: +3:14
+          StorageLive(_5);                 // scope 4 at $DIR/reborrow.rs:+4:5: +4:14
+-         StorageLive(_6);                 // scope 4 at $DIR/reborrow.rs:+4:12: +4:13
+-         _6 = _4;                         // scope 4 at $DIR/reborrow.rs:+4:12: +4:13
+-         _5 = opaque::<*mut u8>(move _6) -> bb1; // scope 4 at $DIR/reborrow.rs:+4:5: +4:14
++         _5 = opaque::<*mut u8>(_2) -> bb1; // scope 4 at $DIR/reborrow.rs:+4:5: +4:14
+                                           // mir::Constant
+                                           // + span: $DIR/reborrow.rs:30:5: 30:11
+                                           // + literal: Const { ty: fn(*mut u8) {opaque::<*mut u8>}, val: Value(<ZST>) }
+      }
+  
+      bb1: {
+-         StorageDead(_6);                 // scope 4 at $DIR/reborrow.rs:+4:13: +4:14
+          StorageDead(_5);                 // scope 4 at $DIR/reborrow.rs:+4:14: +4:15
+          _0 = const ();                   // scope 0 at $DIR/reborrow.rs:+0:21: +5:2
+-         StorageDead(_4);                 // scope 2 at $DIR/reborrow.rs:+5:1: +5:2
+          StorageDead(_3);                 // scope 1 at $DIR/reborrow.rs:+5:1: +5:2
+-         StorageDead(_2);                 // scope 0 at $DIR/reborrow.rs:+5:1: +5:2
+          return;                          // scope 0 at $DIR/reborrow.rs:+5:2: +5:2
+      }
+  }
+  
diff --git a/tests/mir-opt/copy-prop/reborrow.remut.CopyProp.diff b/tests/mir-opt/copy-prop/reborrow.remut.CopyProp.diff
new file mode 100644
index 00000000000..9b580c1f4e1
--- /dev/null
+++ b/tests/mir-opt/copy-prop/reborrow.remut.CopyProp.diff
@@ -0,0 +1,50 @@
+- // MIR for `remut` before CopyProp
++ // MIR for `remut` after CopyProp
+  
+  fn remut(_1: u8) -> () {
+      debug x => _1;                       // in scope 0 at $DIR/reborrow.rs:+0:10: +0:15
+      let mut _0: ();                      // return place in scope 0 at $DIR/reborrow.rs:+0:21: +0:21
+      let _2: &mut u8;                     // in scope 0 at $DIR/reborrow.rs:+1:9: +1:10
+      let _5: ();                          // in scope 0 at $DIR/reborrow.rs:+4:5: +4:14
+      let mut _6: &mut u8;                 // in scope 0 at $DIR/reborrow.rs:+4:12: +4:13
+      scope 1 {
+          debug a => _2;                   // in scope 1 at $DIR/reborrow.rs:+1:9: +1:10
+          let _3: &mut u8;                 // in scope 1 at $DIR/reborrow.rs:+2:9: +2:10
+          scope 2 {
+              debug b => _3;               // in scope 2 at $DIR/reborrow.rs:+2:9: +2:10
+              let _4: &mut u8;             // in scope 2 at $DIR/reborrow.rs:+3:9: +3:10
+              scope 3 {
+-                 debug c => _4;           // in scope 3 at $DIR/reborrow.rs:+3:9: +3:10
++                 debug c => _2;           // in scope 3 at $DIR/reborrow.rs:+3:9: +3:10
+              }
+          }
+      }
+  
+      bb0: {
+-         StorageLive(_2);                 // scope 0 at $DIR/reborrow.rs:+1:9: +1:10
+          _2 = &mut _1;                    // scope 0 at $DIR/reborrow.rs:+1:13: +1:19
+          StorageLive(_3);                 // scope 1 at $DIR/reborrow.rs:+2:9: +2:10
+          _3 = &mut (*_2);                 // scope 1 at $DIR/reborrow.rs:+2:13: +2:20
+-         StorageLive(_4);                 // scope 2 at $DIR/reborrow.rs:+3:9: +3:10
+-         _4 = move _2;                    // scope 2 at $DIR/reborrow.rs:+3:13: +3:14
+          StorageLive(_5);                 // scope 3 at $DIR/reborrow.rs:+4:5: +4:14
+-         StorageLive(_6);                 // scope 3 at $DIR/reborrow.rs:+4:12: +4:13
+-         _6 = move _4;                    // scope 3 at $DIR/reborrow.rs:+4:12: +4:13
+-         _5 = opaque::<&mut u8>(move _6) -> bb1; // scope 3 at $DIR/reborrow.rs:+4:5: +4:14
++         _5 = opaque::<&mut u8>(move _2) -> bb1; // scope 3 at $DIR/reborrow.rs:+4:5: +4:14
+                                           // mir::Constant
+                                           // + span: $DIR/reborrow.rs:14:5: 14:11
+                                           // + literal: Const { ty: fn(&mut u8) {opaque::<&mut u8>}, val: Value(<ZST>) }
+      }
+  
+      bb1: {
+-         StorageDead(_6);                 // scope 3 at $DIR/reborrow.rs:+4:13: +4:14
+          StorageDead(_5);                 // scope 3 at $DIR/reborrow.rs:+4:14: +4:15
+          _0 = const ();                   // scope 0 at $DIR/reborrow.rs:+0:21: +5:2
+-         StorageDead(_4);                 // scope 2 at $DIR/reborrow.rs:+5:1: +5:2
+          StorageDead(_3);                 // scope 1 at $DIR/reborrow.rs:+5:1: +5:2
+-         StorageDead(_2);                 // scope 0 at $DIR/reborrow.rs:+5:1: +5:2
+          return;                          // scope 0 at $DIR/reborrow.rs:+5:2: +5:2
+      }
+  }
+  
diff --git a/tests/mir-opt/copy-prop/reborrow.reraw.CopyProp.diff b/tests/mir-opt/copy-prop/reborrow.reraw.CopyProp.diff
new file mode 100644
index 00000000000..cff4a176098
--- /dev/null
+++ b/tests/mir-opt/copy-prop/reborrow.reraw.CopyProp.diff
@@ -0,0 +1,50 @@
+- // MIR for `reraw` before CopyProp
++ // MIR for `reraw` after CopyProp
+  
+  fn reraw(_1: u8) -> () {
+      debug x => _1;                       // in scope 0 at $DIR/reborrow.rs:+0:10: +0:15
+      let mut _0: ();                      // return place in scope 0 at $DIR/reborrow.rs:+0:21: +0:21
+      let _2: &mut u8;                     // in scope 0 at $DIR/reborrow.rs:+1:9: +1:10
+      let _5: ();                          // in scope 0 at $DIR/reborrow.rs:+4:5: +4:14
+      let mut _6: &mut u8;                 // in scope 0 at $DIR/reborrow.rs:+4:12: +4:13
+      scope 1 {
+          debug a => _2;                   // in scope 1 at $DIR/reborrow.rs:+1:9: +1:10
+          let _3: *mut u8;                 // in scope 1 at $DIR/reborrow.rs:+2:9: +2:10
+          scope 2 {
+              debug b => _3;               // in scope 2 at $DIR/reborrow.rs:+2:9: +2:10
+              let _4: &mut u8;             // in scope 2 at $DIR/reborrow.rs:+3:9: +3:10
+              scope 3 {
+-                 debug c => _4;           // in scope 3 at $DIR/reborrow.rs:+3:9: +3:10
++                 debug c => _2;           // in scope 3 at $DIR/reborrow.rs:+3:9: +3:10
+              }
+          }
+      }
+  
+      bb0: {
+-         StorageLive(_2);                 // scope 0 at $DIR/reborrow.rs:+1:9: +1:10
+          _2 = &mut _1;                    // scope 0 at $DIR/reborrow.rs:+1:13: +1:19
+          StorageLive(_3);                 // scope 1 at $DIR/reborrow.rs:+2:9: +2:10
+          _3 = &raw mut (*_2);             // scope 1 at $DIR/reborrow.rs:+2:13: +2:24
+-         StorageLive(_4);                 // scope 2 at $DIR/reborrow.rs:+3:9: +3:10
+-         _4 = move _2;                    // scope 2 at $DIR/reborrow.rs:+3:13: +3:14
+          StorageLive(_5);                 // scope 3 at $DIR/reborrow.rs:+4:5: +4:14
+-         StorageLive(_6);                 // scope 3 at $DIR/reborrow.rs:+4:12: +4:13
+-         _6 = move _4;                    // scope 3 at $DIR/reborrow.rs:+4:12: +4:13
+-         _5 = opaque::<&mut u8>(move _6) -> bb1; // scope 3 at $DIR/reborrow.rs:+4:5: +4:14
++         _5 = opaque::<&mut u8>(move _2) -> bb1; // scope 3 at $DIR/reborrow.rs:+4:5: +4:14
+                                           // mir::Constant
+                                           // + span: $DIR/reborrow.rs:22:5: 22:11
+                                           // + literal: Const { ty: fn(&mut u8) {opaque::<&mut u8>}, val: Value(<ZST>) }
+      }
+  
+      bb1: {
+-         StorageDead(_6);                 // scope 3 at $DIR/reborrow.rs:+4:13: +4:14
+          StorageDead(_5);                 // scope 3 at $DIR/reborrow.rs:+4:14: +4:15
+          _0 = const ();                   // scope 0 at $DIR/reborrow.rs:+0:21: +5:2
+-         StorageDead(_4);                 // scope 2 at $DIR/reborrow.rs:+5:1: +5:2
+          StorageDead(_3);                 // scope 1 at $DIR/reborrow.rs:+5:1: +5:2
+-         StorageDead(_2);                 // scope 0 at $DIR/reborrow.rs:+5:1: +5:2
+          return;                          // scope 0 at $DIR/reborrow.rs:+5:2: +5:2
+      }
+  }
+  
diff --git a/tests/mir-opt/copy-prop/reborrow.rs b/tests/mir-opt/copy-prop/reborrow.rs
new file mode 100644
index 00000000000..c2926b8fa51
--- /dev/null
+++ b/tests/mir-opt/copy-prop/reborrow.rs
@@ -0,0 +1,46 @@
+// Check that CopyProp considers reborrows as not mutating the pointer.
+// unit-test: CopyProp
+
+#![feature(raw_ref_op)]
+
+#[inline(never)]
+fn opaque(_: impl Sized) {}
+
+// EMIT_MIR reborrow.remut.CopyProp.diff
+fn remut(mut x: u8) {
+    let a = &mut x;
+    let b = &mut *a; //< this cannot mutate a.
+    let c = a; //< so `c` and `a` can be merged.
+    opaque(c);
+}
+
+// EMIT_MIR reborrow.reraw.CopyProp.diff
+fn reraw(mut x: u8) {
+    let a = &mut x;
+    let b = &raw mut *a; //< this cannot mutate a.
+    let c = a; //< so `c` and `a` can be merged.
+    opaque(c);
+}
+
+// EMIT_MIR reborrow.miraw.CopyProp.diff
+fn miraw(mut x: u8) {
+    let a = &raw mut x;
+    let b = unsafe { &raw mut *a }; //< this cannot mutate a.
+    let c = a; //< so `c` and `a` can be merged.
+    opaque(c);
+}
+
+// EMIT_MIR reborrow.demiraw.CopyProp.diff
+fn demiraw(mut x: u8) {
+    let a = &raw mut x;
+    let b = unsafe { &mut *a }; //< this cannot mutate a.
+    let c = a; //< so `c` and `a` can be merged.
+    opaque(c);
+}
+
+fn main() {
+    remut(0);
+    reraw(0);
+    miraw(0);
+    demiraw(0);
+}
diff --git a/tests/mir-opt/issue_72181.main.built.after.mir b/tests/mir-opt/issue_72181.main.built.after.mir
index e8683692770..724e55e17fb 100644
--- a/tests/mir-opt/issue_72181.main.built.after.mir
+++ b/tests/mir-opt/issue_72181.main.built.after.mir
@@ -29,6 +29,7 @@ fn main() -> () {
     }
 
     bb1: {
+        PlaceMention(_1);                // scope 0 at $DIR/issue_72181.rs:+1:13: +1:34
         StorageDead(_1);                 // scope 0 at $DIR/issue_72181.rs:+1:34: +1:35
         StorageLive(_2);                 // scope 1 at $DIR/issue_72181.rs:+3:9: +3:10
         StorageLive(_3);                 // scope 1 at $DIR/issue_72181.rs:+3:14: +3:27
@@ -49,6 +50,7 @@ fn main() -> () {
 
     bb2: {
         _5 = (_2[_6].0: u64);            // scope 4 at $DIR/issue_72181.rs:+4:22: +4:28
+        PlaceMention(_5);                // scope 2 at $DIR/issue_72181.rs:+4:13: +4:30
         StorageDead(_6);                 // scope 2 at $DIR/issue_72181.rs:+4:30: +4:31
         StorageDead(_5);                 // scope 2 at $DIR/issue_72181.rs:+4:30: +4:31
         _0 = const ();                   // scope 0 at $DIR/issue_72181.rs:+0:11: +5:2
diff --git a/tests/mir-opt/issue_91633.bar.built.after.mir b/tests/mir-opt/issue_91633.bar.built.after.mir
index c3fb90e8402..760e5a8f90a 100644
--- a/tests/mir-opt/issue_91633.bar.built.after.mir
+++ b/tests/mir-opt/issue_91633.bar.built.after.mir
@@ -20,6 +20,7 @@ fn bar(_1: Box<[T]>) -> () {
 
     bb1: {
         StorageDead(_3);                 // scope 0 at $DIR/issue_91633.rs:+4:18: +4:19
+        PlaceMention((*_2));             // scope 0 at $DIR/issue_91633.rs:+4:14: +4:19
         StorageDead(_2);                 // scope 0 at $DIR/issue_91633.rs:+4:19: +4:20
         _0 = const ();                   // scope 0 at $DIR/issue_91633.rs:+3:2: +5:3
         drop(_1) -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/issue_91633.rs:+5:2: +5:3
diff --git a/tests/mir-opt/issue_91633.hey.built.after.mir b/tests/mir-opt/issue_91633.hey.built.after.mir
index ccb06dd5983..19f0c133e2e 100644
--- a/tests/mir-opt/issue_91633.hey.built.after.mir
+++ b/tests/mir-opt/issue_91633.hey.built.after.mir
@@ -23,6 +23,7 @@ fn hey(_1: &[T]) -> () {
     bb1: {
         StorageDead(_4);                 // scope 0 at $DIR/issue_91633.rs:+4:19: +4:20
         _2 = &(*_3);                     // scope 0 at $DIR/issue_91633.rs:+4:14: +4:20
+        PlaceMention(_2);                // scope 0 at $DIR/issue_91633.rs:+4:14: +4:20
         StorageDead(_2);                 // scope 0 at $DIR/issue_91633.rs:+4:20: +4:21
         _0 = const ();                   // scope 0 at $DIR/issue_91633.rs:+3:2: +5:3
         StorageDead(_3);                 // scope 0 at $DIR/issue_91633.rs:+5:2: +5:3
diff --git a/tests/run-make-fulldeps/rustdoc-themes/foo.rs b/tests/run-make-fulldeps/rustdoc-themes/foo.rs
index 58efaf7d5a0..995544aeff9 100644
--- a/tests/run-make-fulldeps/rustdoc-themes/foo.rs
+++ b/tests/run-make-fulldeps/rustdoc-themes/foo.rs
@@ -1,4 +1,4 @@
 // @has test.css
 // @has foo/struct.Foo.html
-// @has - '//link[@rel="stylesheet"]/@href' '../test.css'
+// @has - '//*[@id="rustdoc-vars"]/@data-themes' 'test'
 pub struct Foo;
diff --git a/tests/run-make/coverage-reports/expected_show_coverage.continue.txt b/tests/run-make/coverage-reports/expected_show_coverage.continue.txt
index 1c64ead9f26..bf42924b191 100644
--- a/tests/run-make/coverage-reports/expected_show_coverage.continue.txt
+++ b/tests/run-make/coverage-reports/expected_show_coverage.continue.txt
@@ -65,6 +65,6 @@
    65|       |        }
    66|      0|        x = 3;
    67|       |    }
-   68|       |    let _ = x;
+   68|      1|    let _ = x;
    69|      1|}
 
diff --git a/tests/rustdoc-gui/scrape-examples-button-focus.goml b/tests/rustdoc-gui/scrape-examples-button-focus.goml
index 1b5c3a0d202..16f0ced8c6e 100644
--- a/tests/rustdoc-gui/scrape-examples-button-focus.goml
+++ b/tests/rustdoc-gui/scrape-examples-button-focus.goml
@@ -8,24 +8,24 @@ focus: ".scraped-example-list > .scraped-example .next"
 press-key: "Enter"
 assert-property-false: (".scraped-example-list > .scraped-example pre", {
     "scrollTop": |initialScrollTop|
-})
+}, NEAR)
 focus: ".scraped-example-list > .scraped-example .prev"
 press-key: "Enter"
 assert-property: (".scraped-example-list > .scraped-example pre", {
     "scrollTop": |initialScrollTop|
-})
+}, NEAR)
 
 // The expand button increases the scrollHeight of the minimized code viewport
 store-property: (smallOffsetHeight, ".scraped-example-list > .scraped-example pre", "offsetHeight")
 assert-property-false: (".scraped-example-list > .scraped-example pre", {
     "scrollHeight": |smallOffsetHeight|
-})
+}, NEAR)
 focus: ".scraped-example-list > .scraped-example .expand"
 press-key: "Enter"
 assert-property-false: (".scraped-example-list > .scraped-example pre", {
     "offsetHeight": |smallOffsetHeight|
-})
+}, NEAR)
 store-property: (fullOffsetHeight, ".scraped-example-list > .scraped-example pre", "offsetHeight")
 assert-property: (".scraped-example-list > .scraped-example pre", {
     "scrollHeight": |fullOffsetHeight|
-})
+}, NEAR)
diff --git a/tests/rustdoc-gui/sidebar.goml b/tests/rustdoc-gui/sidebar.goml
index a6d51709019..473ab8fc960 100644
--- a/tests/rustdoc-gui/sidebar.goml
+++ b/tests/rustdoc-gui/sidebar.goml
@@ -149,3 +149,17 @@ assert-property: (".sidebar", {"clientWidth": "200"})
 click: "#toggle-all-docs"
 assert-text: ("#toggle-all-docs", "[−]")
 assert-property: (".sidebar", {"clientWidth": "200"})
+
+// Checks that all.html and index.html have their sidebar link in the same place.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+store-property: (index_sidebar_width, ".sidebar .location a", "clientWidth")
+store-property: (index_sidebar_height, ".sidebar .location a", "clientHeight")
+store-property: (index_sidebar_x, ".sidebar .location a", "offsetTop")
+store-property: (index_sidebar_y, ".sidebar .location a", "offsetLeft")
+goto: "file://" + |DOC_PATH| + "/test_docs/all.html"
+assert-property: (".sidebar .location a", {
+    "clientWidth": |index_sidebar_width|,
+    "clientHeight": |index_sidebar_height|,
+    "offsetTop": |index_sidebar_x|,
+    "offsetLeft": |index_sidebar_y|,
+})
diff --git a/tests/rustdoc-js-std/println-typo.js b/tests/rustdoc-js-std/println-typo.js
new file mode 100644
index 00000000000..7ca3ab8e563
--- /dev/null
+++ b/tests/rustdoc-js-std/println-typo.js
@@ -0,0 +1,12 @@
+// exact-check
+
+const QUERY = 'prinltn';
+const FILTER_CRATE = 'std';
+
+const EXPECTED = {
+    'others': [
+        { 'path': 'std', 'name': 'println' },
+        { 'path': 'std', 'name': 'print' },
+        { 'path': 'std', 'name': 'eprintln' },
+    ],
+};
diff --git a/tests/rustdoc-ui/crate-reference-in-block-module.rs b/tests/rustdoc-ui/crate-reference-in-block-module.rs
new file mode 100644
index 00000000000..aede030e072
--- /dev/null
+++ b/tests/rustdoc-ui/crate-reference-in-block-module.rs
@@ -0,0 +1,5 @@
+// check-pass
+fn main() {
+    /// [](crate)
+    struct X;
+}
diff --git a/tests/rustdoc-ui/crate-reference-in-block-module.stderr b/tests/rustdoc-ui/crate-reference-in-block-module.stderr
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/tests/rustdoc-ui/crate-reference-in-block-module.stderr
diff --git a/tests/rustdoc/anonymous-reexport.rs b/tests/rustdoc/anonymous-reexport.rs
index 6b884ff14df..839c1a30346 100644
--- a/tests/rustdoc/anonymous-reexport.rs
+++ b/tests/rustdoc/anonymous-reexport.rs
@@ -4,9 +4,13 @@
 
 // @has 'foo/index.html'
 // @has - '//*[@id="main-content"]' ''
-// We check that the only "h2" present is for "Bla".
-// @count - '//*[@id="main-content"]/h2' 1
+// We check that the only "h2" present are "Structs" (for "Bla") and "Re-exports".
+// @count - '//*[@id="main-content"]/h2' 2
 // @has - '//*[@id="main-content"]/h2' 'Structs'
+// @has - '//*[@id="main-content"]/h2' 'Re-exports'
+// The 3 re-exports.
+// @count - '//*[@id="main-content"]//*[@class="item-table"]//li//code' 3
+// The public struct.
 // @count - '//*[@id="main-content"]//a[@class="struct"]' 1
 
 mod ext {
diff --git a/tests/rustdoc/issue-108679-reexport-of-reexport.rs b/tests/rustdoc/issue-108679-reexport-of-reexport.rs
new file mode 100644
index 00000000000..5f977801cfd
--- /dev/null
+++ b/tests/rustdoc/issue-108679-reexport-of-reexport.rs
@@ -0,0 +1,29 @@
+// This test ensures that the `struct.B.html` only exists in `a`:
+// since `a::B` is public (and inlined too), `self::a::B` doesn't
+// need to be inlined as well.
+
+#![crate_name = "foo"]
+
+pub mod a {
+    // @has 'foo/a/index.html'
+    // Should only contain "Structs".
+    // @count - '//*[@id="main-content"]//*[@class="item-table"]' 1
+    // @has - '//*[@id="structs"]' 'Structs'
+    // @has - '//*[@id="main-content"]//a[@href="struct.A.html"]' 'A'
+    // @has - '//*[@id="main-content"]//a[@href="struct.B.html"]' 'B'
+    mod b {
+        pub struct B;
+    }
+    pub use self::b::B;
+    pub struct A;
+}
+
+// @has 'foo/index.html'
+// @!has - '//*[@id="structs"]' 'Structs'
+// @has - '//*[@id="reexports"]' 'Re-exports'
+// @has - '//*[@id="modules"]' 'Modules'
+// @has - '//*[@id="main-content"]//*[@id="reexport.A"]' 'pub use self::a::A;'
+// @has - '//*[@id="main-content"]//*[@id="reexport.B"]' 'pub use self::a::B;'
+// Should only contain "Modules" and "Re-exports".
+// @count - '//*[@id="main-content"]//*[@class="item-table"]' 2
+pub use self::a::{A, B};
diff --git a/tests/rustdoc/issue-108931-anonymous-reexport.rs b/tests/rustdoc/issue-108931-anonymous-reexport.rs
new file mode 100644
index 00000000000..302f7413398
--- /dev/null
+++ b/tests/rustdoc/issue-108931-anonymous-reexport.rs
@@ -0,0 +1,21 @@
+// Ensuring that anonymous re-exports are always inlined.
+
+#![crate_name = "foo"]
+
+pub mod foo {
+    pub struct Foo;
+}
+
+mod bar {
+    pub struct Bar;
+}
+
+// @has 'foo/index.html'
+// We check that the only "h2" present are "Re-exports" and "Modules".
+// @count - '//*[@id="main-content"]/h2' 2
+// @has - '//*[@id="main-content"]/h2' 'Re-exports'
+// @has - '//*[@id="main-content"]/h2' 'Modules'
+// @has - '//*[@id="main-content"]//*[@class="item-table"]//li//code' 'pub use foo::Foo as _;'
+// @has - '//*[@id="main-content"]//*[@class="item-table"]//li//code' 'pub use bar::Bar as _;'
+pub use foo::Foo as _;
+pub use bar::Bar as _;
diff --git a/tests/rustdoc/normalize-assoc-item.rs b/tests/rustdoc/normalize-assoc-item.rs
index af7b2f955fd..c6fd5e1101e 100644
--- a/tests/rustdoc/normalize-assoc-item.rs
+++ b/tests/rustdoc/normalize-assoc-item.rs
@@ -63,12 +63,12 @@ impl<'a> Lifetimes<'a> for usize {
     type Y = &'a isize;
 }
 
-// @has 'normalize_assoc_item/fn.g.html' '//pre[@class="rust item-decl"]' "pub fn g() -> &isize"
+// @has 'normalize_assoc_item/fn.g.html' '//pre[@class="rust item-decl"]' "pub fn g() -> &'static isize"
 pub fn g() -> <usize as Lifetimes<'static>>::Y {
     &0
 }
 
-// @has 'normalize_assoc_item/constant.A.html' '//pre[@class="rust item-decl"]' "pub const A: &isize"
+// @has 'normalize_assoc_item/constant.A.html' '//pre[@class="rust item-decl"]' "pub const A: &'static isize"
 pub const A: <usize as Lifetimes<'static>>::Y = &0;
 
 // test cross-crate re-exports
diff --git a/tests/ui-fulldeps/stable-mir/crate-info.rs b/tests/ui-fulldeps/stable-mir/crate-info.rs
new file mode 100644
index 00000000000..4458ab0162e
--- /dev/null
+++ b/tests/ui-fulldeps/stable-mir/crate-info.rs
@@ -0,0 +1,104 @@
+// run-pass
+// Test that users are able to use stable mir APIs to retrieve information of the current crate
+
+// ignore-stage-1
+// ignore-cross-compile
+// ignore-remote
+
+#![feature(rustc_private)]
+
+extern crate rustc_driver;
+extern crate rustc_hir;
+extern crate rustc_interface;
+extern crate rustc_middle;
+extern crate rustc_smir;
+
+use rustc_driver::{Callbacks, Compilation, RunCompiler};
+use rustc_hir::def::DefKind;
+use rustc_interface::{interface, Queries};
+use rustc_middle::ty::TyCtxt;
+use rustc_smir::{rustc_internal, stable_mir};
+use std::io::Write;
+
+const CRATE_NAME: &str = "input";
+
+/// This function uses the Stable MIR APIs to get information about the test crate.
+fn test_stable_mir(tcx: TyCtxt<'_>) {
+    // Get the local crate using stable_mir API.
+    let local = stable_mir::local_crate();
+    assert_eq!(&local.name, CRATE_NAME);
+
+    // Find items in the local crate.
+    let items = stable_mir::all_local_items();
+    assert!(has_item(tcx, &items, (DefKind::Fn, "foo_bar")));
+    assert!(has_item(tcx, &items, (DefKind::Fn, "foo::bar")));
+
+    // Find the `std` crate.
+    assert!(stable_mir::find_crate("std").is_some());
+}
+
+// Use internal API to find a function in a crate.
+fn has_item(tcx: TyCtxt, items: &stable_mir::CrateItems, item: (DefKind, &str)) -> bool {
+    items.iter().any(|crate_item| {
+        let def_id = rustc_internal::item_def_id(crate_item);
+        tcx.def_kind(def_id) == item.0 && tcx.def_path_str(def_id) == item.1
+    })
+}
+
+/// This test will generate and analyze a dummy crate using the stable mir.
+/// For that, it will first write the dummy crate into a file.
+/// It will invoke the compiler using a custom Callback implementation, which will
+/// invoke Stable MIR APIs after the compiler has finished its analysis.
+fn main() {
+    let path = "input.rs";
+    generate_input(&path).unwrap();
+    let args = vec![
+        "rustc".to_string(),
+        "--crate-type=lib".to_string(),
+        "--crate-name".to_string(),
+        CRATE_NAME.to_string(),
+        path.to_string(),
+    ];
+    rustc_driver::catch_fatal_errors(|| {
+        RunCompiler::new(&args, &mut SMirCalls {}).run().unwrap();
+    })
+    .unwrap();
+}
+
+struct SMirCalls {}
+
+impl Callbacks for SMirCalls {
+    /// Called after analysis. Return value instructs the compiler whether to
+    /// continue the compilation afterwards (defaults to `Compilation::Continue`)
+    fn after_analysis<'tcx>(
+        &mut self,
+        _compiler: &interface::Compiler,
+        queries: &'tcx Queries<'tcx>,
+    ) -> Compilation {
+        queries.global_ctxt().unwrap().enter(|tcx| {
+            test_stable_mir(tcx);
+        });
+        // No need to keep going.
+        Compilation::Stop
+    }
+}
+
+fn generate_input(path: &str) -> std::io::Result<()> {
+    let mut file = std::fs::File::create(path)?;
+    write!(
+        file,
+        r#"
+    mod foo {{
+        pub fn bar(i: i32) -> i64 {{
+            i as i64
+        }}
+    }}
+
+    pub fn foo_bar(x: i32, y: i32) -> i64 {{
+        let x_64 = foo::bar(x);
+        let y_64 = foo::bar(y);
+        x_64.wrapping_add(y_64)
+    }}"#
+    )?;
+    Ok(())
+}
diff --git a/tests/ui/asm/aarch64/parse-error.rs b/tests/ui/asm/aarch64/parse-error.rs
index cbc93cd3f75..9b8170840bb 100644
--- a/tests/ui/asm/aarch64/parse-error.rs
+++ b/tests/ui/asm/aarch64/parse-error.rs
@@ -37,8 +37,7 @@ fn main() {
         asm!("", options(nomem, foo));
         //~^ ERROR expected one of
         asm!("{}", options(), const foo);
-        //~^ ERROR arguments are not allowed after options
-        //~^^ ERROR attempt to use a non-constant value in a constant
+        //~^ ERROR attempt to use a non-constant value in a constant
         asm!("", clobber_abi(foo));
         //~^ ERROR expected string literal
         asm!("", clobber_abi("C" foo));
@@ -46,12 +45,10 @@ fn main() {
         asm!("", clobber_abi("C", foo));
         //~^ ERROR expected string literal
         asm!("{}", clobber_abi("C"), const foo);
-        //~^ ERROR arguments are not allowed after clobber_abi
-        //~^^ ERROR attempt to use a non-constant value in a constant
+        //~^ ERROR attempt to use a non-constant value in a constant
         asm!("", options(), clobber_abi("C"));
-        //~^ ERROR clobber_abi is not allowed after options
         asm!("{}", options(), clobber_abi("C"), const foo);
-        //~^ ERROR clobber_abi is not allowed after options
+        //~^ ERROR attempt to use a non-constant value in a constant
         asm!("{a}", a = const foo, a = const bar);
         //~^ ERROR duplicate argument named `a`
         //~^^ ERROR argument never used
@@ -60,11 +57,9 @@ fn main() {
         asm!("", a = in("x0") foo);
         //~^ ERROR explicit register arguments cannot have names
         asm!("{a}", in("x0") foo, a = const bar);
-        //~^ ERROR named arguments cannot follow explicit register arguments
-        //~^^ ERROR attempt to use a non-constant value in a constant
+        //~^ ERROR attempt to use a non-constant value in a constant
         asm!("{a}", in("x0") foo, a = const bar);
-        //~^ ERROR named arguments cannot follow explicit register arguments
-        //~^^ ERROR attempt to use a non-constant value in a constant
+        //~^ ERROR attempt to use a non-constant value in a constant
         asm!("{1}", in("x0") foo, const bar);
         //~^ ERROR positional arguments cannot follow named arguments or explicit register arguments
         //~^^ ERROR attempt to use a non-constant value in a constant
@@ -106,7 +101,6 @@ global_asm!("", options(nomem FOO));
 global_asm!("", options(nomem, FOO));
 //~^ ERROR expected one of
 global_asm!("{}", options(), const FOO);
-//~^ ERROR arguments are not allowed after options
 global_asm!("", clobber_abi(FOO));
 //~^ ERROR expected string literal
 global_asm!("", clobber_abi("C" FOO));
@@ -114,12 +108,11 @@ global_asm!("", clobber_abi("C" FOO));
 global_asm!("", clobber_abi("C", FOO));
 //~^ ERROR expected string literal
 global_asm!("{}", clobber_abi("C"), const FOO);
-//~^ ERROR arguments are not allowed after clobber_abi
-//~^^ ERROR `clobber_abi` cannot be used with `global_asm!`
+//~^ ERROR `clobber_abi` cannot be used with `global_asm!`
 global_asm!("", options(), clobber_abi("C"));
-//~^ ERROR clobber_abi is not allowed after options
+//~^ ERROR `clobber_abi` cannot be used with `global_asm!`
 global_asm!("{}", options(), clobber_abi("C"), const FOO);
-//~^ ERROR clobber_abi is not allowed after options
+//~^ ERROR `clobber_abi` cannot be used with `global_asm!`
 global_asm!("{a}", a = const FOO, a = const BAR);
 //~^ ERROR duplicate argument named `a`
 //~^^ ERROR argument never used
diff --git a/tests/ui/asm/aarch64/parse-error.stderr b/tests/ui/asm/aarch64/parse-error.stderr
index 804966b06ba..46984a1fe1c 100644
--- a/tests/ui/asm/aarch64/parse-error.stderr
+++ b/tests/ui/asm/aarch64/parse-error.stderr
@@ -82,58 +82,26 @@ error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `no
 LL |         asm!("", options(nomem, foo));
    |                                 ^^^ expected one of 10 possible tokens
 
-error: arguments are not allowed after options
-  --> $DIR/parse-error.rs:39:31
-   |
-LL |         asm!("{}", options(), const foo);
-   |                    ---------  ^^^^^^^^^ argument
-   |                    |
-   |                    previous options
-
 error: expected string literal
-  --> $DIR/parse-error.rs:42:30
+  --> $DIR/parse-error.rs:41:30
    |
 LL |         asm!("", clobber_abi(foo));
    |                              ^^^ not a string literal
 
 error: expected one of `)` or `,`, found `foo`
-  --> $DIR/parse-error.rs:44:34
+  --> $DIR/parse-error.rs:43:34
    |
 LL |         asm!("", clobber_abi("C" foo));
    |                                  ^^^ expected one of `)` or `,`
 
 error: expected string literal
-  --> $DIR/parse-error.rs:46:35
+  --> $DIR/parse-error.rs:45:35
    |
 LL |         asm!("", clobber_abi("C", foo));
    |                                   ^^^ not a string literal
 
-error: arguments are not allowed after clobber_abi
-  --> $DIR/parse-error.rs:48:38
-   |
-LL |         asm!("{}", clobber_abi("C"), const foo);
-   |                    ----------------  ^^^^^^^^^ argument
-   |                    |
-   |                    clobber_abi
-
-error: clobber_abi is not allowed after options
-  --> $DIR/parse-error.rs:51:29
-   |
-LL |         asm!("", options(), clobber_abi("C"));
-   |                  ---------  ^^^^^^^^^^^^^^^^
-   |                  |
-   |                  options
-
-error: clobber_abi is not allowed after options
-  --> $DIR/parse-error.rs:53:31
-   |
-LL |         asm!("{}", options(), clobber_abi("C"), const foo);
-   |                    ---------  ^^^^^^^^^^^^^^^^
-   |                    |
-   |                    options
-
 error: duplicate argument named `a`
-  --> $DIR/parse-error.rs:55:36
+  --> $DIR/parse-error.rs:52:36
    |
 LL |         asm!("{a}", a = const foo, a = const bar);
    |                     -------------  ^^^^^^^^^^^^^ duplicate argument
@@ -141,7 +109,7 @@ LL |         asm!("{a}", a = const foo, a = const bar);
    |                     previously here
 
 error: argument never used
-  --> $DIR/parse-error.rs:55:36
+  --> $DIR/parse-error.rs:52:36
    |
 LL |         asm!("{a}", a = const foo, a = const bar);
    |                                    ^^^^^^^^^^^^^ argument never used
@@ -149,29 +117,13 @@ LL |         asm!("{a}", a = const foo, a = const bar);
    = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
 
 error: explicit register arguments cannot have names
-  --> $DIR/parse-error.rs:60:18
+  --> $DIR/parse-error.rs:57:18
    |
 LL |         asm!("", a = in("x0") foo);
    |                  ^^^^^^^^^^^^^^^^
 
-error: named arguments cannot follow explicit register arguments
-  --> $DIR/parse-error.rs:62:35
-   |
-LL |         asm!("{a}", in("x0") foo, a = const bar);
-   |                     ------------  ^^^^^^^^^^^^^ named argument
-   |                     |
-   |                     explicit register argument
-
-error: named arguments cannot follow explicit register arguments
-  --> $DIR/parse-error.rs:65:35
-   |
-LL |         asm!("{a}", in("x0") foo, a = const bar);
-   |                     ------------  ^^^^^^^^^^^^^ named argument
-   |                     |
-   |                     explicit register argument
-
 error: positional arguments cannot follow named arguments or explicit register arguments
-  --> $DIR/parse-error.rs:68:35
+  --> $DIR/parse-error.rs:63:35
    |
 LL |         asm!("{1}", in("x0") foo, const bar);
    |                     ------------  ^^^^^^^^^ positional argument
@@ -179,19 +131,19 @@ LL |         asm!("{1}", in("x0") foo, const bar);
    |                     explicit register argument
 
 error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `""`
-  --> $DIR/parse-error.rs:71:29
+  --> $DIR/parse-error.rs:66:29
    |
 LL |         asm!("", options(), "");
    |                             ^^ expected one of 9 possible tokens
 
 error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `"{}"`
-  --> $DIR/parse-error.rs:73:33
+  --> $DIR/parse-error.rs:68:33
    |
 LL |         asm!("{}", in(reg) foo, "{}", out(reg) foo);
    |                                 ^^^^ expected one of 9 possible tokens
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:75:14
+  --> $DIR/parse-error.rs:70:14
    |
 LL |         asm!(format!("{{{}}}", 0), in(reg) foo);
    |              ^^^^^^^^^^^^^^^^^^^^
@@ -199,7 +151,7 @@ LL |         asm!(format!("{{{}}}", 0), in(reg) foo);
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:77:21
+  --> $DIR/parse-error.rs:72:21
    |
 LL |         asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar);
    |                     ^^^^^^^^^^^^^^^^^^^^
@@ -207,135 +159,115 @@ LL |         asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar);
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: _ cannot be used for input operands
-  --> $DIR/parse-error.rs:79:28
+  --> $DIR/parse-error.rs:74:28
    |
 LL |         asm!("{}", in(reg) _);
    |                            ^
 
 error: _ cannot be used for input operands
-  --> $DIR/parse-error.rs:81:31
+  --> $DIR/parse-error.rs:76:31
    |
 LL |         asm!("{}", inout(reg) _);
    |                               ^
 
 error: _ cannot be used for input operands
-  --> $DIR/parse-error.rs:83:35
+  --> $DIR/parse-error.rs:78:35
    |
 LL |         asm!("{}", inlateout(reg) _);
    |                                   ^
 
 error: requires at least a template string argument
-  --> $DIR/parse-error.rs:90:1
+  --> $DIR/parse-error.rs:85:1
    |
 LL | global_asm!();
    | ^^^^^^^^^^^^^
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:92:13
+  --> $DIR/parse-error.rs:87:13
    |
 LL | global_asm!(FOO);
    |             ^^^
 
 error: expected token: `,`
-  --> $DIR/parse-error.rs:94:18
+  --> $DIR/parse-error.rs:89:18
    |
 LL | global_asm!("{}" FOO);
    |                  ^^^ expected `,`
 
 error: expected operand, options, or additional template string
-  --> $DIR/parse-error.rs:96:19
+  --> $DIR/parse-error.rs:91:19
    |
 LL | global_asm!("{}", FOO);
    |                   ^^^ expected operand, options, or additional template string
 
 error: expected expression, found end of macro arguments
-  --> $DIR/parse-error.rs:98:24
+  --> $DIR/parse-error.rs:93:24
    |
 LL | global_asm!("{}", const);
    |                        ^ expected expression
 
 error: expected one of `,`, `.`, `?`, or an operator, found `FOO`
-  --> $DIR/parse-error.rs:100:30
+  --> $DIR/parse-error.rs:95:30
    |
 LL | global_asm!("{}", const(reg) FOO);
    |                              ^^^ expected one of `,`, `.`, `?`, or an operator
 
 error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
-  --> $DIR/parse-error.rs:102:25
+  --> $DIR/parse-error.rs:97:25
    |
 LL | global_asm!("", options(FOO));
    |                         ^^^ expected one of `)`, `att_syntax`, or `raw`
 
 error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
-  --> $DIR/parse-error.rs:104:25
+  --> $DIR/parse-error.rs:99:25
    |
 LL | global_asm!("", options(nomem FOO));
    |                         ^^^^^ expected one of `)`, `att_syntax`, or `raw`
 
 error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
-  --> $DIR/parse-error.rs:106:25
+  --> $DIR/parse-error.rs:101:25
    |
 LL | global_asm!("", options(nomem, FOO));
    |                         ^^^^^ expected one of `)`, `att_syntax`, or `raw`
 
-error: arguments are not allowed after options
-  --> $DIR/parse-error.rs:108:30
-   |
-LL | global_asm!("{}", options(), const FOO);
-   |                   ---------  ^^^^^^^^^ argument
-   |                   |
-   |                   previous options
-
 error: expected string literal
-  --> $DIR/parse-error.rs:110:29
+  --> $DIR/parse-error.rs:104:29
    |
 LL | global_asm!("", clobber_abi(FOO));
    |                             ^^^ not a string literal
 
 error: expected one of `)` or `,`, found `FOO`
-  --> $DIR/parse-error.rs:112:33
+  --> $DIR/parse-error.rs:106:33
    |
 LL | global_asm!("", clobber_abi("C" FOO));
    |                                 ^^^ expected one of `)` or `,`
 
 error: expected string literal
-  --> $DIR/parse-error.rs:114:34
+  --> $DIR/parse-error.rs:108:34
    |
 LL | global_asm!("", clobber_abi("C", FOO));
    |                                  ^^^ not a string literal
 
-error: arguments are not allowed after clobber_abi
-  --> $DIR/parse-error.rs:116:37
-   |
-LL | global_asm!("{}", clobber_abi("C"), const FOO);
-   |                   ----------------  ^^^^^^^^^ argument
-   |                   |
-   |                   clobber_abi
-
 error: `clobber_abi` cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:116:19
+  --> $DIR/parse-error.rs:110:19
    |
 LL | global_asm!("{}", clobber_abi("C"), const FOO);
    |                   ^^^^^^^^^^^^^^^^
 
-error: clobber_abi is not allowed after options
-  --> $DIR/parse-error.rs:119:28
+error: `clobber_abi` cannot be used with `global_asm!`
+  --> $DIR/parse-error.rs:112:28
    |
 LL | global_asm!("", options(), clobber_abi("C"));
-   |                 ---------  ^^^^^^^^^^^^^^^^
-   |                 |
-   |                 options
+   |                            ^^^^^^^^^^^^^^^^
 
-error: clobber_abi is not allowed after options
-  --> $DIR/parse-error.rs:121:30
+error: `clobber_abi` cannot be used with `global_asm!`
+  --> $DIR/parse-error.rs:114:30
    |
 LL | global_asm!("{}", options(), clobber_abi("C"), const FOO);
-   |                   ---------  ^^^^^^^^^^^^^^^^
-   |                   |
-   |                   options
+   |                              ^^^^^^^^^^^^^^^^
 
 error: duplicate argument named `a`
-  --> $DIR/parse-error.rs:123:35
+  --> $DIR/parse-error.rs:116:35
    |
 LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                    -------------  ^^^^^^^^^^^^^ duplicate argument
@@ -343,7 +275,7 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                    previously here
 
 error: argument never used
-  --> $DIR/parse-error.rs:123:35
+  --> $DIR/parse-error.rs:116:35
    |
 LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                                   ^^^^^^^^^^^^^ argument never used
@@ -351,19 +283,19 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR);
    = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
 
 error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""`
-  --> $DIR/parse-error.rs:126:28
+  --> $DIR/parse-error.rs:119:28
    |
 LL | global_asm!("", options(), "");
    |                            ^^ expected one of `clobber_abi`, `const`, `options`, or `sym`
 
 error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"`
-  --> $DIR/parse-error.rs:128:30
+  --> $DIR/parse-error.rs:121:30
    |
 LL | global_asm!("{}", const FOO, "{}", const FOO);
    |                              ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym`
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:130:13
+  --> $DIR/parse-error.rs:123:13
    |
 LL | global_asm!(format!("{{{}}}", 0), const FOO);
    |             ^^^^^^^^^^^^^^^^^^^^
@@ -371,7 +303,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO);
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:132:20
+  --> $DIR/parse-error.rs:125:20
    |
 LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
    |                    ^^^^^^^^^^^^^^^^^^^^
@@ -388,7 +320,7 @@ LL |         asm!("{}", options(), const foo);
    |                                     ^^^ non-constant value
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:48:44
+  --> $DIR/parse-error.rs:47:44
    |
 LL |     let mut foo = 0;
    |     ----------- help: consider using `const` instead of `let`: `const foo`
@@ -397,7 +329,16 @@ LL |         asm!("{}", clobber_abi("C"), const foo);
    |                                            ^^^ non-constant value
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:55:31
+  --> $DIR/parse-error.rs:50:55
+   |
+LL |     let mut foo = 0;
+   |     ----------- help: consider using `const` instead of `let`: `const foo`
+...
+LL |         asm!("{}", options(), clobber_abi("C"), const foo);
+   |                                                       ^^^ non-constant value
+
+error[E0435]: attempt to use a non-constant value in a constant
+  --> $DIR/parse-error.rs:52:31
    |
 LL |     let mut foo = 0;
    |     ----------- help: consider using `const` instead of `let`: `const foo`
@@ -406,7 +347,7 @@ LL |         asm!("{a}", a = const foo, a = const bar);
    |                               ^^^ non-constant value
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:55:46
+  --> $DIR/parse-error.rs:52:46
    |
 LL |     let mut bar = 0;
    |     ----------- help: consider using `const` instead of `let`: `const bar`
@@ -415,7 +356,7 @@ LL |         asm!("{a}", a = const foo, a = const bar);
    |                                              ^^^ non-constant value
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:62:45
+  --> $DIR/parse-error.rs:59:45
    |
 LL |     let mut bar = 0;
    |     ----------- help: consider using `const` instead of `let`: `const bar`
@@ -424,7 +365,7 @@ LL |         asm!("{a}", in("x0") foo, a = const bar);
    |                                             ^^^ non-constant value
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:65:45
+  --> $DIR/parse-error.rs:61:45
    |
 LL |     let mut bar = 0;
    |     ----------- help: consider using `const` instead of `let`: `const bar`
@@ -433,7 +374,7 @@ LL |         asm!("{a}", in("x0") foo, a = const bar);
    |                                             ^^^ non-constant value
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:68:41
+  --> $DIR/parse-error.rs:63:41
    |
 LL |     let mut bar = 0;
    |     ----------- help: consider using `const` instead of `let`: `const bar`
@@ -441,6 +382,6 @@ LL |     let mut bar = 0;
 LL |         asm!("{1}", in("x0") foo, const bar);
    |                                         ^^^ non-constant value
 
-error: aborting due to 64 previous errors
+error: aborting due to 57 previous errors
 
 For more information about this error, try `rustc --explain E0435`.
diff --git a/tests/ui/asm/bad-template.aarch64_mirunsafeck.stderr b/tests/ui/asm/bad-template.aarch64_mirunsafeck.stderr
index bb6a222b22e..b16f9a06c2a 100644
--- a/tests/ui/asm/bad-template.aarch64_mirunsafeck.stderr
+++ b/tests/ui/asm/bad-template.aarch64_mirunsafeck.stderr
@@ -81,6 +81,11 @@ note: explicit register arguments cannot be used in the asm template
    |
 LL |         asm!("{}", in("x0") foo);
    |                    ^^^^^^^^^^^^
+help: use the register name directly in the assembly code
+  --> $DIR/bad-template.rs:48:20
+   |
+LL |         asm!("{}", in("x0") foo);
+   |                    ^^^^^^^^^^^^
 
 error: asm template modifier must be a single character
   --> $DIR/bad-template.rs:50:17
diff --git a/tests/ui/asm/bad-template.aarch64_thirunsafeck.stderr b/tests/ui/asm/bad-template.aarch64_thirunsafeck.stderr
index bb6a222b22e..b16f9a06c2a 100644
--- a/tests/ui/asm/bad-template.aarch64_thirunsafeck.stderr
+++ b/tests/ui/asm/bad-template.aarch64_thirunsafeck.stderr
@@ -81,6 +81,11 @@ note: explicit register arguments cannot be used in the asm template
    |
 LL |         asm!("{}", in("x0") foo);
    |                    ^^^^^^^^^^^^
+help: use the register name directly in the assembly code
+  --> $DIR/bad-template.rs:48:20
+   |
+LL |         asm!("{}", in("x0") foo);
+   |                    ^^^^^^^^^^^^
 
 error: asm template modifier must be a single character
   --> $DIR/bad-template.rs:50:17
diff --git a/tests/ui/asm/bad-template.x86_64_mirunsafeck.stderr b/tests/ui/asm/bad-template.x86_64_mirunsafeck.stderr
index 903b5e959f3..41ac37c33c2 100644
--- a/tests/ui/asm/bad-template.x86_64_mirunsafeck.stderr
+++ b/tests/ui/asm/bad-template.x86_64_mirunsafeck.stderr
@@ -81,6 +81,11 @@ note: explicit register arguments cannot be used in the asm template
    |
 LL |         asm!("{}", in("eax") foo);
    |                    ^^^^^^^^^^^^^
+help: use the register name directly in the assembly code
+  --> $DIR/bad-template.rs:45:20
+   |
+LL |         asm!("{}", in("eax") foo);
+   |                    ^^^^^^^^^^^^^
 
 error: asm template modifier must be a single character
   --> $DIR/bad-template.rs:50:17
diff --git a/tests/ui/asm/bad-template.x86_64_thirunsafeck.stderr b/tests/ui/asm/bad-template.x86_64_thirunsafeck.stderr
index 903b5e959f3..41ac37c33c2 100644
--- a/tests/ui/asm/bad-template.x86_64_thirunsafeck.stderr
+++ b/tests/ui/asm/bad-template.x86_64_thirunsafeck.stderr
@@ -81,6 +81,11 @@ note: explicit register arguments cannot be used in the asm template
    |
 LL |         asm!("{}", in("eax") foo);
    |                    ^^^^^^^^^^^^^
+help: use the register name directly in the assembly code
+  --> $DIR/bad-template.rs:45:20
+   |
+LL |         asm!("{}", in("eax") foo);
+   |                    ^^^^^^^^^^^^^
 
 error: asm template modifier must be a single character
   --> $DIR/bad-template.rs:50:17
diff --git a/tests/ui/asm/x86_64/issue-89875.rs b/tests/ui/asm/x86_64/issue-89875.rs
index 669fd7e7e46..39c64564022 100644
--- a/tests/ui/asm/x86_64/issue-89875.rs
+++ b/tests/ui/asm/x86_64/issue-89875.rs
@@ -7,7 +7,7 @@
 use std::arch::asm;
 
 #[target_feature(enable = "avx")]
-fn main() {
+fn foo() {
     unsafe {
         asm!(
             "/* {} */",
@@ -15,3 +15,5 @@ fn main() {
         );
     }
 }
+
+fn main() {}
diff --git a/tests/ui/asm/x86_64/parse-error.rs b/tests/ui/asm/x86_64/parse-error.rs
index 9aeb6b2853f..2e714d464ae 100644
--- a/tests/ui/asm/x86_64/parse-error.rs
+++ b/tests/ui/asm/x86_64/parse-error.rs
@@ -37,8 +37,7 @@ fn main() {
         asm!("", options(nomem, foo));
         //~^ ERROR expected one of
         asm!("{}", options(), const foo);
-        //~^ ERROR arguments are not allowed after options
-        //~^^ ERROR attempt to use a non-constant value in a constant
+        //~^ ERROR attempt to use a non-constant value in a constant
         asm!("", clobber_abi());
         //~^ ERROR at least one abi must be provided
         asm!("", clobber_abi(foo));
@@ -48,12 +47,10 @@ fn main() {
         asm!("", clobber_abi("C", foo));
         //~^ ERROR expected string literal
         asm!("{}", clobber_abi("C"), const foo);
-        //~^ ERROR arguments are not allowed after clobber_abi
-        //~^^ ERROR attempt to use a non-constant value in a constant
+        //~^ ERROR attempt to use a non-constant value in a constant
         asm!("", options(), clobber_abi("C"));
-        //~^ ERROR clobber_abi is not allowed after options
         asm!("{}", options(), clobber_abi("C"), const foo);
-        //~^ ERROR clobber_abi is not allowed after options
+        //~^ ERROR attempt to use a non-constant value in a constant
         asm!("{a}", a = const foo, a = const bar);
         //~^ ERROR duplicate argument named `a`
         //~^^ ERROR argument never used
@@ -62,11 +59,9 @@ fn main() {
         asm!("", a = in("eax") foo);
         //~^ ERROR explicit register arguments cannot have names
         asm!("{a}", in("eax") foo, a = const bar);
-        //~^ ERROR named arguments cannot follow explicit register arguments
-        //~^^ ERROR attempt to use a non-constant value in a constant
+        //~^ ERROR attempt to use a non-constant value in a constant
         asm!("{a}", in("eax") foo, a = const bar);
-        //~^ ERROR named arguments cannot follow explicit register arguments
-        //~^^ ERROR attempt to use a non-constant value in a constant
+        //~^ ERROR attempt to use a non-constant value in a constant
         asm!("{1}", in("eax") foo, const bar);
         //~^ ERROR positional arguments cannot follow named arguments or explicit register arguments
         //~^^ ERROR attempt to use a non-constant value in a constant
@@ -108,7 +103,6 @@ global_asm!("", options(nomem FOO));
 global_asm!("", options(nomem, FOO));
 //~^ ERROR expected one of
 global_asm!("{}", options(), const FOO);
-//~^ ERROR arguments are not allowed after options
 global_asm!("", clobber_abi(FOO));
 //~^ ERROR expected string literal
 global_asm!("", clobber_abi("C" FOO));
@@ -116,12 +110,11 @@ global_asm!("", clobber_abi("C" FOO));
 global_asm!("", clobber_abi("C", FOO));
 //~^ ERROR expected string literal
 global_asm!("{}", clobber_abi("C"), const FOO);
-//~^ ERROR arguments are not allowed after clobber_abi
-//~^^ ERROR `clobber_abi` cannot be used with `global_asm!`
+//~^ ERROR `clobber_abi` cannot be used with `global_asm!`
 global_asm!("", options(), clobber_abi("C"));
-//~^ ERROR clobber_abi is not allowed after options
+//~^ ERROR `clobber_abi` cannot be used with `global_asm!`
 global_asm!("{}", options(), clobber_abi("C"), const FOO);
-//~^ ERROR clobber_abi is not allowed after options
+//~^ ERROR `clobber_abi` cannot be used with `global_asm!`
 global_asm!("", clobber_abi("C"), clobber_abi("C"));
 //~^ ERROR `clobber_abi` cannot be used with `global_asm!`
 global_asm!("{a}", a = const FOO, a = const BAR);
diff --git a/tests/ui/asm/x86_64/parse-error.stderr b/tests/ui/asm/x86_64/parse-error.stderr
index 57702c37b7c..0c9d6f71529 100644
--- a/tests/ui/asm/x86_64/parse-error.stderr
+++ b/tests/ui/asm/x86_64/parse-error.stderr
@@ -82,64 +82,32 @@ error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `no
 LL |         asm!("", options(nomem, foo));
    |                                 ^^^ expected one of 10 possible tokens
 
-error: arguments are not allowed after options
-  --> $DIR/parse-error.rs:39:31
-   |
-LL |         asm!("{}", options(), const foo);
-   |                    ---------  ^^^^^^^^^ argument
-   |                    |
-   |                    previous options
-
 error: at least one abi must be provided as an argument to `clobber_abi`
-  --> $DIR/parse-error.rs:42:30
+  --> $DIR/parse-error.rs:41:30
    |
 LL |         asm!("", clobber_abi());
    |                              ^
 
 error: expected string literal
-  --> $DIR/parse-error.rs:44:30
+  --> $DIR/parse-error.rs:43:30
    |
 LL |         asm!("", clobber_abi(foo));
    |                              ^^^ not a string literal
 
 error: expected one of `)` or `,`, found `foo`
-  --> $DIR/parse-error.rs:46:34
+  --> $DIR/parse-error.rs:45:34
    |
 LL |         asm!("", clobber_abi("C" foo));
    |                                  ^^^ expected one of `)` or `,`
 
 error: expected string literal
-  --> $DIR/parse-error.rs:48:35
+  --> $DIR/parse-error.rs:47:35
    |
 LL |         asm!("", clobber_abi("C", foo));
    |                                   ^^^ not a string literal
 
-error: arguments are not allowed after clobber_abi
-  --> $DIR/parse-error.rs:50:38
-   |
-LL |         asm!("{}", clobber_abi("C"), const foo);
-   |                    ----------------  ^^^^^^^^^ argument
-   |                    |
-   |                    clobber_abi
-
-error: clobber_abi is not allowed after options
-  --> $DIR/parse-error.rs:53:29
-   |
-LL |         asm!("", options(), clobber_abi("C"));
-   |                  ---------  ^^^^^^^^^^^^^^^^
-   |                  |
-   |                  options
-
-error: clobber_abi is not allowed after options
-  --> $DIR/parse-error.rs:55:31
-   |
-LL |         asm!("{}", options(), clobber_abi("C"), const foo);
-   |                    ---------  ^^^^^^^^^^^^^^^^
-   |                    |
-   |                    options
-
 error: duplicate argument named `a`
-  --> $DIR/parse-error.rs:57:36
+  --> $DIR/parse-error.rs:54:36
    |
 LL |         asm!("{a}", a = const foo, a = const bar);
    |                     -------------  ^^^^^^^^^^^^^ duplicate argument
@@ -147,7 +115,7 @@ LL |         asm!("{a}", a = const foo, a = const bar);
    |                     previously here
 
 error: argument never used
-  --> $DIR/parse-error.rs:57:36
+  --> $DIR/parse-error.rs:54:36
    |
 LL |         asm!("{a}", a = const foo, a = const bar);
    |                                    ^^^^^^^^^^^^^ argument never used
@@ -155,29 +123,13 @@ LL |         asm!("{a}", a = const foo, a = const bar);
    = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
 
 error: explicit register arguments cannot have names
-  --> $DIR/parse-error.rs:62:18
+  --> $DIR/parse-error.rs:59:18
    |
 LL |         asm!("", a = in("eax") foo);
    |                  ^^^^^^^^^^^^^^^^^
 
-error: named arguments cannot follow explicit register arguments
-  --> $DIR/parse-error.rs:64:36
-   |
-LL |         asm!("{a}", in("eax") foo, a = const bar);
-   |                     -------------  ^^^^^^^^^^^^^ named argument
-   |                     |
-   |                     explicit register argument
-
-error: named arguments cannot follow explicit register arguments
-  --> $DIR/parse-error.rs:67:36
-   |
-LL |         asm!("{a}", in("eax") foo, a = const bar);
-   |                     -------------  ^^^^^^^^^^^^^ named argument
-   |                     |
-   |                     explicit register argument
-
 error: positional arguments cannot follow named arguments or explicit register arguments
-  --> $DIR/parse-error.rs:70:36
+  --> $DIR/parse-error.rs:65:36
    |
 LL |         asm!("{1}", in("eax") foo, const bar);
    |                     -------------  ^^^^^^^^^ positional argument
@@ -185,19 +137,19 @@ LL |         asm!("{1}", in("eax") foo, const bar);
    |                     explicit register argument
 
 error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `""`
-  --> $DIR/parse-error.rs:73:29
+  --> $DIR/parse-error.rs:68:29
    |
 LL |         asm!("", options(), "");
    |                             ^^ expected one of 9 possible tokens
 
 error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `"{}"`
-  --> $DIR/parse-error.rs:75:33
+  --> $DIR/parse-error.rs:70:33
    |
 LL |         asm!("{}", in(reg) foo, "{}", out(reg) foo);
    |                                 ^^^^ expected one of 9 possible tokens
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:77:14
+  --> $DIR/parse-error.rs:72:14
    |
 LL |         asm!(format!("{{{}}}", 0), in(reg) foo);
    |              ^^^^^^^^^^^^^^^^^^^^
@@ -205,7 +157,7 @@ LL |         asm!(format!("{{{}}}", 0), in(reg) foo);
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:79:21
+  --> $DIR/parse-error.rs:74:21
    |
 LL |         asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar);
    |                     ^^^^^^^^^^^^^^^^^^^^
@@ -213,141 +165,121 @@ LL |         asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar);
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: _ cannot be used for input operands
-  --> $DIR/parse-error.rs:81:28
+  --> $DIR/parse-error.rs:76:28
    |
 LL |         asm!("{}", in(reg) _);
    |                            ^
 
 error: _ cannot be used for input operands
-  --> $DIR/parse-error.rs:83:31
+  --> $DIR/parse-error.rs:78:31
    |
 LL |         asm!("{}", inout(reg) _);
    |                               ^
 
 error: _ cannot be used for input operands
-  --> $DIR/parse-error.rs:85:35
+  --> $DIR/parse-error.rs:80:35
    |
 LL |         asm!("{}", inlateout(reg) _);
    |                                   ^
 
 error: requires at least a template string argument
-  --> $DIR/parse-error.rs:92:1
+  --> $DIR/parse-error.rs:87:1
    |
 LL | global_asm!();
    | ^^^^^^^^^^^^^
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:94:13
+  --> $DIR/parse-error.rs:89:13
    |
 LL | global_asm!(FOO);
    |             ^^^
 
 error: expected token: `,`
-  --> $DIR/parse-error.rs:96:18
+  --> $DIR/parse-error.rs:91:18
    |
 LL | global_asm!("{}" FOO);
    |                  ^^^ expected `,`
 
 error: expected operand, options, or additional template string
-  --> $DIR/parse-error.rs:98:19
+  --> $DIR/parse-error.rs:93:19
    |
 LL | global_asm!("{}", FOO);
    |                   ^^^ expected operand, options, or additional template string
 
 error: expected expression, found end of macro arguments
-  --> $DIR/parse-error.rs:100:24
+  --> $DIR/parse-error.rs:95:24
    |
 LL | global_asm!("{}", const);
    |                        ^ expected expression
 
 error: expected one of `,`, `.`, `?`, or an operator, found `FOO`
-  --> $DIR/parse-error.rs:102:30
+  --> $DIR/parse-error.rs:97:30
    |
 LL | global_asm!("{}", const(reg) FOO);
    |                              ^^^ expected one of `,`, `.`, `?`, or an operator
 
 error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
-  --> $DIR/parse-error.rs:104:25
+  --> $DIR/parse-error.rs:99:25
    |
 LL | global_asm!("", options(FOO));
    |                         ^^^ expected one of `)`, `att_syntax`, or `raw`
 
 error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
-  --> $DIR/parse-error.rs:106:25
+  --> $DIR/parse-error.rs:101:25
    |
 LL | global_asm!("", options(nomem FOO));
    |                         ^^^^^ expected one of `)`, `att_syntax`, or `raw`
 
 error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
-  --> $DIR/parse-error.rs:108:25
+  --> $DIR/parse-error.rs:103:25
    |
 LL | global_asm!("", options(nomem, FOO));
    |                         ^^^^^ expected one of `)`, `att_syntax`, or `raw`
 
-error: arguments are not allowed after options
-  --> $DIR/parse-error.rs:110:30
-   |
-LL | global_asm!("{}", options(), const FOO);
-   |                   ---------  ^^^^^^^^^ argument
-   |                   |
-   |                   previous options
-
 error: expected string literal
-  --> $DIR/parse-error.rs:112:29
+  --> $DIR/parse-error.rs:106:29
    |
 LL | global_asm!("", clobber_abi(FOO));
    |                             ^^^ not a string literal
 
 error: expected one of `)` or `,`, found `FOO`
-  --> $DIR/parse-error.rs:114:33
+  --> $DIR/parse-error.rs:108:33
    |
 LL | global_asm!("", clobber_abi("C" FOO));
    |                                 ^^^ expected one of `)` or `,`
 
 error: expected string literal
-  --> $DIR/parse-error.rs:116:34
+  --> $DIR/parse-error.rs:110:34
    |
 LL | global_asm!("", clobber_abi("C", FOO));
    |                                  ^^^ not a string literal
 
-error: arguments are not allowed after clobber_abi
-  --> $DIR/parse-error.rs:118:37
-   |
-LL | global_asm!("{}", clobber_abi("C"), const FOO);
-   |                   ----------------  ^^^^^^^^^ argument
-   |                   |
-   |                   clobber_abi
-
 error: `clobber_abi` cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:118:19
+  --> $DIR/parse-error.rs:112:19
    |
 LL | global_asm!("{}", clobber_abi("C"), const FOO);
    |                   ^^^^^^^^^^^^^^^^
 
-error: clobber_abi is not allowed after options
-  --> $DIR/parse-error.rs:121:28
+error: `clobber_abi` cannot be used with `global_asm!`
+  --> $DIR/parse-error.rs:114:28
    |
 LL | global_asm!("", options(), clobber_abi("C"));
-   |                 ---------  ^^^^^^^^^^^^^^^^
-   |                 |
-   |                 options
+   |                            ^^^^^^^^^^^^^^^^
 
-error: clobber_abi is not allowed after options
-  --> $DIR/parse-error.rs:123:30
+error: `clobber_abi` cannot be used with `global_asm!`
+  --> $DIR/parse-error.rs:116:30
    |
 LL | global_asm!("{}", options(), clobber_abi("C"), const FOO);
-   |                   ---------  ^^^^^^^^^^^^^^^^
-   |                   |
-   |                   options
+   |                              ^^^^^^^^^^^^^^^^
 
 error: `clobber_abi` cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:125:17
+  --> $DIR/parse-error.rs:118:17
    |
 LL | global_asm!("", clobber_abi("C"), clobber_abi("C"));
    |                 ^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^
 
 error: duplicate argument named `a`
-  --> $DIR/parse-error.rs:127:35
+  --> $DIR/parse-error.rs:120:35
    |
 LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                    -------------  ^^^^^^^^^^^^^ duplicate argument
@@ -355,7 +287,7 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                    previously here
 
 error: argument never used
-  --> $DIR/parse-error.rs:127:35
+  --> $DIR/parse-error.rs:120:35
    |
 LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                                   ^^^^^^^^^^^^^ argument never used
@@ -363,19 +295,19 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR);
    = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
 
 error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""`
-  --> $DIR/parse-error.rs:130:28
+  --> $DIR/parse-error.rs:123:28
    |
 LL | global_asm!("", options(), "");
    |                            ^^ expected one of `clobber_abi`, `const`, `options`, or `sym`
 
 error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"`
-  --> $DIR/parse-error.rs:132:30
+  --> $DIR/parse-error.rs:125:30
    |
 LL | global_asm!("{}", const FOO, "{}", const FOO);
    |                              ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym`
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:134:13
+  --> $DIR/parse-error.rs:127:13
    |
 LL | global_asm!(format!("{{{}}}", 0), const FOO);
    |             ^^^^^^^^^^^^^^^^^^^^
@@ -383,7 +315,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO);
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:136:20
+  --> $DIR/parse-error.rs:129:20
    |
 LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
    |                    ^^^^^^^^^^^^^^^^^^^^
@@ -400,7 +332,7 @@ LL |         asm!("{}", options(), const foo);
    |                                     ^^^ non-constant value
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:50:44
+  --> $DIR/parse-error.rs:49:44
    |
 LL |     let mut foo = 0;
    |     ----------- help: consider using `const` instead of `let`: `const foo`
@@ -409,7 +341,16 @@ LL |         asm!("{}", clobber_abi("C"), const foo);
    |                                            ^^^ non-constant value
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:57:31
+  --> $DIR/parse-error.rs:52:55
+   |
+LL |     let mut foo = 0;
+   |     ----------- help: consider using `const` instead of `let`: `const foo`
+...
+LL |         asm!("{}", options(), clobber_abi("C"), const foo);
+   |                                                       ^^^ non-constant value
+
+error[E0435]: attempt to use a non-constant value in a constant
+  --> $DIR/parse-error.rs:54:31
    |
 LL |     let mut foo = 0;
    |     ----------- help: consider using `const` instead of `let`: `const foo`
@@ -418,7 +359,7 @@ LL |         asm!("{a}", a = const foo, a = const bar);
    |                               ^^^ non-constant value
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:57:46
+  --> $DIR/parse-error.rs:54:46
    |
 LL |     let mut bar = 0;
    |     ----------- help: consider using `const` instead of `let`: `const bar`
@@ -427,7 +368,7 @@ LL |         asm!("{a}", a = const foo, a = const bar);
    |                                              ^^^ non-constant value
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:64:46
+  --> $DIR/parse-error.rs:61:46
    |
 LL |     let mut bar = 0;
    |     ----------- help: consider using `const` instead of `let`: `const bar`
@@ -436,7 +377,7 @@ LL |         asm!("{a}", in("eax") foo, a = const bar);
    |                                              ^^^ non-constant value
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:67:46
+  --> $DIR/parse-error.rs:63:46
    |
 LL |     let mut bar = 0;
    |     ----------- help: consider using `const` instead of `let`: `const bar`
@@ -445,7 +386,7 @@ LL |         asm!("{a}", in("eax") foo, a = const bar);
    |                                              ^^^ non-constant value
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:70:42
+  --> $DIR/parse-error.rs:65:42
    |
 LL |     let mut bar = 0;
    |     ----------- help: consider using `const` instead of `let`: `const bar`
@@ -453,6 +394,6 @@ LL |     let mut bar = 0;
 LL |         asm!("{1}", in("eax") foo, const bar);
    |                                          ^^^ non-constant value
 
-error: aborting due to 66 previous errors
+error: aborting due to 59 previous errors
 
 For more information about this error, try `rustc --explain E0435`.
diff --git a/tests/ui/binding/issue-53114-safety-checks.rs b/tests/ui/binding/issue-53114-safety-checks.rs
index e234db516c7..f4be2b482a7 100644
--- a/tests/ui/binding/issue-53114-safety-checks.rs
+++ b/tests/ui/binding/issue-53114-safety-checks.rs
@@ -21,7 +21,7 @@ fn let_wild_gets_unsafe_field() {
     let u2 = U { a: I(1) };
     let p = P { a: &2, b: &3 };
     let _ = &p.b;  //~ ERROR    reference to packed field
-    let _ = u1.a;  // #53114: should eventually signal error as well
+    let _ = u1.a;  //~ ERROR  [E0133]
     let _ = &u2.a; //~ ERROR  [E0133]
 
     // variation on above with `_` in substructure
@@ -30,6 +30,20 @@ fn let_wild_gets_unsafe_field() {
     let (_,) = (&u2.a,); //~ ERROR   [E0133]
 }
 
+fn let_ascribe_gets_unsafe_field() {
+    let u1 = U { a: I(0) };
+    let u2 = U { a: I(1) };
+    let p = P { a: &2, b: &3 };
+    let _: _ = &p.b;  //~ ERROR    reference to packed field
+    let _: _ = u1.a;  //~ ERROR  [E0133]
+    let _: _ = &u2.a; //~ ERROR  [E0133]
+
+    // variation on above with `_` in substructure
+    let (_,): _ = (&p.b,);  //~ ERROR     reference to packed field
+    let (_,): _ = (u1.a,);  //~ ERROR   [E0133]
+    let (_,): _ = (&u2.a,); //~ ERROR   [E0133]
+}
+
 fn match_unsafe_field_to_wild() {
     let u1 = U { a: I(0) };
     let u2 = U { a: I(1) };
diff --git a/tests/ui/binding/issue-53114-safety-checks.stderr b/tests/ui/binding/issue-53114-safety-checks.stderr
index 5c9d7877247..41318d0a38a 100644
--- a/tests/ui/binding/issue-53114-safety-checks.stderr
+++ b/tests/ui/binding/issue-53114-safety-checks.stderr
@@ -17,7 +17,25 @@ LL |     let (_,) = (&p.b,);
    = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
 
 error[E0793]: reference to packed field is unaligned
-  --> $DIR/issue-53114-safety-checks.rs:37:11
+  --> $DIR/issue-53114-safety-checks.rs:37:16
+   |
+LL |     let _: _ = &p.b;
+   |                ^^^^
+   |
+   = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
+   = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
+
+error[E0793]: reference to packed field is unaligned
+  --> $DIR/issue-53114-safety-checks.rs:42:20
+   |
+LL |     let (_,): _ = (&p.b,);
+   |                    ^^^^
+   |
+   = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
+   = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
+
+error[E0793]: reference to packed field is unaligned
+  --> $DIR/issue-53114-safety-checks.rs:51:11
    |
 LL |     match &p.b  { _ => { } }
    |           ^^^^
@@ -26,7 +44,7 @@ LL |     match &p.b  { _ => { } }
    = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
 
 error[E0793]: reference to packed field is unaligned
-  --> $DIR/issue-53114-safety-checks.rs:42:12
+  --> $DIR/issue-53114-safety-checks.rs:56:12
    |
 LL |     match (&p.b,)  { (_,) => { } }
    |            ^^^^
@@ -35,6 +53,14 @@ LL |     match (&p.b,)  { (_,) => { } }
    = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
 
 error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/issue-53114-safety-checks.rs:24:13
+   |
+LL |     let _ = u1.a;
+   |             ^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
   --> $DIR/issue-53114-safety-checks.rs:25:13
    |
 LL |     let _ = &u2.a;
@@ -59,7 +85,39 @@ LL |     let (_,) = (&u2.a,);
    = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
 
 error[E0133]: access to union field is unsafe and requires unsafe function or block
-  --> $DIR/issue-53114-safety-checks.rs:38:11
+  --> $DIR/issue-53114-safety-checks.rs:38:16
+   |
+LL |     let _: _ = u1.a;
+   |                ^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/issue-53114-safety-checks.rs:39:16
+   |
+LL |     let _: _ = &u2.a;
+   |                ^^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/issue-53114-safety-checks.rs:43:20
+   |
+LL |     let (_,): _ = (u1.a,);
+   |                    ^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/issue-53114-safety-checks.rs:44:20
+   |
+LL |     let (_,): _ = (&u2.a,);
+   |                    ^^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/issue-53114-safety-checks.rs:52:11
    |
 LL |     match u1.a  { _ => { } }
    |           ^^^^ access to union field
@@ -67,7 +125,7 @@ LL |     match u1.a  { _ => { } }
    = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
 
 error[E0133]: access to union field is unsafe and requires unsafe function or block
-  --> $DIR/issue-53114-safety-checks.rs:39:11
+  --> $DIR/issue-53114-safety-checks.rs:53:11
    |
 LL |     match &u2.a { _ => { } }
    |           ^^^^^ access to union field
@@ -75,7 +133,7 @@ LL |     match &u2.a { _ => { } }
    = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
 
 error[E0133]: access to union field is unsafe and requires unsafe function or block
-  --> $DIR/issue-53114-safety-checks.rs:43:12
+  --> $DIR/issue-53114-safety-checks.rs:57:12
    |
 LL |     match (u1.a,)  { (_,) => { } }
    |            ^^^^ access to union field
@@ -83,14 +141,14 @@ LL |     match (u1.a,)  { (_,) => { } }
    = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
 
 error[E0133]: access to union field is unsafe and requires unsafe function or block
-  --> $DIR/issue-53114-safety-checks.rs:44:12
+  --> $DIR/issue-53114-safety-checks.rs:58:12
    |
 LL |     match (&u2.a,) { (_,) => { } }
    |            ^^^^^ access to union field
    |
    = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
 
-error: aborting due to 11 previous errors
+error: aborting due to 18 previous errors
 
 Some errors have detailed explanations: E0133, E0793.
 For more information about an error, try `rustc --explain E0133`.
diff --git a/tests/ui/borrowck/let_underscore_temporary.rs b/tests/ui/borrowck/let_underscore_temporary.rs
new file mode 100644
index 00000000000..37b5c5d9d7a
--- /dev/null
+++ b/tests/ui/borrowck/let_underscore_temporary.rs
@@ -0,0 +1,27 @@
+// check-pass
+
+fn let_underscore(string: &Option<&str>, mut num: Option<i32>) {
+    let _ = if let Some(s) = *string { s.len() } else { 0 };
+    let _ = if let Some(s) = &num { s } else { &0 };
+    let _ = if let Some(s) = &mut num {
+        *s += 1;
+        s
+    } else {
+        &mut 0
+    };
+    let _ = if let Some(ref s) = num { s } else { &0 };
+    let _ = if let Some(mut s) = num {
+        s += 1;
+        s
+    } else {
+        0
+    };
+    let _ = if let Some(ref mut s) = num {
+        *s += 1;
+        s
+    } else {
+        &mut 0
+    };
+}
+
+fn main() {}
diff --git a/tests/ui/check-cfg/my-awesome-platform.json b/tests/ui/check-cfg/my-awesome-platform.json
new file mode 100644
index 00000000000..5e9ab8f1a2d
--- /dev/null
+++ b/tests/ui/check-cfg/my-awesome-platform.json
@@ -0,0 +1,12 @@
+{
+    "llvm-target": "x86_64-unknown-none-gnu",
+    "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
+    "arch": "x86_64",
+    "target-endian": "little",
+    "target-pointer-width": "64",
+    "target-c-int-width": "32",
+    "os": "ericos",
+    "linker-flavor": "ld.lld",
+    "linker": "rust-lld",
+    "executables": true
+}
diff --git a/tests/ui/check-cfg/values-target-json.rs b/tests/ui/check-cfg/values-target-json.rs
new file mode 100644
index 00000000000..2ef5a44592b
--- /dev/null
+++ b/tests/ui/check-cfg/values-target-json.rs
@@ -0,0 +1,21 @@
+// This test checks that we don't lint values defined by a custom target (target json)
+//
+// check-pass
+// needs-llvm-components: x86
+// compile-flags: --crate-type=lib --check-cfg=values() --target={{src-base}}/check-cfg/my-awesome-platform.json -Z unstable-options
+
+#![feature(lang_items, no_core, auto_traits)]
+#![no_core]
+
+#[lang = "sized"]
+trait Sized {}
+
+#[cfg(target_os = "linuz")]
+//~^ WARNING unexpected `cfg` condition value
+fn target_os_linux_misspell() {}
+
+#[cfg(target_os = "linux")]
+fn target_os_linux() {}
+
+#[cfg(target_os = "ericos")]
+fn target_os_ericos() {}
diff --git a/tests/ui/check-cfg/values-target-json.stderr b/tests/ui/check-cfg/values-target-json.stderr
new file mode 100644
index 00000000000..b58d2970773
--- /dev/null
+++ b/tests/ui/check-cfg/values-target-json.stderr
@@ -0,0 +1,13 @@
+warning: unexpected `cfg` condition value
+  --> $DIR/values-target-json.rs:13:7
+   |
+LL | #[cfg(target_os = "linuz")]
+   |       ^^^^^^^^^^^^-------
+   |                   |
+   |                   help: did you mean: `"linux"`
+   |
+   = note: expected values for `target_os` are: aix, android, cuda, dragonfly, emscripten, ericos, espidf, freebsd, fuchsia, haiku, hermit, horizon, illumos, ios, l4re, linux, macos, netbsd, none, nto, openbsd, psp, redox, solaris, solid_asp3, tvos, uefi, unknown, vita, vxworks, wasi, watchos, windows, xous
+   = note: `#[warn(unexpected_cfgs)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/coherence/coherence-impls-copy.stderr b/tests/ui/coherence/coherence-impls-copy.stderr
index d40ffc48a29..21dbc606321 100644
--- a/tests/ui/coherence/coherence-impls-copy.stderr
+++ b/tests/ui/coherence/coherence-impls-copy.stderr
@@ -52,19 +52,19 @@ LL | impl Copy for [MyType] {}
    |
    = note: define and implement a trait or new type instead
 
-error[E0206]: the trait `Copy` may not be implemented for this type
+error[E0206]: the trait `Copy` cannot be implemented for this type
   --> $DIR/coherence-impls-copy.rs:21:15
    |
 LL | impl Copy for &'static mut MyType {}
    |               ^^^^^^^^^^^^^^^^^^^ type is not a structure or enumeration
 
-error[E0206]: the trait `Copy` may not be implemented for this type
+error[E0206]: the trait `Copy` cannot be implemented for this type
   --> $DIR/coherence-impls-copy.rs:25:15
    |
 LL | impl Copy for (MyType, MyType) {}
    |               ^^^^^^^^^^^^^^^^ type is not a structure or enumeration
 
-error[E0206]: the trait `Copy` may not be implemented for this type
+error[E0206]: the trait `Copy` cannot be implemented for this type
   --> $DIR/coherence-impls-copy.rs:30:15
    |
 LL | impl Copy for [MyType] {}
diff --git a/tests/ui/coherence/deep-bad-copy-reason.rs b/tests/ui/coherence/deep-bad-copy-reason.rs
index 80bbe387ac7..97fd3f719bf 100644
--- a/tests/ui/coherence/deep-bad-copy-reason.rs
+++ b/tests/ui/coherence/deep-bad-copy-reason.rs
@@ -31,7 +31,7 @@ impl<'tcx, T> Clone for List<'tcx, T> {
 }
 
 impl<'tcx, T> Copy for List<'tcx, T> {}
-//~^ ERROR the trait `Copy` may not be implemented for this type
+//~^ ERROR the trait `Copy` cannot be implemented for this type
 
 fn assert_is_copy<T: Copy>() {}
 
diff --git a/tests/ui/coherence/deep-bad-copy-reason.stderr b/tests/ui/coherence/deep-bad-copy-reason.stderr
index 168ee57263d..7b6dd4b380f 100644
--- a/tests/ui/coherence/deep-bad-copy-reason.stderr
+++ b/tests/ui/coherence/deep-bad-copy-reason.stderr
@@ -1,4 +1,4 @@
-error[E0204]: the trait `Copy` may not be implemented for this type
+error[E0204]: the trait `Copy` cannot be implemented for this type
   --> $DIR/deep-bad-copy-reason.rs:33:24
    |
 LL | pub struct List<'tcx, T>(Interned<'tcx, ListS<T>>);
diff --git a/tests/ui/coherence/illegal-copy-bad-projection.rs b/tests/ui/coherence/illegal-copy-bad-projection.rs
new file mode 100644
index 00000000000..797443a0abe
--- /dev/null
+++ b/tests/ui/coherence/illegal-copy-bad-projection.rs
@@ -0,0 +1,16 @@
+trait AsPtr {
+    type Ptr;
+}
+
+impl AsPtr for () {
+    type Ptr = *const void;
+    //~^ ERROR cannot find type `void` in this scope
+}
+
+#[derive(Copy, Clone)]
+struct Foo {
+    p: <() as AsPtr>::Ptr,
+    // Do not report a "`Copy` cannot be implemented" here.
+}
+
+fn main() {}
diff --git a/tests/ui/coherence/illegal-copy-bad-projection.stderr b/tests/ui/coherence/illegal-copy-bad-projection.stderr
new file mode 100644
index 00000000000..8fed9ba23b2
--- /dev/null
+++ b/tests/ui/coherence/illegal-copy-bad-projection.stderr
@@ -0,0 +1,9 @@
+error[E0412]: cannot find type `void` in this scope
+  --> $DIR/illegal-copy-bad-projection.rs:6:23
+   |
+LL |     type Ptr = *const void;
+   |                       ^^^^ not found in this scope
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0412`.
diff --git a/tests/ui/const-generics/bad-generic-in-copy-impl.rs b/tests/ui/const-generics/bad-generic-in-copy-impl.rs
new file mode 100644
index 00000000000..b5663464cf4
--- /dev/null
+++ b/tests/ui/const-generics/bad-generic-in-copy-impl.rs
@@ -0,0 +1,9 @@
+#[derive(Copy, Clone)]
+pub struct Foo {
+    x: [u8; SIZE],
+    //~^ ERROR mismatched types
+}
+
+const SIZE: u32 = 1;
+
+fn main() {}
diff --git a/tests/ui/const-generics/bad-generic-in-copy-impl.stderr b/tests/ui/const-generics/bad-generic-in-copy-impl.stderr
new file mode 100644
index 00000000000..25701ce68cc
--- /dev/null
+++ b/tests/ui/const-generics/bad-generic-in-copy-impl.stderr
@@ -0,0 +1,9 @@
+error[E0308]: mismatched types
+  --> $DIR/bad-generic-in-copy-impl.rs:3:13
+   |
+LL |     x: [u8; SIZE],
+   |             ^^^^ expected `usize`, found `u32`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/const-generics/type_mismatch.rs b/tests/ui/const-generics/type_mismatch.rs
index 4a7534e3713..daa13277be0 100644
--- a/tests/ui/const-generics/type_mismatch.rs
+++ b/tests/ui/const-generics/type_mismatch.rs
@@ -1,5 +1,6 @@
 fn foo<const N: usize>() -> [u8; N] {
     bar::<N>() //~ ERROR mismatched types
+    //~^ ERROR the constant `N` is not of type `u8`
 }
 
 fn bar<const N: u8>() -> [u8; N] {}
diff --git a/tests/ui/const-generics/type_mismatch.stderr b/tests/ui/const-generics/type_mismatch.stderr
index b28ae8f7e71..6d8955e411e 100644
--- a/tests/ui/const-generics/type_mismatch.stderr
+++ b/tests/ui/const-generics/type_mismatch.stderr
@@ -1,3 +1,15 @@
+error: the constant `N` is not of type `u8`
+  --> $DIR/type_mismatch.rs:2:5
+   |
+LL |     bar::<N>()
+   |     ^^^^^^^^
+   |
+note: required by a bound in `bar`
+  --> $DIR/type_mismatch.rs:6:8
+   |
+LL | fn bar<const N: u8>() -> [u8; N] {}
+   |        ^^^^^^^^^^^ required by this bound in `bar`
+
 error[E0308]: mismatched types
   --> $DIR/type_mismatch.rs:2:11
    |
@@ -5,7 +17,7 @@ LL |     bar::<N>()
    |           ^ expected `u8`, found `usize`
 
 error[E0308]: mismatched types
-  --> $DIR/type_mismatch.rs:5:26
+  --> $DIR/type_mismatch.rs:6:26
    |
 LL | fn bar<const N: u8>() -> [u8; N] {}
    |    ---                   ^^^^^^^ expected `[u8; N]`, found `()`
@@ -13,11 +25,11 @@ LL | fn bar<const N: u8>() -> [u8; N] {}
    |    implicitly returns `()` as its body has no tail or `return` expression
 
 error[E0308]: mismatched types
-  --> $DIR/type_mismatch.rs:5:31
+  --> $DIR/type_mismatch.rs:6:31
    |
 LL | fn bar<const N: u8>() -> [u8; N] {}
    |                               ^ expected `usize`, found `u8`
 
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/const-generics/type_not_in_scope.rs b/tests/ui/const-generics/type_not_in_scope.rs
index 5933701808b..917abaed15e 100644
--- a/tests/ui/const-generics/type_not_in_scope.rs
+++ b/tests/ui/const-generics/type_not_in_scope.rs
@@ -6,6 +6,5 @@ impl X {
 }
 fn getn<const N: cfg_attr>() -> [u8; N] {}
 //~^ ERROR expected type, found built-in attribute `cfg_attr`
-//~| ERROR mismatched types
 
 fn main() {}
diff --git a/tests/ui/const-generics/type_not_in_scope.stderr b/tests/ui/const-generics/type_not_in_scope.stderr
index 5f45550a627..5eb81ca0522 100644
--- a/tests/ui/const-generics/type_not_in_scope.stderr
+++ b/tests/ui/const-generics/type_not_in_scope.stderr
@@ -10,15 +10,7 @@ error[E0573]: expected type, found built-in attribute `cfg_attr`
 LL | fn getn<const N: cfg_attr>() -> [u8; N] {}
    |                  ^^^^^^^^ not a type
 
-error[E0308]: mismatched types
-  --> $DIR/type_not_in_scope.rs:7:33
-   |
-LL | fn getn<const N: cfg_attr>() -> [u8; N] {}
-   |    ----                         ^^^^^^^ expected `[u8; N]`, found `()`
-   |    |
-   |    implicitly returns `()` as its body has no tail or `return` expression
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
-Some errors have detailed explanations: E0308, E0412, E0573.
-For more information about an error, try `rustc --explain E0308`.
+Some errors have detailed explanations: E0412, E0573.
+For more information about an error, try `rustc --explain E0412`.
diff --git a/tests/ui/consts/min_const_fn/min_const_fn_unsafe_bad.rs b/tests/ui/consts/min_const_fn/min_const_fn_unsafe_bad.rs
index a6e1788bb7f..df20ff446cd 100644
--- a/tests/ui/consts/min_const_fn/min_const_fn_unsafe_bad.rs
+++ b/tests/ui/consts/min_const_fn/min_const_fn_unsafe_bad.rs
@@ -7,4 +7,7 @@ const unsafe fn bad_const_unsafe_deref_raw(x: *mut usize) -> usize { *x }
 const unsafe fn bad_const_unsafe_deref_raw_ref(x: *mut usize) -> &'static usize { &*x }
 //~^ dereferencing raw mutable pointers in constant functions
 
+const unsafe fn bad_const_unsafe_deref_raw_underscore(x: *mut usize) { let _ = *x; }
+//~^ dereferencing raw mutable pointers in constant functions
+
 fn main() {}
diff --git a/tests/ui/consts/min_const_fn/min_const_fn_unsafe_bad.stderr b/tests/ui/consts/min_const_fn/min_const_fn_unsafe_bad.stderr
index 820b6433f36..e68376e7b87 100644
--- a/tests/ui/consts/min_const_fn/min_const_fn_unsafe_bad.stderr
+++ b/tests/ui/consts/min_const_fn/min_const_fn_unsafe_bad.stderr
@@ -25,6 +25,15 @@ LL | const unsafe fn bad_const_unsafe_deref_raw_ref(x: *mut usize) -> &'static u
    = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
    = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
 
-error: aborting due to 3 previous errors
+error[E0658]: dereferencing raw mutable pointers in constant functions is unstable
+  --> $DIR/min_const_fn_unsafe_bad.rs:10:80
+   |
+LL | const unsafe fn bad_const_unsafe_deref_raw_underscore(x: *mut usize) { let _ = *x; }
+   |                                                                                ^^
+   |
+   = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
+   = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
+
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-1.rs b/tests/ui/dst/issue-90528-unsizing-suggestion-1.rs
new file mode 100644
index 00000000000..52863e22bb6
--- /dev/null
+++ b/tests/ui/dst/issue-90528-unsizing-suggestion-1.rs
@@ -0,0 +1,20 @@
+// Issue #90528: provide helpful suggestions when a trait bound is unsatisfied
+// due to a missed unsizing coercion.
+//
+// This test exercises array literals and a trait implemented on immutable slices.
+
+trait Read {}
+
+impl Read for &[u8] {}
+
+fn wants_read(_: impl Read) {}
+
+fn main() {
+    wants_read([0u8]);
+    //~^ ERROR the trait bound `[u8; 1]: Read` is not satisfied
+    wants_read(&[0u8]);
+    //~^ ERROR the trait bound `&[u8; 1]: Read` is not satisfied
+    wants_read(&[0u8][..]);
+    wants_read(&mut [0u8]);
+    //~^ ERROR the trait bound `&mut [u8; 1]: Read` is not satisfied
+}
diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-1.stderr b/tests/ui/dst/issue-90528-unsizing-suggestion-1.stderr
new file mode 100644
index 00000000000..27ef3fe97a5
--- /dev/null
+++ b/tests/ui/dst/issue-90528-unsizing-suggestion-1.stderr
@@ -0,0 +1,56 @@
+error[E0277]: the trait bound `[u8; 1]: Read` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-1.rs:13:16
+   |
+LL |     wants_read([0u8]);
+   |     ---------- ^^^^^ the trait `Read` is not implemented for `[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Read` is implemented for `&[u8]`
+note: required by a bound in `wants_read`
+  --> $DIR/issue-90528-unsizing-suggestion-1.rs:10:23
+   |
+LL | fn wants_read(_: impl Read) {}
+   |                       ^^^^ required by this bound in `wants_read`
+help: convert the array to a `&[u8]` slice instead
+   |
+LL |     wants_read(&[0u8][..]);
+   |                +     ++++
+
+error[E0277]: the trait bound `&[u8; 1]: Read` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-1.rs:15:16
+   |
+LL |     wants_read(&[0u8]);
+   |     ---------- ^^^^^^ the trait `Read` is not implemented for `&[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Read` is implemented for `&[u8]`
+note: required by a bound in `wants_read`
+  --> $DIR/issue-90528-unsizing-suggestion-1.rs:10:23
+   |
+LL | fn wants_read(_: impl Read) {}
+   |                       ^^^^ required by this bound in `wants_read`
+help: convert the array to a `&[u8]` slice instead
+   |
+LL |     wants_read(&[0u8][..]);
+   |                      ++++
+
+error[E0277]: the trait bound `&mut [u8; 1]: Read` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-1.rs:18:16
+   |
+LL |     wants_read(&mut [0u8]);
+   |     ---------- ^^^^^^^^^^ the trait `Read` is not implemented for `&mut [u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Read` is implemented for `&[u8]`
+note: required by a bound in `wants_read`
+  --> $DIR/issue-90528-unsizing-suggestion-1.rs:10:23
+   |
+LL | fn wants_read(_: impl Read) {}
+   |                       ^^^^ required by this bound in `wants_read`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-2.rs b/tests/ui/dst/issue-90528-unsizing-suggestion-2.rs
new file mode 100644
index 00000000000..f2762ad421b
--- /dev/null
+++ b/tests/ui/dst/issue-90528-unsizing-suggestion-2.rs
@@ -0,0 +1,28 @@
+// Issue #90528: provide helpful suggestions when a trait bound is unsatisfied
+// due to a missed unsizing coercion.
+//
+// This test exercises array variables and a trait implemented on immmutable slices.
+
+trait Read {}
+
+impl Read for &[u8] {}
+
+fn wants_read(_: impl Read) {}
+
+fn main() {
+    let x = [0u8];
+    wants_read(x);
+    //~^ ERROR the trait bound `[u8; 1]: Read` is not satisfied
+    wants_read(&x);
+    //~^ ERROR the trait bound `&[u8; 1]: Read` is not satisfied
+    wants_read(&x[..]);
+
+    let x = &[0u8];
+    wants_read(x);
+    //~^ ERROR the trait bound `&[u8; 1]: Read` is not satisfied
+    wants_read(&x);
+    //~^ ERROR the trait bound `&&[u8; 1]: Read` is not satisfied
+    wants_read(*x);
+    //~^ ERROR the trait bound `[u8; 1]: Read` is not satisfied
+    wants_read(&x[..]);
+}
diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-2.stderr b/tests/ui/dst/issue-90528-unsizing-suggestion-2.stderr
new file mode 100644
index 00000000000..ae0c4ca506a
--- /dev/null
+++ b/tests/ui/dst/issue-90528-unsizing-suggestion-2.stderr
@@ -0,0 +1,94 @@
+error[E0277]: the trait bound `[u8; 1]: Read` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-2.rs:14:16
+   |
+LL |     wants_read(x);
+   |     ---------- ^ the trait `Read` is not implemented for `[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Read` is implemented for `&[u8]`
+note: required by a bound in `wants_read`
+  --> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23
+   |
+LL | fn wants_read(_: impl Read) {}
+   |                       ^^^^ required by this bound in `wants_read`
+help: convert the array to a `&[u8]` slice instead
+   |
+LL |     wants_read(&x[..]);
+   |                + ++++
+
+error[E0277]: the trait bound `&[u8; 1]: Read` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-2.rs:16:16
+   |
+LL |     wants_read(&x);
+   |     ---------- ^^ the trait `Read` is not implemented for `&[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Read` is implemented for `&[u8]`
+note: required by a bound in `wants_read`
+  --> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23
+   |
+LL | fn wants_read(_: impl Read) {}
+   |                       ^^^^ required by this bound in `wants_read`
+help: convert the array to a `&[u8]` slice instead
+   |
+LL |     wants_read(&x[..]);
+   |                  ++++
+
+error[E0277]: the trait bound `&[u8; 1]: Read` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-2.rs:21:16
+   |
+LL |     wants_read(x);
+   |     ---------- ^ the trait `Read` is not implemented for `&[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Read` is implemented for `&[u8]`
+note: required by a bound in `wants_read`
+  --> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23
+   |
+LL | fn wants_read(_: impl Read) {}
+   |                       ^^^^ required by this bound in `wants_read`
+help: convert the array to a `&[u8]` slice instead
+   |
+LL |     wants_read(&x[..]);
+   |                + ++++
+
+error[E0277]: the trait bound `&&[u8; 1]: Read` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-2.rs:23:16
+   |
+LL |     wants_read(&x);
+   |     ---------- ^^ the trait `Read` is not implemented for `&&[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Read` is implemented for `&[u8]`
+note: required by a bound in `wants_read`
+  --> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23
+   |
+LL | fn wants_read(_: impl Read) {}
+   |                       ^^^^ required by this bound in `wants_read`
+
+error[E0277]: the trait bound `[u8; 1]: Read` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-2.rs:25:16
+   |
+LL |     wants_read(*x);
+   |     ---------- ^^ the trait `Read` is not implemented for `[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Read` is implemented for `&[u8]`
+note: required by a bound in `wants_read`
+  --> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23
+   |
+LL | fn wants_read(_: impl Read) {}
+   |                       ^^^^ required by this bound in `wants_read`
+help: convert the array to a `&[u8]` slice instead
+   |
+LL |     wants_read(&*x[..]);
+   |                +  ++++
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-3.rs b/tests/ui/dst/issue-90528-unsizing-suggestion-3.rs
new file mode 100644
index 00000000000..218843d0500
--- /dev/null
+++ b/tests/ui/dst/issue-90528-unsizing-suggestion-3.rs
@@ -0,0 +1,22 @@
+// Issue #90528: provide helpful suggestions when a trait bound is unsatisfied
+// due to a missed unsizing coercion.
+//
+// This test exercises array literals and a trait implemented on mutable slices.
+
+trait Write {}
+
+impl Write for &mut [u8] {}
+
+fn wants_write(_: impl Write) {}
+
+fn main() {
+    wants_write([0u8]);
+    //~^ ERROR the trait bound `[u8; 1]: Write` is not satisfied
+    wants_write(&mut [0u8]);
+    //~^ ERROR the trait bound `&mut [u8; 1]: Write` is not satisfied
+    wants_write(&mut [0u8][..]);
+    wants_write(&[0u8]);
+    //~^ ERROR the trait bound `&[u8; 1]: Write` is not satisfied
+    wants_write(&[0u8][..]);
+    //~^ ERROR the trait bound `&[u8]: Write` is not satisfied
+}
diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-3.stderr b/tests/ui/dst/issue-90528-unsizing-suggestion-3.stderr
new file mode 100644
index 00000000000..774d5ba3c89
--- /dev/null
+++ b/tests/ui/dst/issue-90528-unsizing-suggestion-3.stderr
@@ -0,0 +1,75 @@
+error[E0277]: the trait bound `[u8; 1]: Write` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-3.rs:13:17
+   |
+LL |     wants_write([0u8]);
+   |     ----------- ^^^^^ the trait `Write` is not implemented for `[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Write` is implemented for `&mut [u8]`
+note: required by a bound in `wants_write`
+  --> $DIR/issue-90528-unsizing-suggestion-3.rs:10:24
+   |
+LL | fn wants_write(_: impl Write) {}
+   |                        ^^^^^ required by this bound in `wants_write`
+help: convert the array to a `&mut [u8]` slice instead
+   |
+LL |     wants_write(&mut [0u8][..]);
+   |                 ++++      ++++
+
+error[E0277]: the trait bound `&mut [u8; 1]: Write` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-3.rs:15:17
+   |
+LL |     wants_write(&mut [0u8]);
+   |     ----------- ^^^^^^^^^^ the trait `Write` is not implemented for `&mut [u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Write` is implemented for `&mut [u8]`
+note: required by a bound in `wants_write`
+  --> $DIR/issue-90528-unsizing-suggestion-3.rs:10:24
+   |
+LL | fn wants_write(_: impl Write) {}
+   |                        ^^^^^ required by this bound in `wants_write`
+help: convert the array to a `&mut [u8]` slice instead
+   |
+LL |     wants_write(&mut [0u8][..]);
+   |                           ++++
+
+error[E0277]: the trait bound `&[u8; 1]: Write` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-3.rs:18:17
+   |
+LL |     wants_write(&[0u8]);
+   |     ----------- ^^^^^^ the trait `Write` is not implemented for `&[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Write` is implemented for `&mut [u8]`
+note: required by a bound in `wants_write`
+  --> $DIR/issue-90528-unsizing-suggestion-3.rs:10:24
+   |
+LL | fn wants_write(_: impl Write) {}
+   |                        ^^^^^ required by this bound in `wants_write`
+
+error[E0277]: the trait bound `&[u8]: Write` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-3.rs:20:17
+   |
+LL |     wants_write(&[0u8][..]);
+   |     ----------- ^^^^^^^^^^ the trait `Write` is not implemented for `&[u8]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Write` is implemented for `&mut [u8]`
+note: required by a bound in `wants_write`
+  --> $DIR/issue-90528-unsizing-suggestion-3.rs:10:24
+   |
+LL | fn wants_write(_: impl Write) {}
+   |                        ^^^^^ required by this bound in `wants_write`
+help: consider changing this borrow's mutability
+   |
+LL |     wants_write(&mut [0u8][..]);
+   |                 ~~~~
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-4.rs b/tests/ui/dst/issue-90528-unsizing-suggestion-4.rs
new file mode 100644
index 00000000000..eae953c61ff
--- /dev/null
+++ b/tests/ui/dst/issue-90528-unsizing-suggestion-4.rs
@@ -0,0 +1,26 @@
+// Issue #90528: provide helpful suggestions when a trait bound is unsatisfied
+// due to a missed unsizing coercion.
+//
+// This test exercises array variables and a trait implemented on mutable slices.
+
+trait Write {}
+
+impl Write for &mut [u8] {}
+
+fn wants_write(_: impl Write) {}
+
+fn main() {
+    let mut x = [0u8];
+    wants_write(x);
+    //~^ ERROR the trait bound `[u8; 1]: Write` is not satisfied
+    wants_write(&mut x);
+    //~^ ERROR the trait bound `&mut [u8; 1]: Write` is not satisfied
+    wants_write(&mut x[..]);
+
+    let x = &mut [0u8];
+    wants_write(x);
+    //~^ ERROR the trait bound `&mut [u8; 1]: Write` is not satisfied
+    wants_write(*x);
+    //~^ ERROR the trait bound `[u8; 1]: Write` is not satisfied
+    wants_write(&mut x[..]);
+}
diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-4.stderr b/tests/ui/dst/issue-90528-unsizing-suggestion-4.stderr
new file mode 100644
index 00000000000..a4020ee0708
--- /dev/null
+++ b/tests/ui/dst/issue-90528-unsizing-suggestion-4.stderr
@@ -0,0 +1,79 @@
+error[E0277]: the trait bound `[u8; 1]: Write` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-4.rs:14:17
+   |
+LL |     wants_write(x);
+   |     ----------- ^ the trait `Write` is not implemented for `[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Write` is implemented for `&mut [u8]`
+note: required by a bound in `wants_write`
+  --> $DIR/issue-90528-unsizing-suggestion-4.rs:10:24
+   |
+LL | fn wants_write(_: impl Write) {}
+   |                        ^^^^^ required by this bound in `wants_write`
+help: convert the array to a `&mut [u8]` slice instead
+   |
+LL |     wants_write(&mut x[..]);
+   |                 ++++  ++++
+
+error[E0277]: the trait bound `&mut [u8; 1]: Write` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-4.rs:16:17
+   |
+LL |     wants_write(&mut x);
+   |     ----------- ^^^^^^ the trait `Write` is not implemented for `&mut [u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Write` is implemented for `&mut [u8]`
+note: required by a bound in `wants_write`
+  --> $DIR/issue-90528-unsizing-suggestion-4.rs:10:24
+   |
+LL | fn wants_write(_: impl Write) {}
+   |                        ^^^^^ required by this bound in `wants_write`
+help: convert the array to a `&mut [u8]` slice instead
+   |
+LL |     wants_write(&mut x[..]);
+   |                       ++++
+
+error[E0277]: the trait bound `&mut [u8; 1]: Write` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-4.rs:21:17
+   |
+LL |     wants_write(x);
+   |     ----------- ^ the trait `Write` is not implemented for `&mut [u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Write` is implemented for `&mut [u8]`
+note: required by a bound in `wants_write`
+  --> $DIR/issue-90528-unsizing-suggestion-4.rs:10:24
+   |
+LL | fn wants_write(_: impl Write) {}
+   |                        ^^^^^ required by this bound in `wants_write`
+help: convert the array to a `&mut [u8]` slice instead
+   |
+LL |     wants_write(&mut x[..]);
+   |                 ++++  ++++
+
+error[E0277]: the trait bound `[u8; 1]: Write` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-4.rs:23:17
+   |
+LL |     wants_write(*x);
+   |     ----------- ^^ the trait `Write` is not implemented for `[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Write` is implemented for `&mut [u8]`
+note: required by a bound in `wants_write`
+  --> $DIR/issue-90528-unsizing-suggestion-4.rs:10:24
+   |
+LL | fn wants_write(_: impl Write) {}
+   |                        ^^^^^ required by this bound in `wants_write`
+help: convert the array to a `&mut [u8]` slice instead
+   |
+LL |     wants_write(&mut *x[..]);
+   |                 ++++   ++++
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/dyn-star/feature-gate-dyn_star.rs b/tests/ui/dyn-star/feature-gate-dyn_star.rs
index 4756661cf41..41eed71cdc3 100644
--- a/tests/ui/dyn-star/feature-gate-dyn_star.rs
+++ b/tests/ui/dyn-star/feature-gate-dyn_star.rs
@@ -3,7 +3,7 @@
 /// dyn* is not necessarily the final surface syntax (if we have one at all),
 /// but for now we will support it to aid in writing tests independently.
 pub fn dyn_star_parameter(_: &dyn* Send) {
-    //~^ dyn* trait objects are unstable
+    //~^ `dyn*` trait objects are experimental
 }
 
 fn main() {}
diff --git a/tests/ui/dyn-star/feature-gate-dyn_star.stderr b/tests/ui/dyn-star/feature-gate-dyn_star.stderr
index c3449b6278b..342e71c3a3a 100644
--- a/tests/ui/dyn-star/feature-gate-dyn_star.stderr
+++ b/tests/ui/dyn-star/feature-gate-dyn_star.stderr
@@ -1,8 +1,8 @@
-error[E0658]: dyn* trait objects are unstable
+error[E0658]: `dyn*` trait objects are experimental
   --> $DIR/feature-gate-dyn_star.rs:5:31
    |
 LL | pub fn dyn_star_parameter(_: &dyn* Send) {
-   |                               ^^^^^^^^^
+   |                               ^^^^
    |
    = note: see issue #102425 <https://github.com/rust-lang/rust/issues/102425> for more information
    = help: add `#![feature(dyn_star)]` to the crate attributes to enable
diff --git a/tests/ui/dyn-star/gated-span.rs b/tests/ui/dyn-star/gated-span.rs
new file mode 100644
index 00000000000..a747987bd24
--- /dev/null
+++ b/tests/ui/dyn-star/gated-span.rs
@@ -0,0 +1,8 @@
+macro_rules! t {
+    ($t:ty) => {}
+}
+
+t!(dyn* Send);
+//~^ ERROR `dyn*` trait objects are experimental
+
+fn main() {}
diff --git a/tests/ui/dyn-star/gated-span.stderr b/tests/ui/dyn-star/gated-span.stderr
new file mode 100644
index 00000000000..626b6cd1b7f
--- /dev/null
+++ b/tests/ui/dyn-star/gated-span.stderr
@@ -0,0 +1,12 @@
+error[E0658]: `dyn*` trait objects are experimental
+  --> $DIR/gated-span.rs:5:4
+   |
+LL | t!(dyn* Send);
+   |    ^^^^
+   |
+   = note: see issue #102425 <https://github.com/rust-lang/rust/issues/102425> for more information
+   = help: add `#![feature(dyn_star)]` 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/dyn-star/no-explicit-dyn-star-cast.rs b/tests/ui/dyn-star/no-explicit-dyn-star-cast.rs
index 67240c8e8da..2d28f516ab5 100644
--- a/tests/ui/dyn-star/no-explicit-dyn-star-cast.rs
+++ b/tests/ui/dyn-star/no-explicit-dyn-star-cast.rs
@@ -4,8 +4,8 @@ fn make_dyn_star() {
     let i = 42usize;
     let dyn_i: dyn* Debug = i as dyn* Debug;
     //~^ ERROR casting `usize` as `dyn* Debug` is invalid
-    //~| ERROR dyn* trait objects are unstable
-    //~| ERROR dyn* trait objects are unstable
+    //~| ERROR `dyn*` trait objects are experimental
+    //~| ERROR `dyn*` trait objects are experimental
 }
 
 fn main() {
diff --git a/tests/ui/dyn-star/no-explicit-dyn-star-cast.stderr b/tests/ui/dyn-star/no-explicit-dyn-star-cast.stderr
index eb9c933055a..78af9c7a389 100644
--- a/tests/ui/dyn-star/no-explicit-dyn-star-cast.stderr
+++ b/tests/ui/dyn-star/no-explicit-dyn-star-cast.stderr
@@ -1,17 +1,17 @@
-error[E0658]: dyn* trait objects are unstable
+error[E0658]: `dyn*` trait objects are experimental
   --> $DIR/no-explicit-dyn-star-cast.rs:5:16
    |
 LL |     let dyn_i: dyn* Debug = i as dyn* Debug;
-   |                ^^^^^^^^^^
+   |                ^^^^
    |
    = note: see issue #102425 <https://github.com/rust-lang/rust/issues/102425> for more information
    = help: add `#![feature(dyn_star)]` to the crate attributes to enable
 
-error[E0658]: dyn* trait objects are unstable
+error[E0658]: `dyn*` trait objects are experimental
   --> $DIR/no-explicit-dyn-star-cast.rs:5:34
    |
 LL |     let dyn_i: dyn* Debug = i as dyn* Debug;
-   |                                  ^^^^^^^^^^
+   |                                  ^^^^
    |
    = note: see issue #102425 <https://github.com/rust-lang/rust/issues/102425> for more information
    = help: add `#![feature(dyn_star)]` to the crate attributes to enable
diff --git a/tests/ui/error-codes/E0184.stderr b/tests/ui/error-codes/E0184.stderr
index bb3017b6ec2..52f1f30a408 100644
--- a/tests/ui/error-codes/E0184.stderr
+++ b/tests/ui/error-codes/E0184.stderr
@@ -1,4 +1,4 @@
-error[E0184]: the trait `Copy` may not be implemented for this type; the type has a destructor
+error[E0184]: the trait `Copy` cannot be implemented for this type; the type has a destructor
   --> $DIR/E0184.rs:1:10
    |
 LL | #[derive(Copy)]
diff --git a/tests/ui/error-codes/E0206.rs b/tests/ui/error-codes/E0206.rs
index 0f3d427ce11..74738d81015 100644
--- a/tests/ui/error-codes/E0206.rs
+++ b/tests/ui/error-codes/E0206.rs
@@ -2,7 +2,7 @@
 struct Bar;
 
 impl Copy for &'static mut Bar { }
-//~^ ERROR the trait `Copy` may not be implemented for this type
+//~^ ERROR the trait `Copy` cannot be implemented for this type
 
 fn main() {
 }
diff --git a/tests/ui/error-codes/E0206.stderr b/tests/ui/error-codes/E0206.stderr
index 57ae2647d33..60d8d7bfe98 100644
--- a/tests/ui/error-codes/E0206.stderr
+++ b/tests/ui/error-codes/E0206.stderr
@@ -1,4 +1,4 @@
-error[E0206]: the trait `Copy` may not be implemented for this type
+error[E0206]: the trait `Copy` cannot be implemented for this type
   --> $DIR/E0206.rs:4:15
    |
 LL | impl Copy for &'static mut Bar { }
diff --git a/tests/ui/exclusive-drop-and-copy.rs b/tests/ui/exclusive-drop-and-copy.rs
index 7a251671ee9..210ecaed756 100644
--- a/tests/ui/exclusive-drop-and-copy.rs
+++ b/tests/ui/exclusive-drop-and-copy.rs
@@ -1,13 +1,13 @@
 // issue #20126
 
-#[derive(Copy, Clone)] //~ ERROR the trait `Copy` may not be implemented
+#[derive(Copy, Clone)] //~ ERROR the trait `Copy` cannot be implemented
 struct Foo;
 
 impl Drop for Foo {
     fn drop(&mut self) {}
 }
 
-#[derive(Copy, Clone)] //~ ERROR the trait `Copy` may not be implemented
+#[derive(Copy, Clone)] //~ ERROR the trait `Copy` cannot be implemented
 struct Bar<T>(::std::marker::PhantomData<T>);
 
 impl<T> Drop for Bar<T> {
diff --git a/tests/ui/exclusive-drop-and-copy.stderr b/tests/ui/exclusive-drop-and-copy.stderr
index 8649c8abbfa..546079422a7 100644
--- a/tests/ui/exclusive-drop-and-copy.stderr
+++ b/tests/ui/exclusive-drop-and-copy.stderr
@@ -1,4 +1,4 @@
-error[E0184]: the trait `Copy` may not be implemented for this type; the type has a destructor
+error[E0184]: the trait `Copy` cannot be implemented for this type; the type has a destructor
   --> $DIR/exclusive-drop-and-copy.rs:3:10
    |
 LL | #[derive(Copy, Clone)]
@@ -6,7 +6,7 @@ LL | #[derive(Copy, Clone)]
    |
    = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error[E0184]: the trait `Copy` may not be implemented for this type; the type has a destructor
+error[E0184]: the trait `Copy` cannot be implemented for this type; the type has a destructor
   --> $DIR/exclusive-drop-and-copy.rs:10:10
    |
 LL | #[derive(Copy, Clone)]
diff --git a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr
index f647380ef9b..b1613f638d3 100644
--- a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr
+++ b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr
@@ -64,6 +64,12 @@ error[E0229]: associated type bindings are not allowed here
    |
 LL | impl FnOnce() for Foo1 {
    |      ^^^^^^^^ associated type not allowed here
+   |
+help: parenthesized trait syntax expands to `FnOnce<(), Output=()>`
+  --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:16:6
+   |
+LL | impl FnOnce() for Foo1 {
+   |      ^^^^^^^^
 
 error[E0658]: the precise format of `Fn`-family traits' type parameters is subject to change
   --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:23:6
diff --git a/tests/ui/fn/issue-39259.rs b/tests/ui/fn/issue-39259.rs
new file mode 100644
index 00000000000..5872f1007b0
--- /dev/null
+++ b/tests/ui/fn/issue-39259.rs
@@ -0,0 +1,13 @@
+#![feature(fn_traits)]
+#![feature(unboxed_closures)]
+
+struct S;
+
+impl Fn(u32) -> u32 for S {
+//~^ ERROR associated type bindings are not allowed here [E0229]
+    fn call(&self) -> u32 {
+        5
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/fn/issue-39259.stderr b/tests/ui/fn/issue-39259.stderr
new file mode 100644
index 00000000000..b656b76bfe4
--- /dev/null
+++ b/tests/ui/fn/issue-39259.stderr
@@ -0,0 +1,15 @@
+error[E0229]: associated type bindings are not allowed here
+  --> $DIR/issue-39259.rs:6:17
+   |
+LL | impl Fn(u32) -> u32 for S {
+   |                 ^^^ associated type not allowed here
+   |
+help: parenthesized trait syntax expands to `Fn<(u32,), Output=u32>`
+  --> $DIR/issue-39259.rs:6:6
+   |
+LL | impl Fn(u32) -> u32 for S {
+   |      ^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0229`.
diff --git a/tests/ui/higher-rank-trait-bounds/fn-ptr.classic.stderr b/tests/ui/higher-rank-trait-bounds/fn-ptr.classic.stderr
new file mode 100644
index 00000000000..9af6bc45c7a
--- /dev/null
+++ b/tests/ui/higher-rank-trait-bounds/fn-ptr.classic.stderr
@@ -0,0 +1,19 @@
+error[E0277]: expected a `Fn<(&'w (),)>` closure, found `fn(&'w ())`
+  --> $DIR/fn-ptr.rs:12:5
+   |
+LL |     ice();
+   |     ^^^ expected an `Fn<(&'w (),)>` closure, found `fn(&'w ())`
+   |
+   = help: the trait `for<'w> Fn<(&'w (),)>` is not implemented for `fn(&'w ())`
+note: required by a bound in `ice`
+  --> $DIR/fn-ptr.rs:7:25
+   |
+LL | fn ice()
+   |    --- required by a bound in this function
+LL | where
+LL |     for<'w> fn(&'w ()): Fn(&'w ()),
+   |                         ^^^^^^^^^^ required by this bound in `ice`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/higher-rank-trait-bounds/fn-ptr.rs b/tests/ui/higher-rank-trait-bounds/fn-ptr.rs
new file mode 100644
index 00000000000..853160f9612
--- /dev/null
+++ b/tests/ui/higher-rank-trait-bounds/fn-ptr.rs
@@ -0,0 +1,14 @@
+// revisions: classic next
+//[next] compile-flags: -Ztrait-solver=next
+//[next] check-pass
+
+fn ice()
+where
+    for<'w> fn(&'w ()): Fn(&'w ()),
+{
+}
+
+fn main() {
+    ice();
+    //[classic]~^ ERROR expected a `Fn<(&'w (),)>` closure, found `fn(&'w ())`
+}
diff --git a/tests/ui/impl-trait/in-trait/new-lowering-strategy/simple-impl-trait.rs b/tests/ui/impl-trait/in-trait/new-lowering-strategy/simple-impl-trait.rs
new file mode 100644
index 00000000000..ae09d20f6f5
--- /dev/null
+++ b/tests/ui/impl-trait/in-trait/new-lowering-strategy/simple-impl-trait.rs
@@ -0,0 +1,17 @@
+// check-pass
+// compile-flags: -Zlower-impl-trait-in-trait-to-assoc-ty
+
+#![feature(return_position_impl_trait_in_trait)]
+#![allow(incomplete_features)]
+
+trait Foo {
+    fn foo() -> impl Sized;
+}
+
+impl Foo for String {
+    fn foo() -> i32 {
+        22
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/issues/issue-27340.rs b/tests/ui/issues/issue-27340.rs
index 61c77cc1ff3..aff37d95a3f 100644
--- a/tests/ui/issues/issue-27340.rs
+++ b/tests/ui/issues/issue-27340.rs
@@ -1,6 +1,6 @@
 struct Foo;
 #[derive(Copy, Clone)]
-//~^ ERROR the trait `Copy` may not be implemented for this type
+//~^ ERROR the trait `Copy` cannot be implemented for this type
 struct Bar(Foo);
 
 fn main() {}
diff --git a/tests/ui/issues/issue-27340.stderr b/tests/ui/issues/issue-27340.stderr
index 40889b86668..9caaffd9c9a 100644
--- a/tests/ui/issues/issue-27340.stderr
+++ b/tests/ui/issues/issue-27340.stderr
@@ -1,4 +1,4 @@
-error[E0204]: the trait `Copy` may not be implemented for this type
+error[E0204]: the trait `Copy` cannot be implemented for this type
   --> $DIR/issue-27340.rs:2:10
    |
 LL | #[derive(Copy, Clone)]
diff --git a/tests/ui/issues/issue-3029.rs b/tests/ui/issues/issue-3029.rs
index a5d30960a4c..43c8a0a23fb 100644
--- a/tests/ui/issues/issue-3029.rs
+++ b/tests/ui/issues/issue-3029.rs
@@ -2,9 +2,7 @@
 // error-pattern:so long
 // ignore-emscripten no processes
 
-#![allow(unused_allocation)]
 #![allow(unreachable_code)]
-#![allow(unused_variables)]
 
 fn main() {
     let mut x = Vec::new();
diff --git a/tests/ui/issues/issue-66667-function-cmp-cycle.rs b/tests/ui/issues/issue-66667-function-cmp-cycle.rs
index 7b025be11a0..b4f09fbbb04 100644
--- a/tests/ui/issues/issue-66667-function-cmp-cycle.rs
+++ b/tests/ui/issues/issue-66667-function-cmp-cycle.rs
@@ -1,16 +1,19 @@
 fn first() {
     second == 1 //~ ERROR binary operation
     //~^ ERROR mismatched types
+    //~| ERROR mismatched types
 }
 
 fn second() {
     first == 1 //~ ERROR binary operation
     //~^ ERROR mismatched types
+    //~| ERROR mismatched types
 }
 
 fn bar() {
     bar == 1 //~ ERROR binary operation
     //~^ ERROR mismatched types
+    //~| ERROR mismatched types
 }
 
 fn main() {}
diff --git a/tests/ui/issues/issue-66667-function-cmp-cycle.stderr b/tests/ui/issues/issue-66667-function-cmp-cycle.stderr
index 887699ef5ce..d9a960ce197 100644
--- a/tests/ui/issues/issue-66667-function-cmp-cycle.stderr
+++ b/tests/ui/issues/issue-66667-function-cmp-cycle.stderr
@@ -15,8 +15,16 @@ LL |     second == 1
    = note: expected fn item `fn() {second}`
                  found type `{integer}`
 
+error[E0308]: mismatched types
+  --> $DIR/issue-66667-function-cmp-cycle.rs:2:5
+   |
+LL | fn first() {
+   |            - help: try adding a return type: `-> bool`
+LL |     second == 1
+   |     ^^^^^^^^^^^ expected `()`, found `bool`
+
 error[E0369]: binary operation `==` cannot be applied to type `fn() {first}`
-  --> $DIR/issue-66667-function-cmp-cycle.rs:7:11
+  --> $DIR/issue-66667-function-cmp-cycle.rs:8:11
    |
 LL |     first == 1
    |     ----- ^^ - {integer}
@@ -24,7 +32,7 @@ LL |     first == 1
    |     fn() {first}
 
 error[E0308]: mismatched types
-  --> $DIR/issue-66667-function-cmp-cycle.rs:7:14
+  --> $DIR/issue-66667-function-cmp-cycle.rs:8:14
    |
 LL |     first == 1
    |              ^ expected fn item, found integer
@@ -32,8 +40,16 @@ LL |     first == 1
    = note: expected fn item `fn() {first}`
                  found type `{integer}`
 
+error[E0308]: mismatched types
+  --> $DIR/issue-66667-function-cmp-cycle.rs:8:5
+   |
+LL | fn second() {
+   |             - help: try adding a return type: `-> bool`
+LL |     first == 1
+   |     ^^^^^^^^^^ expected `()`, found `bool`
+
 error[E0369]: binary operation `==` cannot be applied to type `fn() {bar}`
-  --> $DIR/issue-66667-function-cmp-cycle.rs:12:9
+  --> $DIR/issue-66667-function-cmp-cycle.rs:14:9
    |
 LL |     bar == 1
    |     --- ^^ - {integer}
@@ -41,7 +57,7 @@ LL |     bar == 1
    |     fn() {bar}
 
 error[E0308]: mismatched types
-  --> $DIR/issue-66667-function-cmp-cycle.rs:12:12
+  --> $DIR/issue-66667-function-cmp-cycle.rs:14:12
    |
 LL |     bar == 1
    |            ^ expected fn item, found integer
@@ -49,7 +65,15 @@ LL |     bar == 1
    = note: expected fn item `fn() {bar}`
                  found type `{integer}`
 
-error: aborting due to 6 previous errors
+error[E0308]: mismatched types
+  --> $DIR/issue-66667-function-cmp-cycle.rs:14:5
+   |
+LL | fn bar() {
+   |          - help: try adding a return type: `-> bool`
+LL |     bar == 1
+   |     ^^^^^^^^ expected `()`, found `bool`
+
+error: aborting due to 9 previous errors
 
 Some errors have detailed explanations: E0308, E0369.
 For more information about an error, try `rustc --explain E0308`.
diff --git a/tests/ui/iterators/into-iter-on-arrays-lint.fixed b/tests/ui/iterators/into-iter-on-arrays-lint.fixed
index 6e02a7024b9..5b91aaf9ea5 100644
--- a/tests/ui/iterators/into-iter-on-arrays-lint.fixed
+++ b/tests/ui/iterators/into-iter-on-arrays-lint.fixed
@@ -2,7 +2,7 @@
 // run-rustfix
 // rustfix-only-machine-applicable
 
-#[allow(unused_must_use)]
+#[allow(unused_must_use, unused_allocation)]
 fn main() {
     let small = [1, 2];
     let big = [0u8; 33];
diff --git a/tests/ui/iterators/into-iter-on-arrays-lint.rs b/tests/ui/iterators/into-iter-on-arrays-lint.rs
index 582d5cadd06..25b0cef73d7 100644
--- a/tests/ui/iterators/into-iter-on-arrays-lint.rs
+++ b/tests/ui/iterators/into-iter-on-arrays-lint.rs
@@ -2,7 +2,7 @@
 // run-rustfix
 // rustfix-only-machine-applicable
 
-#[allow(unused_must_use)]
+#[allow(unused_must_use, unused_allocation)]
 fn main() {
     let small = [1, 2];
     let big = [0u8; 33];
diff --git a/tests/ui/lifetimes/issue-95023.stderr b/tests/ui/lifetimes/issue-95023.stderr
index 35c3797c77a..5b93eff8614 100644
--- a/tests/ui/lifetimes/issue-95023.stderr
+++ b/tests/ui/lifetimes/issue-95023.stderr
@@ -25,6 +25,12 @@ error[E0229]: associated type bindings are not allowed here
    |
 LL | impl Fn(&isize) for Error {
    |      ^^^^^^^^^^ associated type not allowed here
+   |
+help: parenthesized trait syntax expands to `Fn<(&isize,), Output=()>`
+  --> $DIR/issue-95023.rs:3:6
+   |
+LL | impl Fn(&isize) for Error {
+   |      ^^^^^^^^^^
 
 error[E0220]: associated type `B` not found for `Self`
   --> $DIR/issue-95023.rs:6:44
diff --git a/tests/ui/lint/unused/unused-allocation.rs b/tests/ui/lint/unused/unused-allocation.rs
new file mode 100644
index 00000000000..c1a6f5ceaf1
--- /dev/null
+++ b/tests/ui/lint/unused/unused-allocation.rs
@@ -0,0 +1,7 @@
+#![feature(rustc_attrs, stmt_expr_attributes)]
+#![deny(unused_allocation)]
+
+fn main() {
+    _ = (#[rustc_box] Box::new([1])).len(); //~ error: unnecessary allocation, use `&` instead
+    _ = Box::new([1]).len(); //~ error: unnecessary allocation, use `&` instead
+}
diff --git a/tests/ui/lint/unused/unused-allocation.stderr b/tests/ui/lint/unused/unused-allocation.stderr
new file mode 100644
index 00000000000..c9ccfbd30e5
--- /dev/null
+++ b/tests/ui/lint/unused/unused-allocation.stderr
@@ -0,0 +1,20 @@
+error: unnecessary allocation, use `&` instead
+  --> $DIR/unused-allocation.rs:5:9
+   |
+LL |     _ = (#[rustc_box] Box::new([1])).len();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/unused-allocation.rs:2:9
+   |
+LL | #![deny(unused_allocation)]
+   |         ^^^^^^^^^^^^^^^^^
+
+error: unnecessary allocation, use `&` instead
+  --> $DIR/unused-allocation.rs:6:9
+   |
+LL |     _ = Box::new([1]).len();
+   |         ^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/macros/bad-concat.stderr b/tests/ui/macros/bad-concat.stderr
index 4316fd312c7..d67f3c33d36 100644
--- a/tests/ui/macros/bad-concat.stderr
+++ b/tests/ui/macros/bad-concat.stderr
@@ -4,7 +4,7 @@ error: expected a literal
 LL |     let _ = concat!(x, y, z, "bar");
    |                     ^  ^  ^
    |
-   = note: only literals (like `"foo"`, `42` and `3.14`) can be passed to `concat!()`
+   = note: only literals (like `"foo"`, `-42` and `3.14`) can be passed to `concat!()`
 
 error: aborting due to previous error
 
diff --git a/tests/ui/macros/concat.stderr b/tests/ui/macros/concat.stderr
index 61fb9de1ef9..d65d9354454 100644
--- a/tests/ui/macros/concat.stderr
+++ b/tests/ui/macros/concat.stderr
@@ -16,7 +16,7 @@ error: expected a literal
 LL |     concat!(foo);
    |             ^^^
    |
-   = note: only literals (like `"foo"`, `42` and `3.14`) can be passed to `concat!()`
+   = note: only literals (like `"foo"`, `-42` and `3.14`) can be passed to `concat!()`
 
 error: expected a literal
   --> $DIR/concat.rs:5:13
@@ -24,7 +24,7 @@ error: expected a literal
 LL |     concat!(foo());
    |             ^^^^^
    |
-   = note: only literals (like `"foo"`, `42` and `3.14`) can be passed to `concat!()`
+   = note: only literals (like `"foo"`, `-42` and `3.14`) can be passed to `concat!()`
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/macros/issue-106837.rs b/tests/ui/macros/issue-106837.rs
new file mode 100644
index 00000000000..7bbd3d68a90
--- /dev/null
+++ b/tests/ui/macros/issue-106837.rs
@@ -0,0 +1,10 @@
+fn main() {
+    concat!(-42);
+    concat!(-3.14);
+
+    concat!(-"hello");
+    //~^ ERROR expected a literal
+
+    concat!(--1);
+    //~^ ERROR expected a literal
+}
diff --git a/tests/ui/macros/issue-106837.stderr b/tests/ui/macros/issue-106837.stderr
new file mode 100644
index 00000000000..998d6c5eb6f
--- /dev/null
+++ b/tests/ui/macros/issue-106837.stderr
@@ -0,0 +1,18 @@
+error: expected a literal
+  --> $DIR/issue-106837.rs:5:13
+   |
+LL |     concat!(-"hello");
+   |             ^^^^^^^^
+   |
+   = note: only literals (like `"foo"`, `-42` and `3.14`) can be passed to `concat!()`
+
+error: expected a literal
+  --> $DIR/issue-106837.rs:8:13
+   |
+LL |     concat!(--1);
+   |             ^^^
+   |
+   = note: only literals (like `"foo"`, `-42` and `3.14`) can be passed to `concat!()`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/macros/issue-98790.rs b/tests/ui/macros/issue-98790.rs
new file mode 100644
index 00000000000..8fe6fc41d10
--- /dev/null
+++ b/tests/ui/macros/issue-98790.rs
@@ -0,0 +1,24 @@
+// run-pass
+
+macro_rules! stringify_item {
+    ($item:item) => {
+        stringify!($item)
+    };
+}
+
+macro_rules! repro {
+    ($expr:expr) => {
+        stringify_item! {
+            pub fn repro() -> bool {
+                $expr
+            }
+        }
+    };
+}
+
+fn main() {
+    assert_eq!(
+        repro!(match () { () => true } | true),
+        "pub fn repro() -> bool { (match () { () => true, }) | true }"
+    );
+}
diff --git a/tests/ui/macros/nonterminal-matching.stderr b/tests/ui/macros/nonterminal-matching.stderr
index 5bbd5439098..762ecc3207f 100644
--- a/tests/ui/macros/nonterminal-matching.stderr
+++ b/tests/ui/macros/nonterminal-matching.stderr
@@ -18,6 +18,7 @@ LL |     macro n(a $nt_item b) {
 ...
 LL | complex_nonterminal!(enum E {});
    | ------------------------------- in this macro invocation
+   = note: captured metavariables except for `$tt`, `$ident` and `$lifetime` cannot be compared to other tokens
    = note: this error originates in the macro `complex_nonterminal` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to previous error
diff --git a/tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs b/tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs
index b8b6f0846bb..39ba1714cc7 100644
--- a/tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs
+++ b/tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs
@@ -164,7 +164,7 @@ fn main() {
     // mac call
 
     // match
-    [ match elem { _ => elem } == 3 ] => "Assertion failed: match elem { _ => elem, } == 3"
+    [ match elem { _ => elem } == 3 ] => "Assertion failed: (match elem { _ => elem, }) == 3"
 
     // ret
     [ (|| { return elem; })() == 3 ] => "Assertion failed: (|| { return elem; })() == 3"
diff --git a/tests/ui/opt-in-copy.rs b/tests/ui/opt-in-copy.rs
index 0b48418e464..d0257b5745d 100644
--- a/tests/ui/opt-in-copy.rs
+++ b/tests/ui/opt-in-copy.rs
@@ -5,7 +5,7 @@ struct IWantToCopyThis {
 }
 
 impl Copy for IWantToCopyThis {}
-//~^ ERROR the trait `Copy` may not be implemented for this type
+//~^ ERROR the trait `Copy` cannot be implemented for this type
 
 enum CantCopyThisEither {
     A,
@@ -17,6 +17,6 @@ enum IWantToCopyThisToo {
 }
 
 impl Copy for IWantToCopyThisToo {}
-//~^ ERROR the trait `Copy` may not be implemented for this type
+//~^ ERROR the trait `Copy` cannot be implemented for this type
 
 fn main() {}
diff --git a/tests/ui/opt-in-copy.stderr b/tests/ui/opt-in-copy.stderr
index 4461567df0a..258ff16e6e4 100644
--- a/tests/ui/opt-in-copy.stderr
+++ b/tests/ui/opt-in-copy.stderr
@@ -1,4 +1,4 @@
-error[E0204]: the trait `Copy` may not be implemented for this type
+error[E0204]: the trait `Copy` cannot be implemented for this type
   --> $DIR/opt-in-copy.rs:7:15
    |
 LL |     but_i_cant: CantCopyThis,
@@ -7,7 +7,7 @@ LL |     but_i_cant: CantCopyThis,
 LL | impl Copy for IWantToCopyThis {}
    |               ^^^^^^^^^^^^^^^
 
-error[E0204]: the trait `Copy` may not be implemented for this type
+error[E0204]: the trait `Copy` cannot be implemented for this type
   --> $DIR/opt-in-copy.rs:19:15
    |
 LL |     ButICant(CantCopyThisEither),
diff --git a/tests/ui/parser/integer-literal-start-ident.rs b/tests/ui/parser/integer-literal-start-ident.rs
new file mode 100644
index 00000000000..12537482e0f
--- /dev/null
+++ b/tests/ui/parser/integer-literal-start-ident.rs
@@ -0,0 +1,2 @@
+fn 1main() {}
+//~^ ERROR expected identifier, found `1main`
diff --git a/tests/ui/parser/integer-literal-start-ident.stderr b/tests/ui/parser/integer-literal-start-ident.stderr
new file mode 100644
index 00000000000..51c37a0d24c
--- /dev/null
+++ b/tests/ui/parser/integer-literal-start-ident.stderr
@@ -0,0 +1,10 @@
+error: expected identifier, found `1main`
+  --> $DIR/integer-literal-start-ident.rs:1:4
+   |
+LL | fn 1main() {}
+   |    ^^^^^ expected identifier
+   |
+   = help: identifiers cannot start with a number
+
+error: aborting due to previous error
+
diff --git a/tests/ui/parser/issues/issue-104088.rs b/tests/ui/parser/issues/issue-104088.rs
index 5f794fe2dc9..86988c8cd21 100644
--- a/tests/ui/parser/issues/issue-104088.rs
+++ b/tests/ui/parser/issues/issue-104088.rs
@@ -4,12 +4,12 @@ fn test() {
 
 fn test_2() {
     let 1x = 123;
-    //~^ ERROR expected identifier, found number literal
+    //~^ ERROR expected identifier, found `1x`
 }
 
 fn test_3() {
     let 2x: i32 = 123;
-    //~^ ERROR expected identifier, found number literal
+    //~^ ERROR expected identifier, found `2x`
 }
 
 fn test_4() {
@@ -20,7 +20,7 @@ fn test_4() {
 
 fn test_5() {
     let 23name = 123;
-    //~^ ERROR expected identifier, found number literal
+    //~^ ERROR expected identifier, found `23name`
 }
 
 fn main() {}
diff --git a/tests/ui/parser/issues/issue-104088.stderr b/tests/ui/parser/issues/issue-104088.stderr
index ff4b4bdb695..6511a313149 100644
--- a/tests/ui/parser/issues/issue-104088.stderr
+++ b/tests/ui/parser/issues/issue-104088.stderr
@@ -1,20 +1,26 @@
-error: expected identifier, found number literal
+error: expected identifier, found `1x`
   --> $DIR/issue-104088.rs:6:9
    |
 LL |     let 1x = 123;
-   |         ^^ identifiers cannot start with a number
+   |         ^^ expected identifier
+   |
+   = help: identifiers cannot start with a number
 
-error: expected identifier, found number literal
+error: expected identifier, found `2x`
   --> $DIR/issue-104088.rs:11:9
    |
 LL |     let 2x: i32 = 123;
-   |         ^^ identifiers cannot start with a number
+   |         ^^ expected identifier
+   |
+   = help: identifiers cannot start with a number
 
-error: expected identifier, found number literal
+error: expected identifier, found `23name`
   --> $DIR/issue-104088.rs:22:9
    |
 LL |     let 23name = 123;
-   |         ^^^^^^ identifiers cannot start with a number
+   |         ^^^^^^ expected identifier
+   |
+   = help: identifiers cannot start with a number
 
 error[E0308]: mismatched types
   --> $DIR/issue-104088.rs:16:12
diff --git a/tests/ui/range/range_traits-2.stderr b/tests/ui/range/range_traits-2.stderr
index 61facba535b..0829fc2ce9b 100644
--- a/tests/ui/range/range_traits-2.stderr
+++ b/tests/ui/range/range_traits-2.stderr
@@ -1,4 +1,4 @@
-error[E0204]: the trait `Copy` may not be implemented for this type
+error[E0204]: the trait `Copy` cannot be implemented for this type
   --> $DIR/range_traits-2.rs:3:10
    |
 LL | #[derive(Copy, Clone)]
diff --git a/tests/ui/range/range_traits-3.stderr b/tests/ui/range/range_traits-3.stderr
index e54d17b329e..db19d1baec9 100644
--- a/tests/ui/range/range_traits-3.stderr
+++ b/tests/ui/range/range_traits-3.stderr
@@ -1,4 +1,4 @@
-error[E0204]: the trait `Copy` may not be implemented for this type
+error[E0204]: the trait `Copy` cannot be implemented for this type
   --> $DIR/range_traits-3.rs:3:10
    |
 LL | #[derive(Copy, Clone)]
diff --git a/tests/ui/range/range_traits-6.stderr b/tests/ui/range/range_traits-6.stderr
index addc525f1fa..dfc74f87ca7 100644
--- a/tests/ui/range/range_traits-6.stderr
+++ b/tests/ui/range/range_traits-6.stderr
@@ -1,4 +1,4 @@
-error[E0204]: the trait `Copy` may not be implemented for this type
+error[E0204]: the trait `Copy` cannot be implemented for this type
   --> $DIR/range_traits-6.rs:3:10
    |
 LL | #[derive(Copy, Clone)]
diff --git a/tests/ui/rfc-2632-const-trait-impl/gate.rs b/tests/ui/rfc-2632-const-trait-impl/gate.rs
index f2cd26c91b6..d1c93ab9f95 100644
--- a/tests/ui/rfc-2632-const-trait-impl/gate.rs
+++ b/tests/ui/rfc-2632-const-trait-impl/gate.rs
@@ -1,5 +1,13 @@
 // gate-test-const_closures
+
 fn main() {
     (const || {})();
     //~^ ERROR: const closures are experimental
 }
+
+macro_rules! e {
+    ($e:expr) => {}
+}
+
+e!((const || {}));
+//~^ ERROR const closures are experimental
diff --git a/tests/ui/rfc-2632-const-trait-impl/gate.stderr b/tests/ui/rfc-2632-const-trait-impl/gate.stderr
index 30edc4127e1..11cc2cd569a 100644
--- a/tests/ui/rfc-2632-const-trait-impl/gate.stderr
+++ b/tests/ui/rfc-2632-const-trait-impl/gate.stderr
@@ -1,12 +1,21 @@
 error[E0658]: const closures are experimental
-  --> $DIR/gate.rs:3:6
+  --> $DIR/gate.rs:4:6
    |
 LL |     (const || {})();
-   |      ^^^^^^^^^^^
+   |      ^^^^^
    |
    = note: see issue #106003 <https://github.com/rust-lang/rust/issues/106003> for more information
    = help: add `#![feature(const_closures)]` to the crate attributes to enable
 
-error: aborting due to previous error
+error[E0658]: const closures are experimental
+  --> $DIR/gate.rs:12:5
+   |
+LL | e!((const || {}));
+   |     ^^^^^
+   |
+   = note: see issue #106003 <https://github.com/rust-lang/rust/issues/106003> for more information
+   = help: add `#![feature(const_closures)]` to the crate attributes to enable
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.rs
new file mode 100644
index 00000000000..0d59e50264e
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.rs
@@ -0,0 +1,7 @@
+// only-x86_64
+
+#![feature(target_feature_11)]
+
+#[target_feature(enable = "avx2")]
+fn main() {}
+//~^ ERROR `main` function is not allowed to have `#[target_feature]`
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.stderr
new file mode 100644
index 00000000000..cfafbd52286
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.stderr
@@ -0,0 +1,8 @@
+error: `main` function is not allowed to have `#[target_feature]`
+  --> $DIR/issue-108645-target-feature-on-main.rs:6:1
+   |
+LL | fn main() {}
+   | ^^^^^^^^^ `main` function is not allowed to have `#[target_feature]`
+
+error: aborting due to previous error
+
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.rs
new file mode 100644
index 00000000000..50e8ce2fdd5
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.rs
@@ -0,0 +1,9 @@
+// only-x86_64
+
+#![feature(start)]
+#![feature(target_feature_11)]
+
+#[start]
+#[target_feature(enable = "avx2")]
+//~^ ERROR `start` is not allowed to have `#[target_feature]`
+fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 }
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.stderr
new file mode 100644
index 00000000000..07687f3c7f4
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.stderr
@@ -0,0 +1,11 @@
+error: `start` is not allowed to have `#[target_feature]`
+  --> $DIR/issue-108645-target-feature-on-start.rs:7:1
+   |
+LL | #[target_feature(enable = "avx2")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL | fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 }
+   | -------------------------------------------------------- `start` is not allowed to have `#[target_feature]`
+
+error: aborting due to previous error
+
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs
index 7314fa8cced..9108f27b5f7 100644
--- a/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs
+++ b/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs
@@ -18,4 +18,10 @@ impl Foo for Bar {
     unsafe fn unsf_foo(&self) {}
 }
 
+trait Qux {
+    #[target_feature(enable = "sse2")]
+    //~^ ERROR cannot be applied to safe trait method
+    fn foo(&self) {}
+}
+
 fn main() {}
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr
index 07d6e090059..eb0f18edd34 100644
--- a/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr
+++ b/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr
@@ -1,4 +1,13 @@
 error: `#[target_feature(..)]` cannot be applied to safe trait method
+  --> $DIR/trait-impl.rs:22:5
+   |
+LL |     #[target_feature(enable = "sse2")]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be applied to safe trait method
+LL |
+LL |     fn foo(&self) {}
+   |     ------------- not an `unsafe` function
+
+error: `#[target_feature(..)]` cannot be applied to safe trait method
   --> $DIR/trait-impl.rs:13:5
    |
 LL |     #[target_feature(enable = "sse2")]
@@ -7,5 +16,5 @@ LL |
 LL |     fn foo(&self) {}
    |     ------------- not an `unsafe` function
 
-error: aborting due to previous error
+error: aborting due to 2 previous errors
 
diff --git a/tests/ui/self/arbitrary_self_types_trait.rs b/tests/ui/self/arbitrary_self_types_trait.rs
index 973c7cae85a..c4651ec7177 100644
--- a/tests/ui/self/arbitrary_self_types_trait.rs
+++ b/tests/ui/self/arbitrary_self_types_trait.rs
@@ -1,4 +1,5 @@
 // run-pass
+#![allow(unused_allocation)]
 
 use std::rc::Rc;
 
@@ -13,7 +14,7 @@ impl Trait for Vec<i32> {
 }
 
 fn main() {
-    let v = vec![1,2,3];
+    let v = vec![1, 2, 3];
 
-    assert_eq!(&[1,2,3], Box::new(Rc::new(v)).trait_method());
+    assert_eq!(&[1, 2, 3], Box::new(Rc::new(v)).trait_method());
 }
diff --git a/tests/ui/span/E0204.rs b/tests/ui/span/E0204.rs
index 174de8cdd63..8793a05c8a8 100644
--- a/tests/ui/span/E0204.rs
+++ b/tests/ui/span/E0204.rs
@@ -2,9 +2,9 @@ struct Foo {
     foo: Vec<u32>,
 }
 
-impl Copy for Foo { } //~ ERROR may not be implemented for this type
+impl Copy for Foo { } //~ ERROR cannot be implemented for this type
 
-#[derive(Copy)] //~ ERROR may not be implemented for this type
+#[derive(Copy)] //~ ERROR cannot be implemented for this type
 struct Foo2<'a> {
     ty: &'a mut bool,
 }
@@ -14,9 +14,9 @@ enum EFoo {
     Baz,
 }
 
-impl Copy for EFoo { } //~ ERROR may not be implemented for this type
+impl Copy for EFoo { } //~ ERROR cannot be implemented for this type
 
-#[derive(Copy)] //~ ERROR may not be implemented for this type
+#[derive(Copy)] //~ ERROR cannot be implemented for this type
 enum EFoo2<'a> {
     Bar(&'a mut bool),
     Baz,
diff --git a/tests/ui/span/E0204.stderr b/tests/ui/span/E0204.stderr
index 0b2166eed7e..3a0afb541ba 100644
--- a/tests/ui/span/E0204.stderr
+++ b/tests/ui/span/E0204.stderr
@@ -1,4 +1,4 @@
-error[E0204]: the trait `Copy` may not be implemented for this type
+error[E0204]: the trait `Copy` cannot be implemented for this type
   --> $DIR/E0204.rs:5:15
    |
 LL |     foo: Vec<u32>,
@@ -7,7 +7,7 @@ LL |     foo: Vec<u32>,
 LL | impl Copy for Foo { }
    |               ^^^
 
-error[E0204]: the trait `Copy` may not be implemented for this type
+error[E0204]: the trait `Copy` cannot be implemented for this type
   --> $DIR/E0204.rs:7:10
    |
 LL | #[derive(Copy)]
@@ -18,7 +18,7 @@ LL |     ty: &'a mut bool,
    |
    = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error[E0204]: the trait `Copy` may not be implemented for this type
+error[E0204]: the trait `Copy` cannot be implemented for this type
   --> $DIR/E0204.rs:17:15
    |
 LL |     Bar { x: Vec<u32> },
@@ -27,7 +27,7 @@ LL |     Bar { x: Vec<u32> },
 LL | impl Copy for EFoo { }
    |               ^^^^
 
-error[E0204]: the trait `Copy` may not be implemented for this type
+error[E0204]: the trait `Copy` cannot be implemented for this type
   --> $DIR/E0204.rs:19:10
    |
 LL | #[derive(Copy)]
diff --git a/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs
new file mode 100644
index 00000000000..582b480aa25
--- /dev/null
+++ b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs
@@ -0,0 +1,12 @@
+#![feature(min_specialization)]
+
+// An impl that has an erroneous const substitution should not specialize one
+// that is well-formed.
+
+struct S<const L: usize>;
+
+impl<const N: i32> Copy for S<N> {}
+impl<const M: usize> Copy for S<M> {}
+//~^ ERROR conflicting implementations of trait `Copy` for type `S<_>`
+
+fn main() {}
diff --git a/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr
new file mode 100644
index 00000000000..a3906a9a22f
--- /dev/null
+++ b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr
@@ -0,0 +1,11 @@
+error[E0119]: conflicting implementations of trait `Copy` for type `S<_>`
+  --> $DIR/bad-const-wf-doesnt-specialize.rs:9:1
+   |
+LL | impl<const N: i32> Copy for S<N> {}
+   | -------------------------------- first implementation here
+LL | impl<const M: usize> Copy for S<M> {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `S<_>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/tests/ui/str/str-escape.rs b/tests/ui/str/str-escape.rs
index 0264632fd24..10a72421f24 100644
--- a/tests/ui/str/str-escape.rs
+++ b/tests/ui/str/str-escape.rs
@@ -1,11 +1,31 @@
 // check-pass
+// ignore-tidy-tab
+
 fn main() {
     let s = "\
 
              ";
     //~^^^ WARNING multiple lines skipped by escaped newline
+    assert_eq!(s, "");
+
     let s = "foo\
              bar
              ";
-    //~^^^ WARNING non-ASCII whitespace symbol '\u{a0}' is not skipped
+    //~^^^ WARNING whitespace symbol '\u{a0}' is not skipped
+    assert_eq!(s, "foo           bar\n             ");
+
+    let s = "a\
+ b";
+    assert_eq!(s, "ab");
+
+    let s = "a\
+	b";
+    assert_eq!(s, "ab");
+
+    let s = "a\
+    b";
+    //~^^ WARNING whitespace symbol '\u{c}' is not skipped
+    // '\x0c' is ASCII whitespace, but it may not need skipped
+    // discussion: https://github.com/rust-lang/rust/pull/108403
+    assert_eq!(s, "a\x0cb");
 }
diff --git a/tests/ui/str/str-escape.stderr b/tests/ui/str/str-escape.stderr
index b2501f1a214..43b4f7e36f6 100644
--- a/tests/ui/str/str-escape.stderr
+++ b/tests/ui/str/str-escape.stderr
@@ -1,5 +1,5 @@
 warning: multiple lines skipped by escaped newline
-  --> $DIR/str-escape.rs:3:14
+  --> $DIR/str-escape.rs:5:14
    |
 LL |       let s = "\
    |  ______________^
@@ -7,15 +7,25 @@ LL | |
 LL | |              ";
    | |_____________^ skipping everything up to and including this point
 
-warning: non-ASCII whitespace symbol '\u{a0}' is not skipped
-  --> $DIR/str-escape.rs:7:17
+warning: whitespace symbol '\u{a0}' is not skipped
+  --> $DIR/str-escape.rs:11:17
    |
 LL |       let s = "foo\
    |  _________________^
 LL | |              bar
-   | |   ^ non-ASCII whitespace symbol '\u{a0}' is not skipped
+   | |   ^ whitespace symbol '\u{a0}' is not skipped
    | |___|
    | 
 
-warning: 2 warnings emitted
+warning: whitespace symbol '\u{c}' is not skipped
+  --> $DIR/str-escape.rs:25:15
+   |
+LL |       let s = "a\
+   |  _______________^
+LL | |     b";
+   | |    ^- whitespace symbol '\u{c}' is not skipped
+   | |____|
+   | 
+
+warning: 3 warnings emitted
 
diff --git a/tests/ui/structs-enums/align-struct.rs b/tests/ui/structs-enums/align-struct.rs
index f5418e754b2..54092542f98 100644
--- a/tests/ui/structs-enums/align-struct.rs
+++ b/tests/ui/structs-enums/align-struct.rs
@@ -1,5 +1,5 @@
 // run-pass
-#![allow(dead_code)]
+#![allow(dead_code, unused_allocation)]
 
 use std::mem;
 
@@ -20,7 +20,6 @@ struct AlignMany(i32);
 
 // Raising alignment may not alter size.
 #[repr(align(8))]
-#[allow(dead_code)]
 struct Align8Many {
     a: i32,
     b: i32,
@@ -29,9 +28,8 @@ struct Align8Many {
 }
 
 enum Enum {
-    #[allow(dead_code)]
     A(i32),
-    B(Align16)
+    B(Align16),
 }
 
 // Nested alignment - use `#[repr(C)]` to suppress field reordering for sizeof test
@@ -73,7 +71,7 @@ struct AlignLarge {
 
 union UnionContainsAlign {
     a: Align16,
-    b: f32
+    b: f32,
 }
 
 impl Align16 {
@@ -158,7 +156,7 @@ pub fn main() {
     // Note that the size of Nested may change if struct field re-ordering is enabled
     assert_eq!(mem::align_of::<Nested>(), 16);
     assert_eq!(mem::size_of::<Nested>(), 48);
-    let a = Nested{ a: 1, b: 2, c: Align16(3), d: 4};
+    let a = Nested { a: 1, b: 2, c: Align16(3), d: 4 };
     assert_eq!(mem::align_of_val(&a), 16);
     assert_eq!(mem::align_of_val(&a.b), 4);
     assert_eq!(mem::align_of_val(&a.c), 16);
@@ -179,8 +177,8 @@ pub fn main() {
             assert_eq!(a.0, 15);
             assert_eq!(mem::align_of_val(a), 16);
             assert_eq!(mem::size_of_val(a), 16);
-        },
-        _ => ()
+        }
+        _ => (),
     }
     assert!(is_aligned_to(&e, 16));
 
@@ -197,8 +195,8 @@ pub fn main() {
     }
 
     // arrays of aligned elements should also be aligned
-    assert_eq!(mem::align_of::<[Align16;2]>(), 16);
-    assert_eq!(mem::size_of::<[Align16;2]>(), 32);
+    assert_eq!(mem::align_of::<[Align16; 2]>(), 16);
+    assert_eq!(mem::size_of::<[Align16; 2]>(), 32);
 
     let a = [Align16(0), Align16(1)];
     assert_eq!(mem::align_of_val(&a[0]), 16);
@@ -209,7 +207,7 @@ pub fn main() {
     assert_eq!(mem::align_of_val(Box::new(Align16(0)).as_ref()), 16);
 
     // check heap array is aligned
-    let a = vec!(Align16(0), Align16(1));
+    let a = vec![Align16(0), Align16(1)];
     assert_eq!(mem::align_of_val(&a[0]), 16);
     assert_eq!(mem::align_of_val(&a[1]), 16);
 
@@ -224,16 +222,14 @@ pub fn main() {
 
     assert_eq!(mem::align_of::<AlignContainsPacked4C>(), 16);
     assert_eq!(mem::size_of::<AlignContainsPacked4C>(), 32);
-    let a = AlignContainsPacked4C { a: Packed4C{ a: 1, b: 2 }, b: 3 };
+    let a = AlignContainsPacked4C { a: Packed4C { a: 1, b: 2 }, b: 3 };
     assert_eq!(mem::align_of_val(&a), 16);
     assert_eq!(mem::align_of_val(&a.a), 4);
     assert_eq!(mem::align_of_val(&a.b), mem::align_of::<u64>());
     assert_eq!(mem::size_of_val(&a), 32);
     assert!(is_aligned_to(&a, 16));
 
-    let mut large = Box::new(AlignLarge {
-        stuff: [0; 0x10000],
-    });
+    let mut large = Box::new(AlignLarge { stuff: [0; 0x10000] });
     large.stuff[0] = 132;
     *large.stuff.last_mut().unwrap() = 102;
     assert_eq!(large.stuff[0], 132);
diff --git a/tests/ui/suggestions/correct-binder-for-arbitrary-bound-sugg.rs b/tests/ui/suggestions/correct-binder-for-arbitrary-bound-sugg.rs
new file mode 100644
index 00000000000..e56c8622ece
--- /dev/null
+++ b/tests/ui/suggestions/correct-binder-for-arbitrary-bound-sugg.rs
@@ -0,0 +1,16 @@
+trait Foo
+where
+    for<'a> &'a Self: Bar,
+{
+}
+
+impl Foo for () {}
+
+trait Bar {}
+
+impl Bar for &() {}
+
+fn foo<T: Foo>() {}
+//~^ ERROR the trait bound `for<'a> &'a T: Bar` is not satisfied
+
+fn main() {}
diff --git a/tests/ui/suggestions/correct-binder-for-arbitrary-bound-sugg.stderr b/tests/ui/suggestions/correct-binder-for-arbitrary-bound-sugg.stderr
new file mode 100644
index 00000000000..2298e7f4e0c
--- /dev/null
+++ b/tests/ui/suggestions/correct-binder-for-arbitrary-bound-sugg.stderr
@@ -0,0 +1,22 @@
+error[E0277]: the trait bound `for<'a> &'a T: Bar` is not satisfied
+  --> $DIR/correct-binder-for-arbitrary-bound-sugg.rs:13:11
+   |
+LL | fn foo<T: Foo>() {}
+   |           ^^^ the trait `for<'a> Bar` is not implemented for `&'a T`
+   |
+note: required by a bound in `Foo`
+  --> $DIR/correct-binder-for-arbitrary-bound-sugg.rs:3:23
+   |
+LL | trait Foo
+   |       --- required by a bound in this trait
+LL | where
+LL |     for<'a> &'a Self: Bar,
+   |                       ^^^ required by this bound in `Foo`
+help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
+   |
+LL | fn foo<T: Foo>() where for<'a> &'a T: Bar {}
+   |                  ++++++++++++++++++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.fixed b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.fixed
index 304360d48a2..47b35b412c0 100644
--- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.fixed
+++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.fixed
@@ -7,7 +7,7 @@ pub struct Vector2<T: Debug + Copy + Clone>{
     pub y: T
 }
 
-#[derive(Debug, Copy, Clone)] //~ ERROR the trait `Copy` may not be implemented for this type
+#[derive(Debug, Copy, Clone)] //~ ERROR the trait `Copy` cannot be implemented for this type
 pub struct AABB<K: Copy + Debug>{
     pub loc: Vector2<K>,
     pub size: Vector2<K>
diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.rs b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.rs
index 14e1fbb3311..771e9105c62 100644
--- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.rs
+++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.rs
@@ -7,7 +7,7 @@ pub struct Vector2<T: Debug + Copy + Clone>{
     pub y: T
 }
 
-#[derive(Debug, Copy, Clone)] //~ ERROR the trait `Copy` may not be implemented for this type
+#[derive(Debug, Copy, Clone)] //~ ERROR the trait `Copy` cannot be implemented for this type
 pub struct AABB<K: Copy>{
     pub loc: Vector2<K>,
     pub size: Vector2<K>
diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.stderr b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.stderr
index faf730a5ce3..09696e0613e 100644
--- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.stderr
+++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.stderr
@@ -1,4 +1,4 @@
-error[E0204]: the trait `Copy` may not be implemented for this type
+error[E0204]: the trait `Copy` cannot be implemented for this type
   --> $DIR/missing-bound-in-derive-copy-impl-3.rs:10:17
    |
 LL | #[derive(Debug, Copy, Clone)]
@@ -6,16 +6,12 @@ LL | #[derive(Debug, Copy, Clone)]
 LL | pub struct AABB<K: Copy>{
 LL |     pub loc: Vector2<K>,
    |     ------------------- this field does not implement `Copy`
-LL |     pub size: Vector2<K>
-   |     -------------------- this field does not implement `Copy`
    |
 note: the `Copy` impl for `Vector2<K>` requires that `K: Debug`
   --> $DIR/missing-bound-in-derive-copy-impl-3.rs:12:14
    |
 LL |     pub loc: Vector2<K>,
    |              ^^^^^^^^^^
-LL |     pub size: Vector2<K>
-   |               ^^^^^^^^^^
    = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider further restricting this bound
    |
diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl.rs b/tests/ui/suggestions/missing-bound-in-derive-copy-impl.rs
index 52163bddd4f..9c7b7ba099c 100644
--- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl.rs
+++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl.rs
@@ -6,7 +6,7 @@ pub struct Vector2<T: Debug + Copy + Clone>{
     pub y: T
 }
 
-#[derive(Debug, Copy, Clone)] //~ ERROR the trait `Copy` may not be implemented for this type
+#[derive(Debug, Copy, Clone)] //~ ERROR the trait `Copy` cannot be implemented for this type
 pub struct AABB<K>{
     pub loc: Vector2<K>,
     pub size: Vector2<K>
diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr b/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr
index 11bc5409917..8585fe47bf3 100644
--- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr
+++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr
@@ -1,4 +1,4 @@
-error[E0204]: the trait `Copy` may not be implemented for this type
+error[E0204]: the trait `Copy` cannot be implemented for this type
   --> $DIR/missing-bound-in-derive-copy-impl.rs:9:17
    |
 LL | #[derive(Debug, Copy, Clone)]
@@ -6,16 +6,12 @@ LL | #[derive(Debug, Copy, Clone)]
 LL | pub struct AABB<K>{
 LL |     pub loc: Vector2<K>,
    |     ------------------- this field does not implement `Copy`
-LL |     pub size: Vector2<K>
-   |     -------------------- this field does not implement `Copy`
    |
 note: the `Copy` impl for `Vector2<K>` requires that `K: Debug`
   --> $DIR/missing-bound-in-derive-copy-impl.rs:11:14
    |
 LL |     pub loc: Vector2<K>,
    |              ^^^^^^^^^^
-LL |     pub size: Vector2<K>
-   |               ^^^^^^^^^^
    = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider restricting type parameter `K`
    |
diff --git a/tests/ui/suggestions/missing-bound-in-manual-copy-impl-2.fixed b/tests/ui/suggestions/missing-bound-in-manual-copy-impl-2.fixed
index 691e7553a09..f32c61a99bb 100644
--- a/tests/ui/suggestions/missing-bound-in-manual-copy-impl-2.fixed
+++ b/tests/ui/suggestions/missing-bound-in-manual-copy-impl-2.fixed
@@ -14,6 +14,6 @@ impl<T: std::fmt::Display> Clone for OnlyCopyIfDisplay<T> {
 impl<T: std::fmt::Display> Copy for OnlyCopyIfDisplay<T> {}
 
 impl<S: std::fmt::Display> Copy for Wrapper<OnlyCopyIfDisplay<S>> {}
-//~^ ERROR the trait `Copy` may not be implemented for this type
+//~^ ERROR the trait `Copy` cannot be implemented for this type
 
 fn main() {}
diff --git a/tests/ui/suggestions/missing-bound-in-manual-copy-impl-2.rs b/tests/ui/suggestions/missing-bound-in-manual-copy-impl-2.rs
index e3185e7eff8..d7725f4a374 100644
--- a/tests/ui/suggestions/missing-bound-in-manual-copy-impl-2.rs
+++ b/tests/ui/suggestions/missing-bound-in-manual-copy-impl-2.rs
@@ -14,6 +14,6 @@ impl<T: std::fmt::Display> Clone for OnlyCopyIfDisplay<T> {
 impl<T: std::fmt::Display> Copy for OnlyCopyIfDisplay<T> {}
 
 impl<S> Copy for Wrapper<OnlyCopyIfDisplay<S>> {}
-//~^ ERROR the trait `Copy` may not be implemented for this type
+//~^ ERROR the trait `Copy` cannot be implemented for this type
 
 fn main() {}
diff --git a/tests/ui/suggestions/missing-bound-in-manual-copy-impl-2.stderr b/tests/ui/suggestions/missing-bound-in-manual-copy-impl-2.stderr
index 9e6f0d9ebbd..856d8db381b 100644
--- a/tests/ui/suggestions/missing-bound-in-manual-copy-impl-2.stderr
+++ b/tests/ui/suggestions/missing-bound-in-manual-copy-impl-2.stderr
@@ -1,4 +1,4 @@
-error[E0204]: the trait `Copy` may not be implemented for this type
+error[E0204]: the trait `Copy` cannot be implemented for this type
   --> $DIR/missing-bound-in-manual-copy-impl-2.rs:16:18
    |
 LL | struct Wrapper<T>(T);
diff --git a/tests/ui/suggestions/missing-bound-in-manual-copy-impl.fixed b/tests/ui/suggestions/missing-bound-in-manual-copy-impl.fixed
index 32a7215c5bd..1139b315313 100644
--- a/tests/ui/suggestions/missing-bound-in-manual-copy-impl.fixed
+++ b/tests/ui/suggestions/missing-bound-in-manual-copy-impl.fixed
@@ -4,6 +4,6 @@
 struct Wrapper<T>(T);
 
 impl<S: Copy> Copy for Wrapper<S> {}
-//~^ ERROR the trait `Copy` may not be implemented for this type
+//~^ ERROR the trait `Copy` cannot be implemented for this type
 
 fn main() {}
diff --git a/tests/ui/suggestions/missing-bound-in-manual-copy-impl.rs b/tests/ui/suggestions/missing-bound-in-manual-copy-impl.rs
index c688f4d41ee..19549248efc 100644
--- a/tests/ui/suggestions/missing-bound-in-manual-copy-impl.rs
+++ b/tests/ui/suggestions/missing-bound-in-manual-copy-impl.rs
@@ -4,6 +4,6 @@
 struct Wrapper<T>(T);
 
 impl<S> Copy for Wrapper<S> {}
-//~^ ERROR the trait `Copy` may not be implemented for this type
+//~^ ERROR the trait `Copy` cannot be implemented for this type
 
 fn main() {}
diff --git a/tests/ui/suggestions/missing-bound-in-manual-copy-impl.stderr b/tests/ui/suggestions/missing-bound-in-manual-copy-impl.stderr
index fe2d133c8aa..ec3e4f23a64 100644
--- a/tests/ui/suggestions/missing-bound-in-manual-copy-impl.stderr
+++ b/tests/ui/suggestions/missing-bound-in-manual-copy-impl.stderr
@@ -1,4 +1,4 @@
-error[E0204]: the trait `Copy` may not be implemented for this type
+error[E0204]: the trait `Copy` cannot be implemented for this type
   --> $DIR/missing-bound-in-manual-copy-impl.rs:6:18
    |
 LL | struct Wrapper<T>(T);
diff --git a/tests/ui/threads-sendsync/issue-43733-2.rs b/tests/ui/threads-sendsync/issue-43733-2.rs
index 32baeec4359..8f7a9c08375 100644
--- a/tests/ui/threads-sendsync/issue-43733-2.rs
+++ b/tests/ui/threads-sendsync/issue-43733-2.rs
@@ -21,7 +21,7 @@ impl<T> Key<T> {
 }
 
 #[cfg(target_thread_local)]
-use std::thread::__FastLocalKeyInner as Key;
+use std::thread::__LocalKeyInner as Key;
 
 static __KEY: Key<()> = Key::new();
 //~^ ERROR `UnsafeCell<Option<()>>` cannot be shared between threads
diff --git a/tests/ui/threads-sendsync/issue-43733.rs b/tests/ui/threads-sendsync/issue-43733.rs
index 935e02944b9..0eadef3e3e8 100644
--- a/tests/ui/threads-sendsync/issue-43733.rs
+++ b/tests/ui/threads-sendsync/issue-43733.rs
@@ -1,8 +1,8 @@
 // ignore-wasm32
 // revisions: mir thir
 // [thir]compile-flags: -Z thir-unsafeck
-// normalize-stderr-test: "__FastLocalKeyInner::<T>::get" -> "$$LOCALKEYINNER::<T>::get"
-// normalize-stderr-test: "__OsLocalKeyInner::<T>::get" -> "$$LOCALKEYINNER::<T>::get"
+// normalize-stderr-test: "__LocalKeyInner::<T>::get" -> "$$LOCALKEYINNER::<T>::get"
+// normalize-stderr-test: "__LocalKeyInner::<T>::get" -> "$$LOCALKEYINNER::<T>::get"
 #![feature(thread_local)]
 #![feature(cfg_target_thread_local, thread_local_internals)]
 
@@ -12,10 +12,10 @@ type Foo = std::cell::RefCell<String>;
 
 #[cfg(target_thread_local)]
 #[thread_local]
-static __KEY: std::thread::__FastLocalKeyInner<Foo> = std::thread::__FastLocalKeyInner::new();
+static __KEY: std::thread::__LocalKeyInner<Foo> = std::thread::__LocalKeyInner::new();
 
 #[cfg(not(target_thread_local))]
-static __KEY: std::thread::__OsLocalKeyInner<Foo> = std::thread::__OsLocalKeyInner::new();
+static __KEY: std::thread::__LocalKeyInner<Foo> = std::thread::__LocalKeyInner::new();
 
 fn __getit(_: Option<&mut Option<RefCell<String>>>) -> std::option::Option<&'static Foo> {
     __KEY.get(Default::default)
diff --git a/tests/ui/traits/copy-is-not-modulo-regions.not_static.stderr b/tests/ui/traits/copy-is-not-modulo-regions.not_static.stderr
index edd94d2010b..13042521184 100644
--- a/tests/ui/traits/copy-is-not-modulo-regions.not_static.stderr
+++ b/tests/ui/traits/copy-is-not-modulo-regions.not_static.stderr
@@ -1,4 +1,4 @@
-error[E0204]: the trait `Copy` may not be implemented for this type
+error[E0204]: the trait `Copy` cannot be implemented for this type
   --> $DIR/copy-is-not-modulo-regions.rs:13:21
    |
 LL | struct Bar<'lt>(Foo<'lt>);
diff --git a/tests/ui/traits/copy-is-not-modulo-regions.rs b/tests/ui/traits/copy-is-not-modulo-regions.rs
index adb87023769..b899083747a 100644
--- a/tests/ui/traits/copy-is-not-modulo-regions.rs
+++ b/tests/ui/traits/copy-is-not-modulo-regions.rs
@@ -11,7 +11,7 @@ struct Bar<'lt>(Foo<'lt>);
 
 #[cfg(not_static)]
 impl<'any> Copy for Bar<'any> {}
-//[not_static]~^ the trait `Copy` may not be implemented for this type
+//[not_static]~^ the trait `Copy` cannot be implemented for this type
 
 #[cfg(yes_static)]
 impl<'any> Copy for Bar<'static> {}
diff --git a/tests/ui/traits/inductive-overflow/lifetime.rs b/tests/ui/traits/inductive-overflow/lifetime.rs
index bf536d21cf9..2f3c90dcece 100644
--- a/tests/ui/traits/inductive-overflow/lifetime.rs
+++ b/tests/ui/traits/inductive-overflow/lifetime.rs
@@ -15,9 +15,9 @@ impl<'a> Y for C<'a> {
 struct C<'a>(&'a ());
 struct X<T: Y>(T::P);
 
-impl<T: NotAuto> NotAuto for Box<T> {} //~ NOTE: required
+impl<T: NotAuto> NotAuto for Box<T> {}
+impl<T: Y> NotAuto for X<T> where T::P: NotAuto {} //~ NOTE: required
 //~^ NOTE unsatisfied trait bound introduced here
-impl<T: Y> NotAuto for X<T> where T::P: NotAuto {}
 impl<'a> NotAuto for C<'a> {}
 
 fn is_send<S: NotAuto>() {}
@@ -28,6 +28,4 @@ fn main() {
     // Should only be a few notes.
     is_send::<X<C<'static>>>();
     //~^ ERROR overflow evaluating
-    //~| 3 redundant requirements hidden
-    //~| required for
 }
diff --git a/tests/ui/traits/inductive-overflow/lifetime.stderr b/tests/ui/traits/inductive-overflow/lifetime.stderr
index 357e59991a3..7ab2864a8cf 100644
--- a/tests/ui/traits/inductive-overflow/lifetime.stderr
+++ b/tests/ui/traits/inductive-overflow/lifetime.stderr
@@ -1,18 +1,14 @@
-error[E0275]: overflow evaluating the requirement `X<C<'_>>: NotAuto`
+error[E0275]: overflow evaluating the requirement `Box<X<C<'static>>>: NotAuto`
   --> $DIR/lifetime.rs:29:5
    |
 LL |     is_send::<X<C<'static>>>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: required for `Box<X<C<'_>>>` to implement `NotAuto`
-  --> $DIR/lifetime.rs:18:18
+note: required for `X<C<'static>>` to implement `NotAuto`
+  --> $DIR/lifetime.rs:19:12
    |
-LL | impl<T: NotAuto> NotAuto for Box<T> {}
-   |         -------  ^^^^^^^     ^^^^^^
-   |         |
-   |         unsatisfied trait bound introduced here
-   = note: 3 redundant requirements hidden
-   = note: required for `X<C<'static>>` to implement `NotAuto`
+LL | impl<T: Y> NotAuto for X<T> where T::P: NotAuto {}
+   |            ^^^^^^^     ^^^^             ------- unsatisfied trait bound introduced here
 note: required by a bound in `is_send`
   --> $DIR/lifetime.rs:23:15
    |
diff --git a/tests/ui/traits/issue-50480.rs b/tests/ui/traits/issue-50480.rs
index 005939e0c46..683a85a32c1 100644
--- a/tests/ui/traits/issue-50480.rs
+++ b/tests/ui/traits/issue-50480.rs
@@ -1,5 +1,5 @@
 #[derive(Clone, Copy)]
-//~^ ERROR the trait `Copy` may not be implemented for this type
+//~^ ERROR the trait `Copy` cannot be implemented for this type
 struct Foo(N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
 //~^ ERROR cannot find type `NotDefined` in this scope
 //~| ERROR cannot find type `NotDefined` in this scope
@@ -7,7 +7,7 @@ struct Foo(N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
 //~| ERROR cannot find type `N` in this scope
 
 #[derive(Clone, Copy)]
-//~^ ERROR the trait `Copy` may not be implemented for this type
+//~^ ERROR the trait `Copy` cannot be implemented for this type
 struct Bar<T>(T, N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
 //~^ ERROR cannot find type `NotDefined` in this scope
 //~| ERROR cannot find type `N` in this scope
diff --git a/tests/ui/traits/issue-50480.stderr b/tests/ui/traits/issue-50480.stderr
index 5063fdca092..4f72db60a16 100644
--- a/tests/ui/traits/issue-50480.stderr
+++ b/tests/ui/traits/issue-50480.stderr
@@ -60,7 +60,7 @@ error[E0412]: cannot find type `NotDefined` in this scope
 LL | struct Bar<T>(T, N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
    |                     ^^^^^^^^^^ not found in this scope
 
-error[E0204]: the trait `Copy` may not be implemented for this type
+error[E0204]: the trait `Copy` cannot be implemented for this type
   --> $DIR/issue-50480.rs:1:17
    |
 LL | #[derive(Clone, Copy)]
@@ -73,7 +73,7 @@ LL | struct Foo(N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
    |
    = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error[E0204]: the trait `Copy` may not be implemented for this type
+error[E0204]: the trait `Copy` cannot be implemented for this type
   --> $DIR/issue-50480.rs:9:17
    |
 LL | #[derive(Clone, Copy)]
diff --git a/tests/ui/traits/issue-87558.stderr b/tests/ui/traits/issue-87558.stderr
index 494274d8c30..b647f9794bd 100644
--- a/tests/ui/traits/issue-87558.stderr
+++ b/tests/ui/traits/issue-87558.stderr
@@ -17,6 +17,12 @@ error[E0229]: associated type bindings are not allowed here
    |
 LL | impl Fn(&isize) for Error {
    |      ^^^^^^^^^^ associated type not allowed here
+   |
+help: parenthesized trait syntax expands to `Fn<(&isize,), Output=()>`
+  --> $DIR/issue-87558.rs:3:6
+   |
+LL | impl Fn(&isize) for Error {
+   |      ^^^^^^^^^^
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/traits/new-solver/canonical-int-var-eq-in-response.rs b/tests/ui/traits/new-solver/canonical-int-var-eq-in-response.rs
new file mode 100644
index 00000000000..3f7316a2279
--- /dev/null
+++ b/tests/ui/traits/new-solver/canonical-int-var-eq-in-response.rs
@@ -0,0 +1,21 @@
+// check-pass
+
+trait Mirror {
+    type Assoc;
+}
+
+impl<T> Mirror for T {
+    type Assoc = T;
+}
+
+trait Test {}
+impl Test for i64 {}
+impl Test for u64 {}
+
+fn mirror_me<T: Mirror>(t: T, s: <T as Mirror>::Assoc) where <T as Mirror>::Assoc: Test {}
+
+fn main() {
+    let mut x = 0;
+    mirror_me(x, 1);
+    x = 1i64;
+}
diff --git a/tests/ui/traits/new-solver/canonical-ty-var-eq-in-response.rs b/tests/ui/traits/new-solver/canonical-ty-var-eq-in-response.rs
new file mode 100644
index 00000000000..d1c6b1077e8
--- /dev/null
+++ b/tests/ui/traits/new-solver/canonical-ty-var-eq-in-response.rs
@@ -0,0 +1,39 @@
+// check-pass
+// compile-flags: -Ztrait-solver=next
+
+trait Mirror {
+    type Item;
+}
+
+struct Wrapper<T>(T);
+impl<T> Mirror for Wrapper<T> {
+    type Item = T;
+}
+
+fn mirror<T>()
+where
+    Wrapper<T>: Mirror<Item = i32>,
+{
+}
+
+fn main() {
+    mirror::<_ /* ?0 */>();
+
+    // Solving `<Wrapper<?0> as Mirror>::Item = i32`
+
+    // First, we replace the term with a fresh infer var:
+    // `<Wrapper<?0> as Mirror>::Item = ?1`
+
+    // We select the impl candidate on line #6, which leads us to learn that
+    // `?0 == ?1`.
+
+    // That should be reflected in our canonical response, which should have
+    // `^0 = ^0, ^1 = ^0`
+    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+    // !! We used to return a totally unconstrained response here :< !!
+    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+    // Then, during the "equate term" part of the projection solving, we
+    // instantiate the response from the unconstrained projection predicate,
+    // and equate `?0 == i32`.
+}
diff --git a/tests/ui/typeck/lazy-norm/cast-checks-handling-projections.rs b/tests/ui/traits/new-solver/cast-checks-handling-projections.rs
index 5ff567cd07c..3b261062f78 100644
--- a/tests/ui/typeck/lazy-norm/cast-checks-handling-projections.rs
+++ b/tests/ui/traits/new-solver/cast-checks-handling-projections.rs
@@ -1,5 +1,5 @@
 // compile-flags: -Ztrait-solver=next
-// known-bug: unknown
+// check-pass
 
 fn main() {
     (0u8 + 0u8) as char;
diff --git a/tests/ui/traits/new-solver/deduce-ty-from-object.rs b/tests/ui/traits/new-solver/deduce-ty-from-object.rs
new file mode 100644
index 00000000000..7398bce7b61
--- /dev/null
+++ b/tests/ui/traits/new-solver/deduce-ty-from-object.rs
@@ -0,0 +1,6 @@
+// check-pass
+// compile-flags: -Ztrait-solver=next
+
+fn main() {
+    let x: Box<dyn Iterator<Item = ()>> = Box::new(std::iter::empty());
+}
diff --git a/tests/ui/typeck/lazy-norm/equating-projection-cyclically.rs b/tests/ui/traits/new-solver/equating-projection-cyclically.rs
index 019c6e81c50..019c6e81c50 100644
--- a/tests/ui/typeck/lazy-norm/equating-projection-cyclically.rs
+++ b/tests/ui/traits/new-solver/equating-projection-cyclically.rs
diff --git a/tests/ui/typeck/lazy-norm/equating-projection-cyclically.stderr b/tests/ui/traits/new-solver/equating-projection-cyclically.stderr
index 57cbc65a17a..57cbc65a17a 100644
--- a/tests/ui/typeck/lazy-norm/equating-projection-cyclically.stderr
+++ b/tests/ui/traits/new-solver/equating-projection-cyclically.stderr
diff --git a/tests/ui/traits/new-solver/int-var-alias-eq.rs b/tests/ui/traits/new-solver/int-var-alias-eq.rs
new file mode 100644
index 00000000000..2da387db4a9
--- /dev/null
+++ b/tests/ui/traits/new-solver/int-var-alias-eq.rs
@@ -0,0 +1,18 @@
+// check-pass
+// compile-flags: -Ztrait-solver=next
+
+// HIR typeck ends up equating `<_#0i as Add>::Output == _#0i`.
+// Want to make sure that we emit an alias-eq goal for this,
+// instead of treating it as a type error and bailing.
+
+fn test() {
+    // fallback
+    let x = 1 + 2;
+}
+
+fn test2() -> u32 {
+    // expectation from return ty
+    1 + 2
+}
+
+fn main() {}
diff --git a/tests/ui/traits/new-solver/lazy-nested-obligations-1.rs b/tests/ui/traits/new-solver/lazy-nested-obligations-1.rs
new file mode 100644
index 00000000000..af00cbb3ba8
--- /dev/null
+++ b/tests/ui/traits/new-solver/lazy-nested-obligations-1.rs
@@ -0,0 +1,13 @@
+// check-pass
+// compile-flags: -Ztrait-solver=next
+// Issue 94358
+
+fn foo<C>(_: C)
+where
+    for <'a> &'a C: IntoIterator,
+    for <'a> <&'a C as IntoIterator>::IntoIter: ExactSizeIterator,
+{}
+
+fn main() {
+    foo::<_>(vec![true, false]);
+}
diff --git a/tests/ui/traits/new-solver/lazy-nested-obligations-2.rs b/tests/ui/traits/new-solver/lazy-nested-obligations-2.rs
new file mode 100644
index 00000000000..32addd829dc
--- /dev/null
+++ b/tests/ui/traits/new-solver/lazy-nested-obligations-2.rs
@@ -0,0 +1,23 @@
+// check-pass
+// compile-flags: -Ztrait-solver=next
+// Issue 95863
+
+pub trait With {
+    type F;
+}
+
+impl With for i32 {
+    type F = fn(&str);
+}
+
+fn f(_: &str) {}
+
+fn main() {
+    let _: V<i32> = V(f);
+    pub struct V<T: With>(<T as With>::F);
+
+    pub enum E3<T: With> {
+        Var(<T as With>::F),
+    }
+    let _: E3<i32> = E3::Var(f);
+}
diff --git a/tests/ui/traits/new-solver/lazy-nested-obligations-3.rs b/tests/ui/traits/new-solver/lazy-nested-obligations-3.rs
new file mode 100644
index 00000000000..baf39957240
--- /dev/null
+++ b/tests/ui/traits/new-solver/lazy-nested-obligations-3.rs
@@ -0,0 +1,38 @@
+// check-pass
+// compile-flags: -Ztrait-solver=next
+// Issue 96750
+
+use std::marker::PhantomData;
+
+trait AsyncFn<Arg> {
+    type Output;
+}
+trait RequestFamily {
+    type Type<'a>;
+}
+trait Service {}
+
+struct MyFn;
+impl AsyncFn<String> for MyFn {
+    type Output = ();
+}
+
+impl RequestFamily for String {
+    type Type<'a> = String;
+}
+
+struct ServiceFromAsyncFn<F, Req>(F, PhantomData<Req>);
+
+impl<F, Req, O> Service for ServiceFromAsyncFn<F, Req>
+where
+    Req: RequestFamily,
+    F: AsyncFn<Req>,
+    F: for<'a> AsyncFn<Req::Type<'a>, Output = O>,
+{
+}
+
+fn assert_service() -> impl Service {
+    ServiceFromAsyncFn(MyFn, PhantomData)
+}
+
+fn main() {}
diff --git a/tests/ui/traits/new-solver/normalize-param-env-1.rs b/tests/ui/traits/new-solver/normalize-param-env-1.rs
new file mode 100644
index 00000000000..b02a5d62330
--- /dev/null
+++ b/tests/ui/traits/new-solver/normalize-param-env-1.rs
@@ -0,0 +1,40 @@
+// check-pass
+// compile-flags: -Ztrait-solver=next
+// Issue 108933
+
+trait Add<Rhs> {
+    type Sum;
+}
+
+impl Add<()> for () {
+    type Sum = ();
+}
+
+type Unit = <() as Add<()>>::Sum;
+
+trait Trait<C> {
+    type Output;
+}
+
+fn f<T>()
+where
+    T: Trait<()>,
+    <T as Trait<()>>::Output: Sized,
+{
+}
+
+fn g<T>()
+where
+    T: Trait<Unit>,
+    <T as Trait<()>>::Output: Sized,
+{
+}
+
+fn h<T>()
+where
+    T: Trait<()>,
+    <T as Trait<Unit>>::Output: Sized,
+{
+}
+
+fn main() {}
diff --git a/tests/ui/traits/new-solver/normalize-param-env-2.rs b/tests/ui/traits/new-solver/normalize-param-env-2.rs
new file mode 100644
index 00000000000..7c2cebdd200
--- /dev/null
+++ b/tests/ui/traits/new-solver/normalize-param-env-2.rs
@@ -0,0 +1,26 @@
+// check-pass
+// compile-flags: -Ztrait-solver=next
+// Issue 92505
+
+trait A<T> {
+    type I;
+
+    fn f()
+    where
+        Self::I: A<T>,
+    {
+    }
+}
+
+impl<T> A<T> for () {
+    type I = ();
+
+    fn f()
+    where
+        Self::I: A<T>,
+    {
+        <() as A<T>>::f();
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/traits/new-solver/normalize-param-env-3.rs b/tests/ui/traits/new-solver/normalize-param-env-3.rs
new file mode 100644
index 00000000000..ce2974b2a16
--- /dev/null
+++ b/tests/ui/traits/new-solver/normalize-param-env-3.rs
@@ -0,0 +1,32 @@
+// check-pass
+// compile-flags: -Ztrait-solver=next
+// Issue 100177
+
+trait GenericTrait<T> {}
+
+trait Channel<I>: GenericTrait<Self::T> {
+    type T;
+}
+
+trait Sender {
+    type Msg;
+
+    fn send<C>()
+    where
+        C: Channel<Self::Msg>;
+}
+
+impl<T> Sender for T {
+    type Msg = ();
+
+    fn send<C>()
+    where
+        C: Channel<Self::Msg>,
+    {
+    }
+}
+
+// This works
+fn foo<I, C>(ch: C) where C: Channel<I> {}
+
+fn main() {}
diff --git a/tests/ui/traits/unsend-future.rs b/tests/ui/traits/unsend-future.rs
new file mode 100644
index 00000000000..fbbc07b11e7
--- /dev/null
+++ b/tests/ui/traits/unsend-future.rs
@@ -0,0 +1,21 @@
+// edition:2021
+
+// issue 108897
+trait Handler {}
+impl<F, Fut> Handler for F
+where
+    Fut: Send,
+    F: FnOnce() -> Fut,
+{}
+
+fn require_handler<H: Handler>(h: H) {}
+
+async fn handler() {
+    let a = &1 as *const i32;
+    async {}.await;
+}
+
+fn main() {
+    require_handler(handler)
+     //~^ ERROR future cannot be sent between threads safely
+}
diff --git a/tests/ui/traits/unsend-future.stderr b/tests/ui/traits/unsend-future.stderr
new file mode 100644
index 00000000000..4aaa7c4a924
--- /dev/null
+++ b/tests/ui/traits/unsend-future.stderr
@@ -0,0 +1,24 @@
+error: future cannot be sent between threads safely
+  --> $DIR/unsend-future.rs:19:21
+   |
+LL |     require_handler(handler)
+   |                     ^^^^^^^ future returned by `handler` is not `Send`
+   |
+   = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `*const i32`
+note: future is not `Send` as this value is used across an await
+  --> $DIR/unsend-future.rs:15:13
+   |
+LL |     let a = &1 as *const i32;
+   |         - has type `*const i32` which is not `Send`
+LL |     async {}.await;
+   |             ^^^^^^ await occurs here, with `a` maybe used later
+LL | }
+   | - `a` is later dropped here
+note: required by a bound in `require_handler`
+  --> $DIR/unsend-future.rs:11:23
+   |
+LL | fn require_handler<H: Handler>(h: H) {}
+   |                       ^^^^^^^ required by this bound in `require_handler`
+
+error: aborting due to previous error
+
diff --git a/tests/ui/transmutability/issue-101739-1.rs b/tests/ui/transmutability/issue-101739-1.rs
index bcb8b158edf..2b966609108 100644
--- a/tests/ui/transmutability/issue-101739-1.rs
+++ b/tests/ui/transmutability/issue-101739-1.rs
@@ -6,7 +6,7 @@ mod assert {
     pub fn is_transmutable<Src, Context, const ASSUME_ALIGNMENT: bool>()
     where
         Dst: BikeshedIntrinsicFrom<Src, Context, ASSUME_ALIGNMENT>, //~ ERROR cannot find type `Dst` in this scope
-        //~^ ERROR mismatched types
+        //~^ the constant `ASSUME_ALIGNMENT` is not of type `Assume`
     {
     }
 }
diff --git a/tests/ui/transmutability/issue-101739-1.stderr b/tests/ui/transmutability/issue-101739-1.stderr
index 7c6b533ef5f..f0fa93722b8 100644
--- a/tests/ui/transmutability/issue-101739-1.stderr
+++ b/tests/ui/transmutability/issue-101739-1.stderr
@@ -4,13 +4,15 @@ error[E0412]: cannot find type `Dst` in this scope
 LL |         Dst: BikeshedIntrinsicFrom<Src, Context, ASSUME_ALIGNMENT>,
    |         ^^^ not found in this scope
 
-error[E0308]: mismatched types
-  --> $DIR/issue-101739-1.rs:8:50
+error: the constant `ASSUME_ALIGNMENT` is not of type `Assume`
+  --> $DIR/issue-101739-1.rs:8:14
    |
 LL |         Dst: BikeshedIntrinsicFrom<Src, Context, ASSUME_ALIGNMENT>,
-   |                                                  ^^^^^^^^^^^^^^^^ expected `Assume`, found `bool`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: required by a bound in `BikeshedIntrinsicFrom`
+  --> $SRC_DIR/core/src/mem/transmutability.rs:LL:COL
 
 error: aborting due to 2 previous errors
 
-Some errors have detailed explanations: E0308, E0412.
-For more information about an error, try `rustc --explain E0308`.
+For more information about this error, try `rustc --explain E0412`.
diff --git a/tests/ui/typeck/lazy-norm/cast-checks-handling-projections.stderr b/tests/ui/typeck/lazy-norm/cast-checks-handling-projections.stderr
deleted file mode 100644
index 6b09ccd5214..00000000000
--- a/tests/ui/typeck/lazy-norm/cast-checks-handling-projections.stderr
+++ /dev/null
@@ -1,9 +0,0 @@
-error[E0271]: type mismatch resolving `char == <u8 as Add>::Output`
-  --> $DIR/cast-checks-handling-projections.rs:5:5
-   |
-LL |     (0u8 + 0u8) as char;
-   |     ^^^^^^^^^^^ types differ
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0271`.
diff --git a/tests/ui/union/field_checks.rs b/tests/ui/union/field_checks.rs
index d5d1e44ac85..bb4eccb4cf8 100644
--- a/tests/ui/union/field_checks.rs
+++ b/tests/ui/union/field_checks.rs
@@ -21,15 +21,15 @@ union U24<T> { // OK
 }
 
 union U3 {
-    a: String, //~ ERROR unions cannot contain fields that may need dropping
+    a: String, //~ ERROR field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
 }
 
 union U32 { // field that does not drop but is not `Copy`, either
-    a: std::cell::RefCell<i32>, //~ ERROR unions cannot contain fields that may need dropping
+    a: std::cell::RefCell<i32>, //~ ERROR field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
 }
 
 union U4<T> {
-    a: T, //~ ERROR unions cannot contain fields that may need dropping
+    a: T, //~ ERROR field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
 }
 
 union U5 { // Having a drop impl is OK
@@ -41,11 +41,11 @@ impl Drop for U5 {
 }
 
 union U5Nested { // a nested union that drops is NOT OK
-    nest: U5, //~ ERROR unions cannot contain fields that may need dropping
+    nest: U5, //~ ERROR field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
 }
 
 union U5Nested2 { // for now we don't special-case empty arrays
-    nest: [U5; 0], //~ ERROR unions cannot contain fields that may need dropping
+    nest: [U5; 0], //~ ERROR field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
 }
 
 union U6 { // OK
diff --git a/tests/ui/union/field_checks.stderr b/tests/ui/union/field_checks.stderr
index 1f97e97ac6e..32407a74970 100644
--- a/tests/ui/union/field_checks.stderr
+++ b/tests/ui/union/field_checks.stderr
@@ -1,59 +1,59 @@
-error[E0740]: unions cannot contain fields that may need dropping
+error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
   --> $DIR/field_checks.rs:24:5
    |
 LL |     a: String,
    |     ^^^^^^^^^
    |
-   = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
-help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
+   = note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`
+help: wrap the field type in `ManuallyDrop<...>`
    |
 LL |     a: std::mem::ManuallyDrop<String>,
    |        +++++++++++++++++++++++      +
 
-error[E0740]: unions cannot contain fields that may need dropping
+error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
   --> $DIR/field_checks.rs:28:5
    |
 LL |     a: std::cell::RefCell<i32>,
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
-help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
+   = note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`
+help: wrap the field type in `ManuallyDrop<...>`
    |
 LL |     a: std::mem::ManuallyDrop<std::cell::RefCell<i32>>,
    |        +++++++++++++++++++++++                       +
 
-error[E0740]: unions cannot contain fields that may need dropping
+error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
   --> $DIR/field_checks.rs:32:5
    |
 LL |     a: T,
    |     ^^^^
    |
-   = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
-help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
+   = note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`
+help: wrap the field type in `ManuallyDrop<...>`
    |
 LL |     a: std::mem::ManuallyDrop<T>,
    |        +++++++++++++++++++++++ +
 
-error[E0740]: unions cannot contain fields that may need dropping
+error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
   --> $DIR/field_checks.rs:44:5
    |
 LL |     nest: U5,
    |     ^^^^^^^^
    |
-   = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
-help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
+   = note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`
+help: wrap the field type in `ManuallyDrop<...>`
    |
 LL |     nest: std::mem::ManuallyDrop<U5>,
    |           +++++++++++++++++++++++  +
 
-error[E0740]: unions cannot contain fields that may need dropping
+error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
   --> $DIR/field_checks.rs:48:5
    |
 LL |     nest: [U5; 0],
    |     ^^^^^^^^^^^^^
    |
-   = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
-help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
+   = note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`
+help: wrap the field type in `ManuallyDrop<...>`
    |
 LL |     nest: std::mem::ManuallyDrop<[U5; 0]>,
    |           +++++++++++++++++++++++       +
diff --git a/tests/ui/union/issue-41073.rs b/tests/ui/union/issue-41073.rs
index 4dfdc606bb4..f7a82b4e7cf 100644
--- a/tests/ui/union/issue-41073.rs
+++ b/tests/ui/union/issue-41073.rs
@@ -1,5 +1,5 @@
 union Test {
-    a: A, //~ ERROR unions cannot contain fields that may need dropping
+    a: A, //~ ERROR field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
     b: B
 }
 
diff --git a/tests/ui/union/issue-41073.stderr b/tests/ui/union/issue-41073.stderr
index b3887fa0f90..ae1c4dfed9a 100644
--- a/tests/ui/union/issue-41073.stderr
+++ b/tests/ui/union/issue-41073.stderr
@@ -1,11 +1,11 @@
-error[E0740]: unions cannot contain fields that may need dropping
+error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
   --> $DIR/issue-41073.rs:2:5
    |
 LL |     a: A,
    |     ^^^^
    |
-   = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
-help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
+   = note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`
+help: wrap the field type in `ManuallyDrop<...>`
    |
 LL |     a: std::mem::ManuallyDrop<A>,
    |        +++++++++++++++++++++++ +
diff --git a/tests/ui/union/union-copy.rs b/tests/ui/union/union-copy.rs
index 5c3f8d90898..7ad0a11c6ac 100644
--- a/tests/ui/union/union-copy.rs
+++ b/tests/ui/union/union-copy.rs
@@ -9,6 +9,6 @@ union W {
 }
 
 impl Copy for U {} // OK
-impl Copy for W {} //~ ERROR the trait `Copy` may not be implemented for this type
+impl Copy for W {} //~ ERROR the trait `Copy` cannot be implemented for this type
 
 fn main() {}
diff --git a/tests/ui/union/union-copy.stderr b/tests/ui/union/union-copy.stderr
index 53ee4dd2e5b..ff6fa48db90 100644
--- a/tests/ui/union/union-copy.stderr
+++ b/tests/ui/union/union-copy.stderr
@@ -1,4 +1,4 @@
-error[E0204]: the trait `Copy` may not be implemented for this type
+error[E0204]: the trait `Copy` cannot be implemented for this type
   --> $DIR/union-copy.rs:12:15
    |
 LL |     a: std::mem::ManuallyDrop<String>
diff --git a/tests/ui/union/union-with-drop-fields.mirunsafeck.stderr b/tests/ui/union/union-with-drop-fields.mirunsafeck.stderr
index 93fe996d2a4..9861a21cb3d 100644
--- a/tests/ui/union/union-with-drop-fields.mirunsafeck.stderr
+++ b/tests/ui/union/union-with-drop-fields.mirunsafeck.stderr
@@ -1,35 +1,35 @@
-error[E0740]: unions cannot contain fields that may need dropping
+error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
   --> $DIR/union-with-drop-fields.rs:11:5
    |
 LL |     a: String,
    |     ^^^^^^^^^
    |
-   = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
-help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
+   = note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`
+help: wrap the field type in `ManuallyDrop<...>`
    |
 LL |     a: std::mem::ManuallyDrop<String>,
    |        +++++++++++++++++++++++      +
 
-error[E0740]: unions cannot contain fields that may need dropping
+error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
   --> $DIR/union-with-drop-fields.rs:19:5
    |
 LL |     a: S,
    |     ^^^^
    |
-   = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
-help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
+   = note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`
+help: wrap the field type in `ManuallyDrop<...>`
    |
 LL |     a: std::mem::ManuallyDrop<S>,
    |        +++++++++++++++++++++++ +
 
-error[E0740]: unions cannot contain fields that may need dropping
+error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
   --> $DIR/union-with-drop-fields.rs:24:5
    |
 LL |     a: T,
    |     ^^^^
    |
-   = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
-help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
+   = note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`
+help: wrap the field type in `ManuallyDrop<...>`
    |
 LL |     a: std::mem::ManuallyDrop<T>,
    |        +++++++++++++++++++++++ +
diff --git a/tests/ui/union/union-with-drop-fields.rs b/tests/ui/union/union-with-drop-fields.rs
index a7a8b69e784..9720830fb1f 100644
--- a/tests/ui/union/union-with-drop-fields.rs
+++ b/tests/ui/union/union-with-drop-fields.rs
@@ -8,7 +8,7 @@ union U {
 }
 
 union W {
-    a: String, //~ ERROR unions cannot contain fields that may need dropping
+    a: String, //~ ERROR field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
     b: String, // OK, only one field is reported
 }
 
@@ -16,12 +16,12 @@ struct S(String);
 
 // `S` doesn't implement `Drop` trait, but still has non-trivial destructor
 union Y {
-    a: S, //~ ERROR unions cannot contain fields that may need dropping
+    a: S, //~ ERROR field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
 }
 
 // We don't know if `T` is trivially-destructable or not until trans
 union J<T> {
-    a: T, //~ ERROR unions cannot contain fields that may need dropping
+    a: T, //~ ERROR field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
 }
 
 union H<T: Copy> {
diff --git a/tests/ui/union/union-with-drop-fields.thirunsafeck.stderr b/tests/ui/union/union-with-drop-fields.thirunsafeck.stderr
index 93fe996d2a4..9861a21cb3d 100644
--- a/tests/ui/union/union-with-drop-fields.thirunsafeck.stderr
+++ b/tests/ui/union/union-with-drop-fields.thirunsafeck.stderr
@@ -1,35 +1,35 @@
-error[E0740]: unions cannot contain fields that may need dropping
+error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
   --> $DIR/union-with-drop-fields.rs:11:5
    |
 LL |     a: String,
    |     ^^^^^^^^^
    |
-   = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
-help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
+   = note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`
+help: wrap the field type in `ManuallyDrop<...>`
    |
 LL |     a: std::mem::ManuallyDrop<String>,
    |        +++++++++++++++++++++++      +
 
-error[E0740]: unions cannot contain fields that may need dropping
+error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
   --> $DIR/union-with-drop-fields.rs:19:5
    |
 LL |     a: S,
    |     ^^^^
    |
-   = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
-help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
+   = note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`
+help: wrap the field type in `ManuallyDrop<...>`
    |
 LL |     a: std::mem::ManuallyDrop<S>,
    |        +++++++++++++++++++++++ +
 
-error[E0740]: unions cannot contain fields that may need dropping
+error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
   --> $DIR/union-with-drop-fields.rs:24:5
    |
 LL |     a: T,
    |     ^^^^
    |
-   = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
-help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
+   = note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`
+help: wrap the field type in `ManuallyDrop<...>`
    |
 LL |     a: std::mem::ManuallyDrop<T>,
    |        +++++++++++++++++++++++ +
diff --git a/tests/ui/union/unresolved-field-isnt-copy.rs b/tests/ui/union/unresolved-field-isnt-copy.rs
new file mode 100644
index 00000000000..7a6d79b2faa
--- /dev/null
+++ b/tests/ui/union/unresolved-field-isnt-copy.rs
@@ -0,0 +1,8 @@
+// Unresolved fields are not copy, but also shouldn't report an extra E0740.
+
+pub union Foo {
+    x: *const Missing,
+    //~^ ERROR cannot find type `Missing` in this scope
+}
+
+fn main() {}
diff --git a/tests/ui/union/unresolved-field-isnt-copy.stderr b/tests/ui/union/unresolved-field-isnt-copy.stderr
new file mode 100644
index 00000000000..22301582eef
--- /dev/null
+++ b/tests/ui/union/unresolved-field-isnt-copy.stderr
@@ -0,0 +1,9 @@
+error[E0412]: cannot find type `Missing` in this scope
+  --> $DIR/unresolved-field-isnt-copy.rs:4:15
+   |
+LL |     x: *const Missing,
+   |               ^^^^^^^ not found in this scope
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0412`.
diff --git a/tests/ui/unsafe/unsafe-fn-deref-ptr.mir.stderr b/tests/ui/unsafe/unsafe-fn-deref-ptr.mir.stderr
index a2614992445..7f1e7c8902f 100644
--- a/tests/ui/unsafe/unsafe-fn-deref-ptr.mir.stderr
+++ b/tests/ui/unsafe/unsafe-fn-deref-ptr.mir.stderr
@@ -1,11 +1,35 @@
 error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
-  --> $DIR/unsafe-fn-deref-ptr.rs:5:12
+  --> $DIR/unsafe-fn-deref-ptr.rs:5:13
+   |
+LL |     let _ = *p;
+   |             ^^ dereference of raw pointer
+   |
+   = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
+
+error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
+  --> $DIR/unsafe-fn-deref-ptr.rs:6:17
+   |
+LL |     let _: u8 = *p;
+   |                 ^^ dereference of raw pointer
+   |
+   = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
+
+error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
+  --> $DIR/unsafe-fn-deref-ptr.rs:7:9
+   |
+LL |     _ = *p;
+   |         ^^ dereference of raw pointer
+   |
+   = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
+
+error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
+  --> $DIR/unsafe-fn-deref-ptr.rs:8:12
    |
 LL |     return *p;
    |            ^^ dereference of raw pointer
    |
    = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
 
-error: aborting due to previous error
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0133`.
diff --git a/tests/ui/unsafe/unsafe-fn-deref-ptr.rs b/tests/ui/unsafe/unsafe-fn-deref-ptr.rs
index dc989535bd6..4b7c6bf6985 100644
--- a/tests/ui/unsafe/unsafe-fn-deref-ptr.rs
+++ b/tests/ui/unsafe/unsafe-fn-deref-ptr.rs
@@ -2,6 +2,9 @@
 // [thir]compile-flags: -Z thir-unsafeck
 
 fn f(p: *const u8) -> u8 {
+    let _ = *p; //~ ERROR dereference of raw pointer is unsafe
+    let _: u8 = *p; //~ ERROR dereference of raw pointer is unsafe
+    _ = *p; //~ ERROR dereference of raw pointer is unsafe
     return *p; //~ ERROR dereference of raw pointer is unsafe
 }
 
diff --git a/tests/ui/unsafe/unsafe-fn-deref-ptr.thir.stderr b/tests/ui/unsafe/unsafe-fn-deref-ptr.thir.stderr
index a2614992445..7f1e7c8902f 100644
--- a/tests/ui/unsafe/unsafe-fn-deref-ptr.thir.stderr
+++ b/tests/ui/unsafe/unsafe-fn-deref-ptr.thir.stderr
@@ -1,11 +1,35 @@
 error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
-  --> $DIR/unsafe-fn-deref-ptr.rs:5:12
+  --> $DIR/unsafe-fn-deref-ptr.rs:5:13
+   |
+LL |     let _ = *p;
+   |             ^^ dereference of raw pointer
+   |
+   = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
+
+error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
+  --> $DIR/unsafe-fn-deref-ptr.rs:6:17
+   |
+LL |     let _: u8 = *p;
+   |                 ^^ dereference of raw pointer
+   |
+   = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
+
+error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
+  --> $DIR/unsafe-fn-deref-ptr.rs:7:9
+   |
+LL |     _ = *p;
+   |         ^^ dereference of raw pointer
+   |
+   = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
+
+error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
+  --> $DIR/unsafe-fn-deref-ptr.rs:8:12
    |
 LL |     return *p;
    |            ^^ dereference of raw pointer
    |
    = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
 
-error: aborting due to previous error
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0133`.
diff --git a/tests/ui/wf/hir-wf-check-erase-regions.rs b/tests/ui/wf/hir-wf-check-erase-regions.rs
index 2b4b480df0a..3855f2c35c1 100644
--- a/tests/ui/wf/hir-wf-check-erase-regions.rs
+++ b/tests/ui/wf/hir-wf-check-erase-regions.rs
@@ -4,10 +4,10 @@
 pub struct Table<T, const N: usize>([Option<T>; N]);
 
 impl<'a, T, const N: usize> IntoIterator for &'a Table<T, N> {
-    type IntoIter = std::iter::Flatten<std::slice::Iter<'a, T>>; //~ ERROR `&T` is not an iterator
+    type IntoIter = std::iter::Flatten<std::slice::Iter<'a, T>>; //~ ERROR `&'a T` is not an iterator
     type Item = &'a T;
 
-    fn into_iter(self) -> Self::IntoIter { //~ ERROR `&T` is not an iterator
+    fn into_iter(self) -> Self::IntoIter { //~ ERROR `&'a T` is not an iterator
         unimplemented!()
     }
 }
diff --git a/tests/ui/wf/hir-wf-check-erase-regions.stderr b/tests/ui/wf/hir-wf-check-erase-regions.stderr
index 7bc19dd2e21..2843983c716 100644
--- a/tests/ui/wf/hir-wf-check-erase-regions.stderr
+++ b/tests/ui/wf/hir-wf-check-erase-regions.stderr
@@ -1,24 +1,24 @@
-error[E0277]: `&T` is not an iterator
+error[E0277]: `&'a T` is not an iterator
   --> $DIR/hir-wf-check-erase-regions.rs:7:21
    |
 LL |     type IntoIter = std::iter::Flatten<std::slice::Iter<'a, T>>;
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&T` is not an iterator
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&'a T` is not an iterator
    |
-   = help: the trait `Iterator` is not implemented for `&T`
+   = help: the trait `Iterator` is not implemented for `&'a T`
    = help: the trait `Iterator` is implemented for `&mut I`
-   = note: required for `&T` to implement `IntoIterator`
+   = note: required for `&'a T` to implement `IntoIterator`
 note: required by a bound in `Flatten`
   --> $SRC_DIR/core/src/iter/adapters/flatten.rs:LL:COL
 
-error[E0277]: `&T` is not an iterator
+error[E0277]: `&'a T` is not an iterator
   --> $DIR/hir-wf-check-erase-regions.rs:10:27
    |
 LL |     fn into_iter(self) -> Self::IntoIter {
-   |                           ^^^^^^^^^^^^^^ `&T` is not an iterator
+   |                           ^^^^^^^^^^^^^^ `&'a T` is not an iterator
    |
-   = help: the trait `Iterator` is not implemented for `&T`
+   = help: the trait `Iterator` is not implemented for `&'a T`
    = help: the trait `Iterator` is implemented for `&mut I`
-   = note: required for `&T` to implement `IntoIterator`
+   = note: required for `&'a T` to implement `IntoIterator`
 note: required by a bound in `Flatten`
   --> $SRC_DIR/core/src/iter/adapters/flatten.rs:LL:COL
 
diff --git a/triagebot.toml b/triagebot.toml
index 403a7087ee1..a81f66fe168 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -185,7 +185,7 @@ trigger_files = [
     "src/tools/x",
     "configure",
     "Cargo.toml",
-    "config.toml.example",
+    "config.example.toml",
     "src/stage0.json"
 ]
 
@@ -430,7 +430,7 @@ message = "The Miri subtree was changed"
 cc = ["@rust-lang/miri"]
 
 [mentions."src/tools/rust-analyzer"]
-cc = ["@rust-lang/wg-rls-2"]
+cc = ["@rust-lang/rust-analyzer"]
 
 [mentions."src/tools/rustfmt"]
 cc = ["@rust-lang/rustfmt"]
@@ -451,6 +451,10 @@ cc = ["@davidtwco", "@compiler-errors", "@JohnTitor", "@TaKO8Ki"]
 message = "`rustc_macros::diagnostics` was changed"
 cc = ["@davidtwco", "@compiler-errors", "@JohnTitor", "@TaKO8Ki"]
 
+[mentions."compiler/rustc_smir"]
+message = "This PR changes Stable MIR"
+cc = ["@oli-obk", "@celinval"]
+
 [mentions."compiler/rustc_target/src/spec"]
 message = """
 These commits modify **compiler targets**.
@@ -497,6 +501,7 @@ compiler-team-contributors = [
     "@TaKO8Ki",
     "@Nilstrieb",
     "@WaffleLapkin",
+    "@b-naber",
 ]
 compiler = [
     "compiler-team",